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

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

Full support for ASLR in the Win32 loader.

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