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

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

Proper child command line construction on Win32. There is no need
to hardcode stuff for debugging purposes any longer.

  • 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", 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 = (HANDLE)_get_osfhandle(pipes[0][CHILD_FD(0)]);
281    sinfo.hStdOutput = (HANDLE)_get_osfhandle(pipes[1][CHILD_FD(1)]);
282    sinfo.hStdError = (HANDLE)_get_osfhandle(pipes[2][CHILD_FD(2)]);
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        return -1;
304
305    /* Insert the replacement code */
306    ret = dll_inject(&pinfo, SONAME);
307    if(ret < 0)
308    {
309        TerminateProcess(pinfo.hProcess, -1);
310        return -1;
311    }
312
313    ret = ResumeThread(pinfo.hThread);
314    if(ret < 0)
315    {
316        TerminateProcess(pinfo.hProcess, -1);
317        return -1;
318    }
319
320    return (long int)pinfo.hProcess;
321#endif
322}
323
324#if defined HAVE_WINDOWS_H
325static void rep32(uint8_t *buf, void *addr)
326{
327    while(buf++)
328        if (memcmp(buf, "____", 4) == 0)
329        {
330            memcpy(buf, &addr, 4);
331            return;
332        }
333}
334
335static int dll_inject(PROCESS_INFORMATION *pinfo, char const *lib)
336{
337    static uint8_t const loader[] =
338        /* Load the injected DLL into memory */
339        "\xb8____"       /* mov %eax, <library_name_address> */
340        "\x50"           /* push %eax */
341        "\xb8____"       /* mov %eax, <LoadLibraryA> */
342        "\xff\xd0"       /* call %eax */
343        /* Restore the clobbered entry point code using our backup */
344        "\xb8\0\0\0\0"   /* mov %eax,0 */
345        "\x50"           /* push %eax */
346        "\xb8____"       /* mov %eax, <jumper_length> */
347        "\x50"           /* push %eax */
348        "\xb8____"       /* mov %eax, <backuped_entry_point_address> */
349        "\x50"           /* push %eax */
350        "\xb8____"       /* mov %eax, <original_entry_point_address> */
351        "\x50"           /* push %eax */
352        "\xb8____"       /* mov %eax, <GetCurrentProcess> */
353        "\xff\xd0"       /* call %eax */
354        "\x50"           /* push %eax */
355        "\xb8____"       /* mov %eax, <WriteProcessMemory> */
356        "\xff\xd0"       /* call %eax */
357        /* Jump to the original entry point */
358        "\xb8____"       /* mov %eax, <original_entry_point_address> */
359        "\xff\xe0";      /* jmp %eax */
360
361    static uint8_t const waiter[] =
362        "\xeb\xfe";      /* jmp <current> */
363
364    static uint8_t const jumper[] =
365        /* Jump to the injected loader */
366        "\xb8____"       /* mov eax, <loader_address> */
367        "\xff\xe0";      /* jmp eax */
368
369    CONTEXT ctx;
370    void *process = pinfo->hProcess;
371    void *thread = pinfo->hThread;
372    void *epaddr;
373    DWORD pid = pinfo->dwProcessId;
374
375    /* code:
376     * +---------------+--------------------+--------------+-------------+
377     * |     loader    | entry point backup | library name |   jumper    |
378     * |  len(loader)  |    len(jumper)     |   len(lib)   | len(jumper) |
379     * +---------------+--------------------+--------------+-------------+ */
380    uint8_t code[1024];
381
382    uint8_t *loaderaddr;
383    size_t liblen, loaderlen, waiterlen, jumperlen;
384    DWORD tmp;
385
386    liblen = strlen(lib) + 1;
387    loaderlen = sizeof(loader) - 1;
388    waiterlen = sizeof(waiter) - 1;
389    jumperlen = sizeof(jumper) - 1;
390    if (loaderlen + jumperlen + liblen > 1024)
391        return -1;
392
393    /* Allocate memory in the child for our injected code */
394    loaderaddr = VirtualAllocEx(process, NULL, loaderlen + jumperlen + liblen,
395                                MEM_COMMIT, PAGE_EXECUTE_READWRITE);
396    if(!loaderaddr)
397        return -1;
398
399    /* Create the first shellcode (jumper).
400     *
401     * The jumper's job is simply to jump at the second shellcode's location.
402     * It is written at the original entry point's location, which will in
403     * turn be restored by the second shellcode.
404     */
405    memcpy(code + loaderlen + jumperlen + liblen, jumper, jumperlen);
406    rep32(code + loaderlen + jumperlen + liblen, loaderaddr);
407
408    /* Create the second shellcode (loader, backuped entry point, and library
409     * name).
410     *
411     * The loader's job is to load the library by calling LoadLibraryA(),
412     * restore the original entry point using the backup copy, and jump
413     * back to the original entry point as if the process had just started.
414     *
415     * The second shellcode is written at a freshly allocated memory location.
416     */
417    memcpy(code, loader, loaderlen);
418    memcpy(code + loaderlen + jumperlen, lib, liblen);
419
420    /* Find the entry point address. It's simply in EAX. */
421    ctx.ContextFlags = CONTEXT_FULL;
422    GetThreadContext(thread, &ctx);
423    epaddr = (void *)(uintptr_t)ctx.Eax;
424
425    /* Backup the old entry point code */
426    ReadProcessMemory(process, epaddr, code + loaderlen, jumperlen, &tmp);
427    if(tmp != jumperlen)
428        return -1;
429
430    /* Replace the entry point code with a short jump to self, then resume
431     * the thread. This is necessary for CreateToolhelp32Snapshot() to
432     * work. */
433    WriteProcessMemory(process, epaddr, waiter, waiterlen, &tmp);
434    if(tmp != waiterlen)
435        return -1;
436    FlushInstructionCache(process, epaddr, waiterlen);
437    ResumeThread(thread);
438
439    /* Wait until the entry point is reached */
440    for (tmp = 0; tmp < 100; tmp++)
441    {
442        CONTEXT ctx;
443        ctx.ContextFlags = CONTEXT_FULL;
444        GetThreadContext(thread, &ctx);
445        if ((uintptr_t)ctx.Eip == (uintptr_t)epaddr)
446            break;
447        Sleep(10);
448    }
449    SuspendThread(thread);
450    if (tmp == 100)
451        return -1;
452
453    /* Remotely parse the target process's module list to get the addresses
454     * of the functions we need. This can only be done because we advanced
455     * the target's execution to the entry point. */
456    rep32(code, loaderaddr + loaderlen + jumperlen);
457    rep32(code, (void *)get_proc_address(process, pid, "LoadLibraryA"));
458    rep32(code, (void *)(uintptr_t)jumperlen);
459    rep32(code, loaderaddr + loaderlen);
460    rep32(code, epaddr);
461    rep32(code, (void *)get_proc_address(process, pid, "GetCurrentProcess"));
462    rep32(code, (void *)get_proc_address(process, pid, "WriteProcessMemory"));
463    rep32(code, epaddr);
464
465    /* Write our shellcodes into the target process */
466    WriteProcessMemory(process, epaddr, code + loaderlen + jumperlen + liblen,
467                       jumperlen, &tmp);
468    if(tmp != jumperlen)
469        return -1;
470    FlushInstructionCache(process, epaddr, waiterlen);
471
472    WriteProcessMemory(process, loaderaddr, code,
473                       loaderlen + jumperlen + liblen, &tmp);
474    if(tmp != loaderlen + jumperlen + liblen)
475        return -1;
476
477    return 0;
478}
479
480static intptr_t get_proc_address(void *process, DWORD pid, const char *func)
481{
482    char buf[1024];
483    size_t buflen = strlen(func) + 1;
484
485    MODULEENTRY32 entry;
486    intptr_t ret = 0;
487    DWORD tmp;
488    void *list;
489    int i, k;
490
491    list = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
492    entry.dwSize = sizeof(entry);
493    for(k = Module32First(list, &entry); k; k = Module32Next(list, &entry))
494    {
495        IMAGE_DOS_HEADER dos;
496        IMAGE_NT_HEADERS nt;
497        IMAGE_EXPORT_DIRECTORY expdir;
498
499        uint32_t exportaddr;
500        uint8_t const *base = entry.modBaseAddr;
501
502        if (strcmp("kernel32.dll", entry.szModule))
503            continue;
504
505        ReadProcessMemory(process, base, &dos, sizeof(dos), &tmp);
506        ReadProcessMemory(process, base + dos.e_lfanew, &nt, sizeof(nt), &tmp);
507
508        exportaddr = nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
509        if (!exportaddr)
510            continue;
511
512        ReadProcessMemory(process, base + exportaddr, &expdir, sizeof(expdir), &tmp);
513
514        for (i = 0; i < (int)expdir.NumberOfNames; i++)
515        {
516            uint32_t nameaddr, funcaddr;
517            uint16_t j;
518
519            /* Look for our function name in the list of names */
520            ReadProcessMemory(process, base + expdir.AddressOfNames
521                                            + i * sizeof(DWORD),
522                              &nameaddr, sizeof(nameaddr), &tmp);
523            ReadProcessMemory(process, base + nameaddr, buf, buflen, &tmp);
524
525            if (strcmp(buf, func))
526                continue;
527
528            /* If we found a function with this name, return its address */
529            ReadProcessMemory(process, base + expdir.AddressOfNameOrdinals
530                                            + i * sizeof(WORD),
531                                &j, sizeof(j), &tmp);
532            ReadProcessMemory(process, base + expdir.AddressOfFunctions
533                                            + j * sizeof(DWORD),
534                                &funcaddr, sizeof(funcaddr), &tmp);
535
536            ret = (intptr_t)base + funcaddr;
537            goto _finished;
538        }
539    }
540
541_finished:
542    CloseHandle(list);
543    return ret;
544}
545
546#endif
Note: See TracBrowser for help on using the repository browser.