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

Last change on this file since 4813 was 4813, checked in by Sam Hocevar, 8 years ago

win32: add debug information to the function diversion code.

  • Property svn:keywords set to Id
File size: 16.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 intptr_t 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
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#endif
104        if(ret < 0)
105        {
106            perror("pipe");
107            return -1;
108        }
109    }
110
111    pid = run_process(child, opts, pipes);
112    if(pid < 0)
113    {
114        /* FIXME: close pipes */
115        fprintf(stderr, "error launching `%s'\n", child->newargv[0]);
116        return -1;
117    }
118
119    child->pid = pid;
120    for(i = 0; i < 3; i++)
121    {
122        close(pipes[i][1]);
123        child->fd[i] = pipes[i][0];
124    }
125
126    return 0;
127}
128
129#if !defined HAVE_SETENV
130static void setenv(char const *name, char const *value, int overwrite)
131{
132    char *str;
133
134    if(!overwrite && getenv(name))
135        return;
136
137    str = malloc(strlen(name) + 1 + strlen(value) + 1);
138    sprintf(str, "%s=%s", name, value);
139    putenv(str);
140}
141#endif
142
143static int run_process(struct child *child, struct opts *opts, int pipes[][2])
144{
145    char buf[64];
146#if defined HAVE_FORK
147    static int const files[] = { DEBUG_FILENO, STDERR_FILENO, STDOUT_FILENO };
148    char *libpath, *tmp;
149    int pid, j, len = strlen(opts->oldargv[0]);
150#   if defined __APPLE__
151#       define EXTRAINFO ""
152#       define PRELOAD "DYLD_INSERT_LIBRARIES"
153    /* Only enforce flat namespace in preload mode */
154    if (opts->opmode == OPMODE_PRELOAD)
155        setenv("DYLD_FORCE_FLAT_NAMESPACE", "1", 1);
156#   elif defined __osf__
157#       define EXTRAINFO ":DEFAULT"
158#       define PRELOAD "_RLD_LIST"
159#   elif defined __sun && defined __i386
160#       define EXTRAINFO ""
161#       define PRELOAD "LD_PRELOAD_32"
162#   else
163#       define EXTRAINFO ""
164#       define PRELOAD "LD_PRELOAD"
165#   endif
166#elif HAVE_WINDOWS_H
167    PROCESS_INFORMATION pinfo;
168    STARTUPINFO sinfo;
169    HANDLE pid;
170    char *cmdline;
171    int i, ret, len;
172#endif
173
174#if defined HAVE_FORK
175    /* Fork and launch child */
176    pid = fork();
177    if(pid < 0)
178        perror("fork");
179    if(pid != 0)
180        return pid;
181
182    /* We loop in reverse order so that files[0] is done last,
183     * just in case one of the other dup2()ed fds had the value */
184    for(j = 3; j--; )
185    {
186        close(pipes[j][0]);
187        if(pipes[j][1] != files[j])
188        {
189            dup2(pipes[j][1], files[j]);
190            close(pipes[j][1]);
191        }
192    }
193#endif
194
195#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
196    if(opts->maxmem >= 0)
197    {
198        struct rlimit rlim;
199        rlim.rlim_cur = opts->maxmem * 1048576;
200        rlim.rlim_max = opts->maxmem * 1048576;
201        setrlimit(ZZUF_RLIMIT_MEM, &rlim);
202    }
203#endif
204
205#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
206    if(opts->maxcpu >= 0)
207    {
208        struct rlimit rlim;
209        rlim.rlim_cur = opts->maxcpu;
210        rlim.rlim_max = opts->maxcpu + 5;
211        setrlimit(ZZUF_RLIMIT_CPU, &rlim);
212    }
213#endif
214
215    /* Set environment variables */
216#if defined _WIN32
217    sprintf(buf, "%i", _get_osfhandle(pipes[0][1]));
218#else
219    sprintf(buf, "%i", pipes[0][1]);
220#endif
221    setenv("ZZUF_DEBUGFD", buf, 1);
222    sprintf(buf, "%i", opts->seed);
223    setenv("ZZUF_SEED", buf, 1);
224    sprintf(buf, "%g", opts->minratio);
225    setenv("ZZUF_MINRATIO", buf, 1);
226    sprintf(buf, "%g", opts->maxratio);
227    setenv("ZZUF_MAXRATIO", buf, 1);
228
229#if defined HAVE_FORK
230    /* Make sure there is space for everything we might do. */
231    libpath = malloc(len + strlen(LIBDIR "/" LT_OBJDIR SONAME EXTRAINFO) + 1);
232    strcpy(libpath, opts->oldargv[0]);
233
234    /* If the binary name contains a '/', we look for a libzzuf in the
235     * same directory. Otherwise, we only look into the system directory
236     * to avoid shared library attacks. Write the result in libpath. */
237    tmp = strrchr(libpath, '/');
238    if(tmp)
239    {
240        strcpy(tmp + 1, LT_OBJDIR SONAME);
241        if(access(libpath, R_OK) < 0)
242            strcpy(libpath, LIBDIR "/" SONAME);
243    }
244    else
245        strcpy(libpath, LIBDIR "/" SONAME);
246
247    /* OSF1 only */
248    strcat(libpath, EXTRAINFO);
249
250    /* Do not clobber previous LD_PRELOAD values */
251    tmp = getenv(PRELOAD);
252    if(tmp && *tmp)
253    {
254        char *bigbuf = malloc(strlen(tmp) + strlen(libpath) + 2);
255        sprintf(bigbuf, "%s:%s", tmp, libpath);
256        free(libpath);
257        libpath = bigbuf;
258    }
259
260    /* Only preload the library in preload mode */
261    if (opts->opmode == OPMODE_PRELOAD)
262        setenv(PRELOAD, libpath, 1);
263    free(libpath);
264
265    if(execvp(child->newargv[0], child->newargv))
266    {
267        perror(child->newargv[0]);
268        exit(EXIT_FAILURE);
269    }
270
271    exit(EXIT_SUCCESS);
272    /* no return */
273    return 0;
274#elif HAVE_WINDOWS_H
275    pid = GetCurrentProcess();
276
277    /* Inherit standard handles */
278    memset(&sinfo, 0, sizeof(sinfo));
279    sinfo.cb = sizeof(sinfo);
280    sinfo.hStdInput = INVALID_HANDLE_VALUE;
281    sinfo.hStdOutput = (HANDLE)_get_osfhandle(pipes[2][1]);
282    sinfo.hStdError = (HANDLE)_get_osfhandle(pipes[1][1]);
283    sinfo.dwFlags = STARTF_USESTDHANDLES;
284
285    /* Build the commandline */
286    for (i = 0, len = 0; child->newargv[i]; i++)
287        len += strlen(child->newargv[i]) + 1;
288    cmdline = malloc(len);
289    for (i = 0, len = 0; child->newargv[i]; i++)
290    {
291        strcpy(cmdline + len, child->newargv[i]);
292        len += strlen(child->newargv[i]) + 1;
293        cmdline[len - 1] = ' ';
294    }
295    cmdline[len - 1] = '\0';
296
297    /* Create the process in suspended state */
298    ret = CreateProcess(child->newargv[0], cmdline, NULL, NULL, TRUE,
299                        CREATE_SUSPENDED, NULL, NULL, &sinfo, &pinfo);
300    free(cmdline);
301
302    if (!ret)
303    {
304        LPTSTR buf;
305        DWORD err = GetLastError();
306        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
307                      FORMAT_MESSAGE_FROM_SYSTEM |
308                      FORMAT_MESSAGE_IGNORE_INSERTS,
309                      NULL, err, 0, (LPTSTR)&buf, 0, NULL);
310        fprintf(stderr, "error launching `%s': %s\n", child->newargv[0], buf);
311        LocalFree(buf);
312        return -1;
313    }
314
315    /* Insert the replacement code */
316    ret = dll_inject(&pinfo, SONAME);
317    if(ret < 0)
318    {
319        TerminateProcess(pinfo.hProcess, -1);
320        return -1;
321    }
322
323    ret = ResumeThread(pinfo.hThread);
324    if(ret < 0)
325    {
326        TerminateProcess(pinfo.hProcess, -1);
327        return -1;
328    }
329
330    return (long int)pinfo.hProcess;
331#endif
332}
333
334#if defined HAVE_WINDOWS_H
335static void rep32(uint8_t *buf, void *addr)
336{
337    while(buf++)
338        if (memcmp(buf, "____", 4) == 0)
339        {
340            memcpy(buf, &addr, 4);
341            return;
342        }
343}
344
345static int dll_inject(PROCESS_INFORMATION *pinfo, char const *lib)
346{
347    static uint8_t const loader[] =
348        /* Load the injected DLL into memory */
349        "\xb8____"       /* mov %eax, <library_name_address> */
350        "\x50"           /* push %eax */
351        "\xb8____"       /* mov %eax, <LoadLibraryA> */
352        "\xff\xd0"       /* call %eax */
353        /* Restore the clobbered entry point code using our backup */
354        "\xb8\0\0\0\0"   /* mov %eax,0 */
355        "\x50"           /* push %eax */
356        "\xb8____"       /* mov %eax, <jumper_length> */
357        "\x50"           /* push %eax */
358        "\xb8____"       /* mov %eax, <backuped_entry_point_address> */
359        "\x50"           /* push %eax */
360        "\xb8____"       /* mov %eax, <original_entry_point_address> */
361        "\x50"           /* push %eax */
362        "\xb8____"       /* mov %eax, <GetCurrentProcess> */
363        "\xff\xd0"       /* call %eax */
364        "\x50"           /* push %eax */
365        "\xb8____"       /* mov %eax, <WriteProcessMemory> */
366        "\xff\xd0"       /* call %eax */
367        /* Jump to the original entry point */
368        "\xb8____"       /* mov %eax, <original_entry_point_address> */
369        "\xff\xe0";      /* jmp %eax */
370
371    static uint8_t const waiter[] =
372        "\xeb\xfe";      /* jmp <current> */
373
374    static uint8_t const jumper[] =
375        /* Jump to the injected loader */
376        "\xb8____"       /* mov eax, <loader_address> */
377        "\xff\xe0";      /* jmp eax */
378
379    CONTEXT ctx;
380    void *process = pinfo->hProcess;
381    void *thread = pinfo->hThread;
382    void *epaddr;
383    DWORD pid = pinfo->dwProcessId;
384
385    /* code:
386     * +---------------+--------------------+--------------+-------------+
387     * |     loader    | entry point backup | library name |   jumper    |
388     * |  len(loader)  |    len(jumper)     |   len(lib)   | len(jumper) |
389     * +---------------+--------------------+--------------+-------------+ */
390    uint8_t code[1024];
391
392    uint8_t *loaderaddr;
393    size_t liblen, loaderlen, waiterlen, jumperlen;
394    DWORD tmp;
395
396    liblen = strlen(lib) + 1;
397    loaderlen = sizeof(loader) - 1;
398    waiterlen = sizeof(waiter) - 1;
399    jumperlen = sizeof(jumper) - 1;
400    if (loaderlen + jumperlen + liblen > 1024)
401        return -1;
402
403    /* Allocate memory in the child for our injected code */
404    loaderaddr = VirtualAllocEx(process, NULL, loaderlen + jumperlen + liblen,
405                                MEM_COMMIT, PAGE_EXECUTE_READWRITE);
406    if(!loaderaddr)
407        return -1;
408
409    /* Create the first shellcode (jumper).
410     *
411     * The jumper's job is simply to jump at the second shellcode's location.
412     * It is written at the original entry point's location, which will in
413     * turn be restored by the second shellcode.
414     */
415    memcpy(code + loaderlen + jumperlen + liblen, jumper, jumperlen);
416    rep32(code + loaderlen + jumperlen + liblen, loaderaddr);
417
418    /* Create the second shellcode (loader, backuped entry point, and library
419     * name).
420     *
421     * The loader's job is to load the library by calling LoadLibraryA(),
422     * restore the original entry point using the backup copy, and jump
423     * back to the original entry point as if the process had just started.
424     *
425     * The second shellcode is written at a freshly allocated memory location.
426     */
427    memcpy(code, loader, loaderlen);
428    memcpy(code + loaderlen + jumperlen, lib, liblen);
429
430    /* Find the entry point address. It's simply in EAX. */
431    ctx.ContextFlags = CONTEXT_FULL;
432    GetThreadContext(thread, &ctx);
433    epaddr = (void *)(uintptr_t)ctx.Eax;
434
435    /* Backup the old entry point code */
436    ReadProcessMemory(process, epaddr, code + loaderlen, jumperlen, &tmp);
437    if(tmp != jumperlen)
438        return -1;
439
440    /* Replace the entry point code with a short jump to self, then resume
441     * the thread. This is necessary for CreateToolhelp32Snapshot() to
442     * work. */
443    WriteProcessMemory(process, epaddr, waiter, waiterlen, &tmp);
444    if(tmp != waiterlen)
445        return -1;
446    FlushInstructionCache(process, epaddr, waiterlen);
447    ResumeThread(thread);
448
449    /* Wait until the entry point is reached */
450    for (tmp = 0; tmp < 100; tmp++)
451    {
452        CONTEXT ctx;
453        ctx.ContextFlags = CONTEXT_FULL;
454        GetThreadContext(thread, &ctx);
455        if ((uintptr_t)ctx.Eip == (uintptr_t)epaddr)
456            break;
457        Sleep(10);
458    }
459    SuspendThread(thread);
460    if (tmp == 100)
461        return -1;
462
463    /* Remotely parse the target process's module list to get the addresses
464     * of the functions we need. This can only be done because we advanced
465     * the target's execution to the entry point. */
466    rep32(code, loaderaddr + loaderlen + jumperlen);
467    rep32(code, (void *)get_proc_address(process, pid, "LoadLibraryA"));
468    rep32(code, (void *)(uintptr_t)jumperlen);
469    rep32(code, loaderaddr + loaderlen);
470    rep32(code, epaddr);
471    rep32(code, (void *)get_proc_address(process, pid, "GetCurrentProcess"));
472    rep32(code, (void *)get_proc_address(process, pid, "WriteProcessMemory"));
473    rep32(code, epaddr);
474
475    /* Write our shellcodes into the target process */
476    WriteProcessMemory(process, epaddr, code + loaderlen + jumperlen + liblen,
477                       jumperlen, &tmp);
478    if(tmp != jumperlen)
479        return -1;
480    FlushInstructionCache(process, epaddr, waiterlen);
481
482    WriteProcessMemory(process, loaderaddr, code,
483                       loaderlen + jumperlen + liblen, &tmp);
484    if(tmp != loaderlen + jumperlen + liblen)
485        return -1;
486
487    return 0;
488}
489
490static intptr_t get_proc_address(void *process, DWORD pid, const char *func)
491{
492    char buf[1024];
493    size_t buflen = strlen(func) + 1;
494
495    MODULEENTRY32 entry;
496    intptr_t ret = 0;
497    DWORD tmp;
498    void *list;
499    int i, k;
500
501    list = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
502    entry.dwSize = sizeof(entry);
503    for(k = Module32First(list, &entry); k; k = Module32Next(list, &entry))
504    {
505        IMAGE_DOS_HEADER dos;
506        IMAGE_NT_HEADERS nt;
507        IMAGE_EXPORT_DIRECTORY expdir;
508
509        uint32_t exportaddr;
510        uint8_t const *base = entry.modBaseAddr;
511
512        if (strcmp("kernel32.dll", entry.szModule))
513            continue;
514
515        ReadProcessMemory(process, base, &dos, sizeof(dos), &tmp);
516        ReadProcessMemory(process, base + dos.e_lfanew, &nt, sizeof(nt), &tmp);
517
518        exportaddr = nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
519        if (!exportaddr)
520            continue;
521
522        ReadProcessMemory(process, base + exportaddr, &expdir, sizeof(expdir), &tmp);
523
524        for (i = 0; i < (int)expdir.NumberOfNames; i++)
525        {
526            uint32_t nameaddr, funcaddr;
527            uint16_t j;
528
529            /* Look for our function name in the list of names */
530            ReadProcessMemory(process, base + expdir.AddressOfNames
531                                            + i * sizeof(DWORD),
532                              &nameaddr, sizeof(nameaddr), &tmp);
533            ReadProcessMemory(process, base + nameaddr, buf, buflen, &tmp);
534
535            if (strcmp(buf, func))
536                continue;
537
538            /* If we found a function with this name, return its address */
539            ReadProcessMemory(process, base + expdir.AddressOfNameOrdinals
540                                            + i * sizeof(WORD),
541                                &j, sizeof(j), &tmp);
542            ReadProcessMemory(process, base + expdir.AddressOfFunctions
543                                            + j * sizeof(DWORD),
544                                &funcaddr, sizeof(funcaddr), &tmp);
545
546            ret = (intptr_t)base + funcaddr;
547            goto _finished;
548        }
549    }
550
551_finished:
552    CloseHandle(list);
553    return ret;
554}
555
556#endif
Note: See TracBrowser for help on using the repository browser.