source: zzuf/trunk/src/myfork.c @ 4115

Last change on this file since 4115 was 4115, checked in by Sam Hocevar, 11 years ago

Move process creation stuff to a separate myfork.c file.

  • Property svn:keywords set to Id
File size: 11.5 KB
Line 
1/*
2 *  zzuf - general purpose fuzzer
3 *  Copyright (c) 2002, 2007-2009 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  $Id: myfork.c 4115 2009-12-12 22:20:11Z sam $
7 *
8 *  This program is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 *  myfork.c: launcher
17 */
18
19#include "config.h"
20
21#define _INCLUDE_POSIX_SOURCE /* for STDERR_FILENO on HP-UX */
22
23#if defined HAVE_STDINT_H
24#   include <stdint.h>
25#elif defined HAVE_INTTYPES_H
26#   include <inttypes.h>
27#endif
28#include <stdio.h>
29#include <stdlib.h>
30#if defined HAVE_UNISTD_H
31#   include <unistd.h>
32#endif
33#if defined HAVE_WINDOWS_H
34#   include <windows.h>
35#   include <imagehlp.h>
36#   include <tlhelp32.h>
37#endif
38#include <string.h>
39#include <fcntl.h> /* for O_BINARY */
40
41#include "common.h"
42#include "opts.h"
43#include "random.h"
44#include "fd.h"
45#include "fuzz.h"
46#include "myfork.h"
47#include "md5.h"
48#include "timer.h"
49
50static int run_process(struct opts *, int[][2]);
51
52#if defined HAVE_WINDOWS_H
53static int dll_inject(void *, void *);
54static intptr_t get_base_address(DWORD);
55static intptr_t get_entry_point_offset(char const *);
56#endif
57
58#if defined HAVE_WINDOWS_H
59static inline void addcpy(void *buf, void *x)
60{
61    memcpy(buf, &x, 4);
62}
63#endif
64
65int myfork(struct child *child, struct opts *opts)
66{
67    int pipes[3][2];
68    pid_t pid;
69    int i;
70
71    /* Prepare communication pipe */
72    for(i = 0; i < 3; i++)
73    {
74        int ret;
75#if defined HAVE_PIPE
76        ret = pipe(pipes[i]);
77#elif defined HAVE__PIPE
78        ret = _pipe(pipes[i], 512, _O_BINARY | O_NOINHERIT);
79#endif
80        if(ret < 0)
81        {
82            perror("pipe");
83            return -1;
84        }
85    }
86
87    pid = run_process(opts, pipes);
88    if(pid < 0)
89    {
90        /* FIXME: close pipes */
91        fprintf(stderr, "error launching `%s'\n", opts->newargv[0]);
92        return -1;
93    }
94
95    child->pid = pid;
96    for(i = 0; i < 3; i++)
97    {
98        close(pipes[i][1]);
99        child->fd[i] = pipes[i][0];
100    }
101
102    return 0;
103}
104
105#if !defined HAVE_SETENV
106static void setenv(char const *name, char const *value, int overwrite)
107{
108    char *str;
109
110    if(!overwrite && getenv(name))
111        return;
112
113    str = malloc(strlen(name) + 1 + strlen(value) + 1);
114    sprintf(str, "%s=%s", name, value);
115    putenv(str);
116}
117#endif
118
119static int run_process(struct opts *opts, int pipes[][2])
120{
121    char buf[64];
122#if defined HAVE_FORK
123    static int const files[] = { DEBUG_FILENO, STDERR_FILENO, STDOUT_FILENO };
124    char *libpath, *tmp;
125    int pid, j, len = strlen(opts->oldargv[0]);
126#   if defined __APPLE__
127#       define EXTRAINFO ""
128#       define PRELOAD "DYLD_INSERT_LIBRARIES"
129    setenv("DYLD_FORCE_FLAT_NAMESPACE", "1", 1);
130#   elif defined __osf__
131#       define EXTRAINFO ":DEFAULT"
132#       define PRELOAD "_RLD_LIST"
133#   else
134#       define EXTRAINFO ""
135#       define PRELOAD "LD_PRELOAD"
136#   endif
137#elif HAVE_WINDOWS_H
138    PROCESS_INFORMATION pinfo;
139    STARTUPINFO sinfo;
140    HANDLE pid;
141    void *epaddr;
142    int ret;
143#endif
144
145#if defined HAVE_FORK
146    /* Fork and launch child */
147    pid = fork();
148    if(pid < -1)
149        perror("fork");
150    if(pid != 0)
151        return pid;
152
153    /* We loop in reverse order so that files[0] is done last,
154     * just in case one of the other dup2()ed fds had the value */
155    for(j = 3; j--; )
156    {
157        close(pipes[j][0]);
158        if(pipes[j][1] != files[j])
159        {
160            dup2(pipes[j][1], files[j]);
161            close(pipes[j][1]);
162        }
163    }
164#endif
165
166#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
167    if(opts->maxmem >= 0)
168    {
169        struct rlimit rlim;
170        rlim.rlim_cur = opts->maxmem * 1000000;
171        rlim.rlim_max = opts->maxmem * 1000000;
172        setrlimit(ZZUF_RLIMIT_MEM, &rlim);
173    }
174#endif
175
176#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
177    if(opts->maxcpu >= 0)
178    {
179        struct rlimit rlim;
180        rlim.rlim_cur = opts->maxcpu;
181        rlim.rlim_max = opts->maxcpu + 5;
182        setrlimit(ZZUF_RLIMIT_CPU, &rlim);
183    }
184#endif
185
186    /* Set environment variables */
187    sprintf(buf, "%i", opts->seed);
188    setenv("ZZUF_SEED", buf, 1);
189    sprintf(buf, "%g", opts->minratio);
190    setenv("ZZUF_MINRATIO", buf, 1);
191    sprintf(buf, "%g", opts->maxratio);
192    setenv("ZZUF_MAXRATIO", buf, 1);
193
194#if defined HAVE_FORK
195    /* Make sure there is space for everything we might do. */
196    libpath = malloc(len + strlen(LIBDIR "/" LT_OBJDIR SONAME EXTRAINFO) + 1);
197    strcpy(libpath, opts->oldargv[0]);
198
199    /* If the binary name contains a '/', we look for a libzzuf in the
200     * same directory. Otherwise, we only look into the system directory
201     * to avoid shared library attacks. Write the result in libpath. */
202    tmp = strrchr(libpath, '/');
203    if(tmp)
204    {
205        strcpy(tmp + 1, LT_OBJDIR SONAME);
206        if(access(libpath, R_OK) < 0)
207            strcpy(libpath, LIBDIR "/" SONAME);
208    }
209    else
210        strcpy(libpath, LIBDIR "/" SONAME);
211
212    /* OSF1 only */
213    strcat(libpath, EXTRAINFO);
214
215    /* Do not clobber previous LD_PRELOAD values */
216    tmp = getenv(PRELOAD);
217    if(tmp && *tmp)
218    {
219        char *bigbuf = malloc(strlen(tmp) + strlen(libpath) + 2);
220        sprintf(bigbuf, "%s:%s", tmp, libpath);
221        free(libpath);
222        libpath = bigbuf;
223    }
224
225    setenv(PRELOAD, libpath, 1);
226    free(libpath);
227
228    if(execvp(opts->newargv[0], opts->newargv))
229    {
230        perror(opts->newargv[0]);
231        exit(EXIT_FAILURE);
232    }
233
234    exit(EXIT_SUCCESS);
235    /* no return */
236    return 0;
237#elif HAVE_WINDOWS_H
238    pid = GetCurrentProcess();
239
240    memset(&sinfo, 0, sizeof(sinfo));
241    sinfo.cb = sizeof(sinfo);
242    DuplicateHandle(pid, (HANDLE)_get_osfhandle(pipes[0][1]), pid,
243        /* FIXME */ &sinfo.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
244    DuplicateHandle(pid, (HANDLE)_get_osfhandle(pipes[1][1]), pid,
245                    &sinfo.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS);
246    DuplicateHandle(pid, (HANDLE)_get_osfhandle(pipes[2][1]), pid,
247                    &sinfo.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
248    sinfo.dwFlags = STARTF_USESTDHANDLES;
249    ret = CreateProcess(NULL, opts->newargv[0], NULL, NULL, FALSE,
250                        CREATE_SUSPENDED, NULL, NULL, &sinfo, &pinfo);
251    if(!ret)
252        return -1;
253
254    /* Get the child process's entry point address */
255    epaddr = (void *)(get_base_address(pinfo.dwProcessId)
256                       + get_entry_point_offset(opts->newargv[0]));
257    if(!epaddr)
258        return -1;
259
260    /* Insert the replacement code */
261    ret = dll_inject(pinfo.hProcess, epaddr);
262    if(ret < 0)
263    {
264        TerminateProcess(pinfo.hProcess, -1);
265        return -1;
266    }
267
268    ret = ResumeThread(pinfo.hThread);
269    if(ret < 0)
270    {
271        TerminateProcess(pinfo.hProcess, -1);
272        return -1;
273    }
274
275    return (long int)pinfo.hProcess;
276#endif
277}
278
279#if defined HAVE_WINDOWS_H
280static int dll_inject(void *process, void *epaddr)
281{
282    uint8_t code1[] =                  /* LIBZZUF: */
283                      "libzzuf.dll\0"
284                                       /* OLDEP: */
285                      "_______"
286                                       /* START: */
287                      "\xb8____"       /* mov eax,<libzzuf.dll> */
288                      "\x50"           /* push eax */
289                      "\xb8____"       /* mov eax,<LoadLibraryA> */
290                      "\xff\xd0"       /* call eax */
291                      "\xb8\0\0\0\0"   /* mov eax,0 */
292                      "\x50"           /* push eax */
293                      "\xb8\x07\0\0\0" /* mov eax,7 */
294                      "\x50"           /* push eax */
295                      "\xb8____"       /* mov eax,<OLDEP> */
296                      "\x50"           /* push eax */
297                      "\xb8____"       /* mov eax,<NEWEP> */
298                      "\x50"           /* push eax */
299                      "\xb8____"       /* mov eax,<GetCurrentProcess> */
300                      "\xff\xd0"       /* call eax */
301                      "\x50"           /* push eax */
302                      "\xb8____"       /* mov eax,<WriteProcessMemory> */
303                      "\xff\xd0"       /* call eax */
304                      "\xb8____"       /* mov eax,<NEWEP> */
305                      "\xff\xe0";      /* jmp eax */
306    uint8_t code2[] =                  /* NEWEP: */
307                      "\xb8____"       /* mov eax,<START> */
308                      "\xff\xe0";      /* jmp eax */
309    void *lib;
310    uint8_t *loaderaddr;
311    DWORD tmp;
312
313    /* Backup the old entry-point code */
314    ReadProcessMemory(process, epaddr, code1 + 0x0c, 7, &tmp);
315    if(tmp != 7)
316        return -1;
317
318    /* Copy the first shell code to a freshly allocated memory area. */
319    loaderaddr = VirtualAllocEx(process, NULL, sizeof(code1), MEM_COMMIT,
320                                PAGE_EXECUTE_READWRITE);
321    if(!loaderaddr)
322        return -1;
323
324    lib = LoadLibrary("kernel32.dll");
325    if(!lib)
326        return -1;
327
328    addcpy(code1 + 0x14, loaderaddr + 0x00); /* offset for dll string */
329    addcpy(code1 + 0x1a, GetProcAddress(lib, "LoadLibraryA"));
330    addcpy(code1 + 0x2d, loaderaddr + 0x0c);
331    addcpy(code1 + 0x33, epaddr);
332    addcpy(code1 + 0x39, GetProcAddress(lib, "GetCurrentProcess"));
333    addcpy(code1 + 0x41, GetProcAddress(lib, "WriteProcessMemory"));
334    addcpy(code1 + 0x48, epaddr);
335    FreeLibrary(lib);
336
337    WriteProcessMemory(process, loaderaddr, code1, sizeof(code1), &tmp);
338    if(tmp != sizeof(code1))
339        return -1;
340
341    /* Copy the second shell code where the old entry point was. */
342    addcpy(code2 + 0x01, loaderaddr + 12 + 7);
343    WriteProcessMemory(process, epaddr, code2, 7, &tmp);
344    if(tmp != 7)
345        return -1;
346
347    return 0;
348}
349
350/* Find the process's base address once it is loaded in memory (the header
351 * information is unreliable because of Vista's ASLR). */
352static intptr_t get_base_address(DWORD pid)
353{
354    MODULEENTRY32 entry;
355    intptr_t ret = 0;
356    void *list;
357    int k;
358
359    list = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
360    entry.dwSize = sizeof(entry);
361    for(k = Module32First(list, &entry); k; k = Module32Next(list, &entry))
362    {
363        /* FIXME: how do we select the correct module? */
364        ret = (intptr_t)entry.modBaseAddr;
365    }
366    CloseHandle(list);
367
368    return ret;
369}
370
371/* Find the process's entry point address offset. The information is in
372 * the file's PE header. */
373static intptr_t get_entry_point_offset(char const *name)
374{
375    PIMAGE_DOS_HEADER dos;
376    PIMAGE_NT_HEADERS nt;
377    intptr_t ret = 0;
378    void *file, *map, *base;
379
380    file = CreateFile(name, GENERIC_READ, FILE_SHARE_READ,
381                      NULL, OPEN_EXISTING, 0, NULL);
382    if(file == INVALID_HANDLE_VALUE)
383        return ret;
384
385    map = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
386    if(!map)
387    {
388        CloseHandle(file);
389        return ret;
390    }
391
392    base = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
393    if(!base)
394    {
395        CloseHandle(map);
396        CloseHandle(file);
397        return ret;
398    }
399
400    /* Sanity checks */
401    dos = (PIMAGE_DOS_HEADER)base;
402    nt = (PIMAGE_NT_HEADERS)((char *)base + dos->e_lfanew);
403    if(dos->e_magic == IMAGE_DOS_SIGNATURE /* 0x5A4D */
404      && nt->Signature == IMAGE_NT_SIGNATURE /* 0x00004550 */
405      && nt->FileHeader.Machine == IMAGE_FILE_MACHINE_I386
406      && nt->OptionalHeader.Magic == 0x10b /* IMAGE_NT_OPTIONAL_HDR32_MAGIC */)
407    {
408        ret = (intptr_t)nt->OptionalHeader.AddressOfEntryPoint;
409    }
410
411    UnmapViewOfFile(base);
412    CloseHandle(map);
413    CloseHandle(file);
414
415    return ret;
416}
417#endif
418
Note: See TracBrowser for help on using the repository browser.