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

Last change on this file since 4828 was 4828, checked in by wisk, 8 years ago

fix tmp file creation on win32, start to implement handling of win32 exception with GetExitCodeProcess?

  • Property svn:keywords set to Id
File size: 17.2 KB
Line 
1/*
2 *  zzuf - general purpose fuzzer
3 *  Copyright (c) 2002-2010 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  This program is free software. It comes without any warranty, to
7 *  the extent permitted by applicable law. You can redistribute it
8 *  and/or modify it under the terms of the Do What The Fuck You Want
9 *  To Public License, Version 2, as published by Sam Hocevar. See
10 *  http://sam.zoy.org/wtfpl/COPYING for more details.
11 */
12
13/*
14 *  myfork.c: launcher
15 */
16
17#include "config.h"
18
19#define _INCLUDE_POSIX_SOURCE /* for STDERR_FILENO on HP-UX */
20
21#if defined HAVE_STDINT_H
22#   include <stdint.h>
23#elif defined HAVE_INTTYPES_H
24#   include <inttypes.h>
25#endif
26#include <stdio.h>
27#include <stdlib.h>
28#if defined HAVE_UNISTD_H
29#   include <unistd.h>
30#endif
31#if defined HAVE_WINDOWS_H
32#   include <windows.h>
33#   include <imagehlp.h>
34#   include <tlhelp32.h>
35#endif
36#if defined HAVE_IO_H
37#   include <io.h>
38#endif
39#include <string.h>
40#include <fcntl.h> /* for O_BINARY */
41#if defined HAVE_SYS_RESOURCE_H
42#   include <sys/resource.h> /* for RLIMIT_AS */
43#endif
44
45#include "common.h"
46#include "opts.h"
47#include "random.h"
48#include "fd.h"
49#include "fuzz.h"
50#include "myfork.h"
51#include "md5.h"
52#include "timer.h"
53
54/* Handle old libtool versions */
55#if !defined LT_OBJDIR
56#   define LT_OBJDIR ".libs/"
57#endif
58
59#if defined RLIMIT_AS
60#   define ZZUF_RLIMIT_MEM RLIMIT_AS
61#elif defined RLIMIT_VMEM
62#   define ZZUF_RLIMIT_MEM RLIMIT_VMEM
63#elif defined RLIMIT_DATA
64#   define ZZUF_RLIMIT_MEM RLIMIT_DATA
65#else
66#   undef ZZUF_RLIMIT_MEM
67#endif
68
69#if defined RLIMIT_CPU
70#   define ZZUF_RLIMIT_CPU RLIMIT_CPU
71#else
72#   undef ZZUF_RLIMIT_CPU
73#endif
74
75static int run_process(struct child *child, struct opts *, int[][2]);
76
77#if defined HAVE_WINDOWS_H
78static void rep32(uint8_t *buf, void *addr);
79static int dll_inject(PROCESS_INFORMATION *, char const *);
80static void *get_proc_address(void *, DWORD, char const *);
81#endif
82
83int myfork(struct child *child, struct opts *opts)
84{
85    int pipes[3][2];
86    pid_t pid;
87    int i;
88
89    /* Prepare communication pipe */
90    for(i = 0; i < 3; i++)
91    {
92        int ret;
93#if defined HAVE_PIPE
94        ret = pipe(pipes[i]);
95#elif defined HAVE__PIPE && !defined _WIN32
96        int tmp;
97        /* The pipe is created with NOINHERIT otherwise both parts are
98         * inherited. We then duplicate the part we want. */
99        ret = _pipe(pipes[i], 512, _O_BINARY | O_NOINHERIT);
100        tmp = _dup(pipes[i][1]);
101        close(pipes[i][1]);
102        pipes[i][1] = tmp;
103#elif defined _WIN32
104        // http://www.daniweb.com/software-development/cpp/threads/295780/using-named-pipes-with-asynchronous-io-redirection-to-winapi
105        {
106            static int pipe_cnt = 0;
107            char pipe_name[BUFSIZ];
108            HANDLE pipe_hdl[2];         /* [0] read | [1] write */
109            HANDLE new_hdl;
110            SECURITY_ATTRIBUTES sa;
111            sa.nLength              = sizeof(sa);
112            sa.bInheritHandle       = TRUE;
113            sa.lpSecurityDescriptor = NULL;
114
115            ret = 0;
116
117            /* Since we have to use a named pipe, we have to make sure the name is unique */
118            _snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\Pipe\\zzuf.%08x.%d", GetCurrentProcessId(), pipe_cnt++);
119
120            /* At this time, the HANDLE is inheritable and can both read/write */
121            if ((pipe_hdl[0] = CreateNamedPipeA(
122                pipe_name,
123                PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
124                PIPE_TYPE_BYTE | PIPE_WAIT,
125                1,
126                BUFSIZ,
127                BUFSIZ,
128                0,
129                &sa)) == INVALID_HANDLE_VALUE ||
130
131            /* Create a new handle for writing access only and it must be inheritable */
132            (pipe_hdl[1] = CreateFileA(
133                pipe_name,
134                GENERIC_WRITE,
135                0,
136                &sa,
137                OPEN_EXISTING,
138                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
139                NULL)) == INVALID_HANDLE_VALUE)
140                ret = -1;
141
142            /* Now we create a new handle for the listener which is not inheritable */
143            if (!DuplicateHandle(
144                GetCurrentProcess(), pipe_hdl[0],
145                GetCurrentProcess(), &new_hdl,
146                0, FALSE,
147                DUPLICATE_SAME_ACCESS))
148                ret = -1;
149
150            /* Finally we can safetly close the pipe handle */
151            CloseHandle(pipe_hdl[0]);
152
153            /* Now we convert handle to fd */
154            pipes[i][0] = _open_osfhandle((intptr_t)new_hdl,     0x0);
155            pipes[i][1] = _open_osfhandle((intptr_t)pipe_hdl[1], 0x0);
156        }
157#endif
158        if(ret < 0)
159        {
160            perror("pipe");
161            return -1;
162        }
163    }
164
165    pid = run_process(child, opts, pipes);
166    if(pid < 0)
167    {
168        /* FIXME: close pipes */
169        fprintf(stderr, "error launching `%s'\n", child->newargv[0]);
170        return -1;
171    }
172
173    child->pid = pid;
174    for(i = 0; i < 3; i++)
175    {
176        close(pipes[i][1]);
177        child->fd[i] = pipes[i][0];
178    }
179
180    return 0;
181}
182
183#if !defined HAVE_SETENV
184static void setenv(char const *name, char const *value, int overwrite)
185{
186    char *str;
187
188    if(!overwrite && getenv(name))
189        return;
190
191    str = malloc(strlen(name) + 1 + strlen(value) + 1);
192    sprintf(str, "%s=%s", name, value);
193    putenv(str);
194}
195#endif
196
197static int run_process(struct child *child, struct opts *opts, int pipes[][2])
198{
199    char buf[64];
200#if defined HAVE_FORK
201    static int const files[] = { DEBUG_FILENO, STDERR_FILENO, STDOUT_FILENO };
202    char *libpath, *tmp;
203    int pid, j, len = strlen(opts->oldargv[0]);
204#   if defined __APPLE__
205#       define EXTRAINFO ""
206#       define PRELOAD "DYLD_INSERT_LIBRARIES"
207    /* Only enforce flat namespace in preload mode */
208    if (opts->opmode == OPMODE_PRELOAD)
209        setenv("DYLD_FORCE_FLAT_NAMESPACE", "1", 1);
210#   elif defined __osf__
211#       define EXTRAINFO ":DEFAULT"
212#       define PRELOAD "_RLD_LIST"
213#   elif defined __sun && defined __i386
214#       define EXTRAINFO ""
215#       define PRELOAD "LD_PRELOAD_32"
216#   else
217#       define EXTRAINFO ""
218#       define PRELOAD "LD_PRELOAD"
219#   endif
220#elif HAVE_WINDOWS_H
221    PROCESS_INFORMATION pinfo;
222    STARTUPINFO sinfo;
223    HANDLE pid;
224    char *cmdline;
225    int i, ret, len;
226#endif
227
228#if defined HAVE_FORK
229    /* Fork and launch child */
230    pid = fork();
231    if(pid < 0)
232        perror("fork");
233    if(pid != 0)
234        return pid;
235
236    /* We loop in reverse order so that files[0] is done last,
237     * just in case one of the other dup2()ed fds had the value */
238    for(j = 3; j--; )
239    {
240        close(pipes[j][0]);
241        if(pipes[j][1] != files[j])
242        {
243            dup2(pipes[j][1], files[j]);
244            close(pipes[j][1]);
245        }
246    }
247#endif
248
249#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
250    if(opts->maxmem >= 0)
251    {
252        struct rlimit rlim;
253        rlim.rlim_cur = opts->maxmem * 1048576;
254        rlim.rlim_max = opts->maxmem * 1048576;
255        setrlimit(ZZUF_RLIMIT_MEM, &rlim);
256    }
257#endif
258
259#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
260    if(opts->maxcpu >= 0)
261    {
262        struct rlimit rlim;
263        rlim.rlim_cur = opts->maxcpu;
264        rlim.rlim_max = opts->maxcpu + 5;
265        setrlimit(ZZUF_RLIMIT_CPU, &rlim);
266    }
267#endif
268
269    /* Set environment variables */
270#if defined _WIN32
271    sprintf(buf, "%i", _get_osfhandle(pipes[0][1]));
272#else
273    sprintf(buf, "%i", pipes[0][1]);
274#endif
275    setenv("ZZUF_DEBUGFD", buf, 1);
276    sprintf(buf, "%i", opts->seed);
277    setenv("ZZUF_SEED", buf, 1);
278    sprintf(buf, "%g", opts->minratio);
279    setenv("ZZUF_MINRATIO", buf, 1);
280    sprintf(buf, "%g", opts->maxratio);
281    setenv("ZZUF_MAXRATIO", buf, 1);
282
283#if defined HAVE_FORK
284    /* Make sure there is space for everything we might do. */
285    libpath = malloc(len + strlen(LIBDIR "/" LT_OBJDIR SONAME EXTRAINFO) + 1);
286    strcpy(libpath, opts->oldargv[0]);
287
288    /* If the binary name contains a '/', we look for a libzzuf in the
289     * same directory. Otherwise, we only look into the system directory
290     * to avoid shared library attacks. Write the result in libpath. */
291    tmp = strrchr(libpath, '/');
292    if(tmp)
293    {
294        strcpy(tmp + 1, LT_OBJDIR SONAME);
295        if(access(libpath, R_OK) < 0)
296            strcpy(libpath, LIBDIR "/" SONAME);
297    }
298    else
299        strcpy(libpath, LIBDIR "/" SONAME);
300
301    /* OSF1 only */
302    strcat(libpath, EXTRAINFO);
303
304    /* Do not clobber previous LD_PRELOAD values */
305    tmp = getenv(PRELOAD);
306    if(tmp && *tmp)
307    {
308        char *bigbuf = malloc(strlen(tmp) + strlen(libpath) + 2);
309        sprintf(bigbuf, "%s:%s", tmp, libpath);
310        free(libpath);
311        libpath = bigbuf;
312    }
313
314    /* Only preload the library in preload mode */
315    if (opts->opmode == OPMODE_PRELOAD)
316        setenv(PRELOAD, libpath, 1);
317    free(libpath);
318
319    if(execvp(child->newargv[0], child->newargv))
320    {
321        perror(child->newargv[0]);
322        exit(EXIT_FAILURE);
323    }
324
325    exit(EXIT_SUCCESS);
326    /* no return */
327    return 0;
328#elif HAVE_WINDOWS_H
329    pid = GetCurrentProcess();
330
331    /* Inherit standard handles */
332    memset(&sinfo, 0, sizeof(sinfo));
333    sinfo.cb = sizeof(sinfo);
334    sinfo.hStdInput = INVALID_HANDLE_VALUE;
335    sinfo.hStdOutput = (HANDLE)_get_osfhandle(pipes[2][1]);
336    sinfo.hStdError = (HANDLE)_get_osfhandle(pipes[1][1]);
337    sinfo.dwFlags = STARTF_USESTDHANDLES;
338
339    /* Build the commandline */
340    for (i = 0, len = 0; child->newargv[i]; i++)
341        len += strlen(child->newargv[i]) + 1;
342    cmdline = malloc(len);
343    for (i = 0, len = 0; child->newargv[i]; i++)
344    {
345        strcpy(cmdline + len, child->newargv[i]);
346        len += strlen(child->newargv[i]) + 1;
347        cmdline[len - 1] = ' ';
348    }
349    cmdline[len - 1] = '\0';
350
351    /* Create the process in suspended state */
352    ret = CreateProcess(child->newargv[0], cmdline, NULL, NULL, TRUE,
353                        CREATE_SUSPENDED, NULL, NULL, &sinfo, &pinfo);
354    free(cmdline);
355
356    child->process_handle = pinfo.hProcess;
357    child->pid            = pinfo.dwProcessId;
358
359    if (!ret)
360    {
361        LPTSTR buf;
362        DWORD err = GetLastError();
363        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
364                      FORMAT_MESSAGE_FROM_SYSTEM     |
365                      FORMAT_MESSAGE_IGNORE_INSERTS,
366                      NULL, err, 0, (LPTSTR)&buf, 0, NULL);
367        fprintf(stderr, "error launching `%s': %s\n", child->newargv[0], buf);
368        LocalFree(buf);
369        return -1;
370    }
371
372    /* Insert the replacement code */
373    ret = dll_inject(&pinfo, SONAME);
374    if(ret < 0)
375    {
376        TerminateProcess(pinfo.hProcess, -1);
377        return -1;
378    }
379
380    ret = ResumeThread(pinfo.hThread);
381    if(ret < 0)
382    {
383        TerminateProcess(pinfo.hProcess, -1);
384        return -1;
385    }
386
387    return (long int)pinfo.hProcess;
388#endif
389}
390
391#if defined HAVE_WINDOWS_H
392
393static int dll_inject(PROCESS_INFORMATION *pinfo, char const *lib)
394{
395    int res = -1;
396
397    /* This payload allows us to load arbitrary module located at the end of this buffer */
398    static uint8_t const ldr[] =
399    {
400        "\x60"                  /* pushad               */
401        "\xEB\x0E"              /* jmp short 0x11       */
402        "\xB8____"              /* mov eax,LoadLibraryA */
403        "\xFF\xD0"              /* call eax             */
404        "\x85\xC0"              /* test eax,eax         */
405        "\x75\x01"              /* jnz 0xf              */
406        "\xCC"                  /* int3                 */
407        "\x61"                  /* popad                */
408        "\xC3"                  /* ret                  */
409        "\xE8\xED\xFF\xFF\xFF"  /* call dword 0x3       */
410    };
411
412    /* We use this code to make the targeted process waits for us */
413    static uint8_t const wait[] = "\xeb\xfe"; /* jmp $-1 */
414    size_t wait_len             = sizeof(wait) - 1;
415    uint8_t orig_data[2];
416
417    void *process   = pinfo->hProcess;
418    void *thread    = pinfo->hThread;
419    DWORD pid       = pinfo->dwProcessId;
420    void *rldlib    = NULL;
421    DWORD written   = 0;
422    DWORD old_prot  = 0;
423
424    /* Payload */
425    void *rpl       = NULL;
426    uint8_t *pl     = NULL;
427    size_t pl_len   = sizeof(ldr) - 1 + strlen(lib) + 1;
428
429    CONTEXT ctxt;
430    DWORD oep; /* Original Entry Point */
431
432    /* Use the main thread to inject our library */
433    ctxt.ContextFlags = CONTEXT_FULL;
434    if (!GetThreadContext(thread, &ctxt)) goto _return;
435
436    /* Make the target program waits when it reachs the original entry point, because we can't do many thing from the windows loader */
437    oep = ctxt.Eax;
438    if (!ReadProcessMemory(process, (LPVOID)oep, orig_data, sizeof(orig_data), &written) || written != sizeof(orig_data)) goto _return; /* save original opcode */
439    if (!WriteProcessMemory(process, (LPVOID)oep, wait, wait_len , &written) || written != wait_len) goto _return;                      /* write jmp short $-1 */
440    if (!FlushInstructionCache(process, (LPVOID)oep, wait_len)) goto _return;
441    if (ResumeThread(thread) == (DWORD)-1) goto _return;
442
443    /* Stop when the program reachs the oep */
444    while (oep != ctxt.Eip)
445    {
446        if (!GetThreadContext(thread, &ctxt)) goto _return;
447        Sleep(10);
448    }
449
450    if (SuspendThread(thread) == (DWORD)-1) goto _return;
451
452    /* Resolve LoadLibraryA from the target process memory context */
453    rldlib = get_proc_address(process, pid, "LoadLibraryA");
454
455    if ((rpl = VirtualAllocEx(process, NULL, pl_len, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == NULL) goto _return;
456
457    /* Emulate a call to the ldr code, thus the ret instruction from ldr will get eip back to the original entry point */
458    ctxt.Esp -= 4;
459    if (!WriteProcessMemory(process, (LPVOID)ctxt.Esp, &oep, sizeof(oep), &written) || written != sizeof(oep)) goto _return;
460    ctxt.Eip = (DWORD)rpl;
461    if (!SetThreadContext(thread, &ctxt)) goto _return;
462
463    /* Forge the payload */
464    if ((pl = (uint8_t *)malloc(pl_len)) == NULL) goto _return;
465    memcpy(pl, ldr, sizeof(ldr) - 1);
466    memcpy(pl + 4, &rldlib, sizeof(rldlib));        /* Write the address of LoadLibraryA         */
467    strcpy((char *)(pl + sizeof(ldr) - 1), lib);    /* Write the first parameter of LoadLibraryA */
468
469    if (!WriteProcessMemory(process, rpl, pl, pl_len, &written) || written != pl_len) goto _return;
470
471    /* Restore original opcode */
472    if (!WriteProcessMemory(process, (LPVOID)oep, orig_data, sizeof(orig_data), &written) || written != sizeof(orig_data)) goto _return;
473
474    if (!FlushInstructionCache(process, rpl, pl_len)) goto _return;
475    if (!FlushInstructionCache(process, (LPVOID)oep, sizeof(orig_data))) goto _return;
476
477    res = 0;
478_return:
479    if (pl != NULL) free(pl);
480
481    /* We must not free remote allocated memory since they will be used after the process will be resumed */
482    return res;
483}
484
485static void *get_proc_address(void *process, DWORD pid, const char *func)
486{
487    char buf[1024];
488    size_t buflen = strlen(func) + 1;
489
490    MODULEENTRY32 entry;
491    void *ret = 0;
492    DWORD tmp;
493    void *list;
494    int i, k;
495
496    list = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
497    entry.dwSize = sizeof(entry);
498    for(k = Module32First(list, &entry); k; k = Module32Next(list, &entry))
499    {
500        IMAGE_DOS_HEADER dos;
501        IMAGE_NT_HEADERS nt;
502        IMAGE_EXPORT_DIRECTORY expdir;
503
504        uint32_t exportaddr;
505        uint8_t *base = entry.modBaseAddr;
506
507        if (strcmp("kernel32.dll", entry.szModule))
508            continue;
509
510        ReadProcessMemory(process, base, &dos, sizeof(dos), &tmp);
511        ReadProcessMemory(process, base + dos.e_lfanew, &nt, sizeof(nt), &tmp);
512
513        exportaddr = nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
514        if (!exportaddr)
515            continue;
516
517        ReadProcessMemory(process, base + exportaddr, &expdir, sizeof(expdir), &tmp);
518
519        for (i = 0; i < (int)expdir.NumberOfNames; i++)
520        {
521            uint32_t nameaddr, funcaddr;
522            uint16_t j;
523
524            /* Look for our function name in the list of names */
525            ReadProcessMemory(process, base + expdir.AddressOfNames
526                                            + i * sizeof(DWORD),
527                              &nameaddr, sizeof(nameaddr), &tmp);
528            ReadProcessMemory(process, base + nameaddr, buf, buflen, &tmp);
529
530            if (strcmp(buf, func))
531                continue;
532
533            /* If we found a function with this name, return its address */
534            ReadProcessMemory(process, base + expdir.AddressOfNameOrdinals
535                                            + i * sizeof(WORD),
536                              &j, sizeof(j), &tmp);
537            ReadProcessMemory(process, base + expdir.AddressOfFunctions
538                                            + j * sizeof(DWORD),
539                              &funcaddr, sizeof(funcaddr), &tmp);
540
541            ret = base + funcaddr;
542            goto _finished;
543        }
544    }
545
546_finished:
547    CloseHandle(list);
548    return ret;
549}
550
551#endif
Note: See TracBrowser for help on using the repository browser.