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

Last change on this file since 4667 was 4667, checked in by Sam Hocevar, 10 years ago

Make it easier to dynamically allocate the debug filedescriptor later.

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