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

Last change on this file since 4827 was 4827, checked in by wisk, 9 years ago

on win32, use a named pipe and IOCP to read stdout, stderr and debugfd correctly.

  • Property svn:keywords set to Id
File size: 16.6 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    if (!ret)
357    {
358        LPTSTR buf;
359        DWORD err = GetLastError();
360        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
361                      FORMAT_MESSAGE_FROM_SYSTEM     |
362                      FORMAT_MESSAGE_IGNORE_INSERTS,
363                      NULL, err, 0, (LPTSTR)&buf, 0, NULL);
364        fprintf(stderr, "error launching `%s': %s\n", child->newargv[0], buf);
365        LocalFree(buf);
366        return -1;
367    }
368
369    /* Insert the replacement code */
370    ret = dll_inject(&pinfo, SONAME);
371    if(ret < 0)
372    {
373        TerminateProcess(pinfo.hProcess, -1);
374        return -1;
375    }
376
377    ret = ResumeThread(pinfo.hThread);
378    if(ret < 0)
379    {
380        TerminateProcess(pinfo.hProcess, -1);
381        return -1;
382    }
383
384    return (long int)pinfo.hProcess;
385#endif
386}
387
388#if defined HAVE_WINDOWS_H
389
390static int dll_inject(PROCESS_INFORMATION *pinfo, char const *lib)
391{
392    int res = -1;
393
394    /* This payload allows us to load arbitrary module located at the end of this buffer */
395    static uint8_t const ldr[] =
396    {
397        "\x60"                  /* pushad               */
398        "\xEB\x0E"              /* jmp short 0x11       */
399        "\xB8____"              /* mov eax,LoadLibraryA */
400        "\xFF\xD0"              /* call eax             */
401        "\x85\xC0"              /* test eax,eax         */
402        "\x75\x01"              /* jnz 0xf              */
403        "\xCC"                  /* int3                 */
404        "\x61"                  /* popad                */
405        "\xC3"                  /* ret                  */
406        "\xE8\xED\xFF\xFF\xFF"  /* call dword 0x3       */
407    };
408
409    /* We use this code to make the targeted process waits for us */
410    static uint8_t const wait[] = "\xeb\xfe"; /* jmp $-1 */
411    size_t wait_len             = sizeof(wait) - 1;
412    uint8_t orig_data[2];
413
414    void *process   = pinfo->hProcess;
415    void *thread    = pinfo->hThread;
416    DWORD pid       = pinfo->dwProcessId;
417    void *rldlib    = NULL;
418    DWORD written   = 0;
419    DWORD old_prot  = 0;
420
421    /* Payload */
422    void *rpl       = NULL;
423    uint8_t *pl     = NULL;
424    size_t pl_len   = sizeof(ldr) - 1 + strlen(lib) + 1;
425
426    CONTEXT ctxt;
427    DWORD oep; /* Original Entry Point */
428
429    /* Use the main thread to inject our library */
430    ctxt.ContextFlags = CONTEXT_FULL;
431    if (!GetThreadContext(thread, &ctxt)) goto _return;
432
433    /* Make the target program waits when it reachs the original entry point, because we can't do many thing from the windows loader */
434    oep = ctxt.Eax;
435    if (!ReadProcessMemory(process, (LPVOID)oep, orig_data, sizeof(orig_data), &written) || written != sizeof(orig_data)) goto _return; /* save original opcode */
436    if (!WriteProcessMemory(process, (LPVOID)oep, wait, wait_len , &written) || written != wait_len) goto _return;                      /* write jmp short $-1 */
437    if (!FlushInstructionCache(process, (LPVOID)oep, wait_len)) goto _return;
438    if (ResumeThread(thread) == (DWORD)-1) goto _return;
439
440    /* Stop when the program reachs the oep */
441    while (oep != ctxt.Eip)
442    {
443        if (!GetThreadContext(thread, &ctxt)) goto _return;
444        Sleep(10);
445    }
446
447    if (SuspendThread(thread) == (DWORD)-1) goto _return;
448
449    /* Resolve LoadLibraryA from the target process memory context */
450    rldlib = get_proc_address(process, pid, "LoadLibraryA");
451
452    if ((rpl = VirtualAllocEx(process, NULL, pl_len, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == NULL) goto _return;
453
454    /* Emulate a call to the ldr code, thus the ret instruction from ldr will get eip back to the original entry point */
455    ctxt.Esp -= 4;
456    if (!WriteProcessMemory(process, (LPVOID)ctxt.Esp, &oep, sizeof(oep), &written) || written != sizeof(oep)) goto _return;
457    ctxt.Eip = (DWORD)rpl;
458    if (!SetThreadContext(thread, &ctxt)) goto _return;
459
460    /* Forge the payload */
461    if ((pl = (uint8_t *)malloc(pl_len)) == NULL) goto _return;
462    memcpy(pl, ldr, sizeof(ldr) - 1);
463    memcpy(pl + 4, &rldlib, sizeof(rldlib));        /* Write the address of LoadLibraryA         */
464    strcpy((char *)(pl + sizeof(ldr) - 1), lib);    /* Write the first parameter of LoadLibraryA */
465
466    if (!WriteProcessMemory(process, rpl, pl, pl_len, &written) || written != pl_len) goto _return;
467
468    /* Restore original opcode */
469    if (!WriteProcessMemory(process, (LPVOID)oep, orig_data, sizeof(orig_data), &written) || written != sizeof(orig_data)) goto _return;
470
471    if (!FlushInstructionCache(process, rpl, pl_len)) goto _return;
472    if (!FlushInstructionCache(process, (LPVOID)oep, sizeof(orig_data))) goto _return;
473
474    res = 0;
475_return:
476    if (pl != NULL) free(pl);
477
478    /* We must not free remote allocated memory since they will be used after the process will be resumed */
479    return res;
480}
481
482static void *get_proc_address(void *process, DWORD pid, const char *func)
483{
484    char buf[1024];
485    size_t buflen = strlen(func) + 1;
486
487    MODULEENTRY32 entry;
488    void *ret = 0;
489    DWORD tmp;
490    void *list;
491    int i, k;
492
493    list = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
494    entry.dwSize = sizeof(entry);
495    for(k = Module32First(list, &entry); k; k = Module32Next(list, &entry))
496    {
497        IMAGE_DOS_HEADER dos;
498        IMAGE_NT_HEADERS nt;
499        IMAGE_EXPORT_DIRECTORY expdir;
500
501        uint32_t exportaddr;
502        uint8_t *base = entry.modBaseAddr;
503
504        if (strcmp("kernel32.dll", entry.szModule))
505            continue;
506
507        ReadProcessMemory(process, base, &dos, sizeof(dos), &tmp);
508        ReadProcessMemory(process, base + dos.e_lfanew, &nt, sizeof(nt), &tmp);
509
510        exportaddr = nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
511        if (!exportaddr)
512            continue;
513
514        ReadProcessMemory(process, base + exportaddr, &expdir, sizeof(expdir), &tmp);
515
516        for (i = 0; i < (int)expdir.NumberOfNames; i++)
517        {
518            uint32_t nameaddr, funcaddr;
519            uint16_t j;
520
521            /* Look for our function name in the list of names */
522            ReadProcessMemory(process, base + expdir.AddressOfNames
523                                            + i * sizeof(DWORD),
524                              &nameaddr, sizeof(nameaddr), &tmp);
525            ReadProcessMemory(process, base + nameaddr, buf, buflen, &tmp);
526
527            if (strcmp(buf, func))
528                continue;
529
530            /* If we found a function with this name, return its address */
531            ReadProcessMemory(process, base + expdir.AddressOfNameOrdinals
532                                            + i * sizeof(WORD),
533                              &j, sizeof(j), &tmp);
534            ReadProcessMemory(process, base + expdir.AddressOfFunctions
535                                            + j * sizeof(DWORD),
536                              &funcaddr, sizeof(funcaddr), &tmp);
537
538            ret = base + funcaddr;
539            goto _finished;
540        }
541    }
542
543_finished:
544    CloseHandle(list);
545    return ret;
546}
547
548#endif
Note: See TracBrowser for help on using the repository browser.