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

Last change on this file since 4137 was 4137, checked in by sam, 4 years ago

Synchronise VS 2010 solution with [4112].

  • Property svn:keywords set to Id
File size: 13.9 KB
Line 
1/*
2 *  zzuf - general purpose fuzzer
3 *  Copyright (c) 2002, 2007-2009 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  $Id$
7 *
8 *  This program is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 *  myfork.c: launcher
17 */
18
19#include "config.h"
20
21#define _INCLUDE_POSIX_SOURCE /* for STDERR_FILENO on HP-UX */
22
23#if defined HAVE_STDINT_H
24#   include <stdint.h>
25#elif defined HAVE_INTTYPES_H
26#   include <inttypes.h>
27#endif
28#include <stdio.h>
29#include <stdlib.h>
30#if defined HAVE_UNISTD_H
31#   include <unistd.h>
32#endif
33#if defined HAVE_WINDOWS_H
34#   include <windows.h>
35#   include <imagehlp.h>
36#   include <tlhelp32.h>
37#endif
38#include <string.h>
39#include <fcntl.h> /* for O_BINARY */
40#if defined HAVE_SYS_RESOURCE_H
41#   include <sys/resource.h> /* for RLIMIT_AS */
42#endif
43
44#include "common.h"
45#include "opts.h"
46#include "random.h"
47#include "fd.h"
48#include "fuzz.h"
49#include "myfork.h"
50#include "md5.h"
51#include "timer.h"
52
53#if defined RLIMIT_AS
54#   define ZZUF_RLIMIT_MEM RLIMIT_AS
55#elif defined RLIMIT_VMEM
56#   define ZZUF_RLIMIT_MEM RLIMIT_VMEM
57#elif defined RLIMIT_DATA
58#   define ZZUF_RLIMIT_MEM RLIMIT_DATA
59#else
60#   undef ZZUF_RLIMIT_MEM
61#endif
62
63#if defined RLIMIT_CPU
64#   define ZZUF_RLIMIT_CPU RLIMIT_CPU
65#else
66#   undef ZZUF_RLIMIT_CPU
67#endif
68
69static int run_process(struct opts *, int[][2]);
70
71#if defined HAVE_WINDOWS_H
72static void rep32(uint8_t *buf, void *addr);
73static int dll_inject(void *, void *, char const *);
74static intptr_t get_base_address(DWORD);
75static intptr_t get_entry_point_offset(char const *);
76#endif
77
78int myfork(struct child *child, struct opts *opts)
79{
80    int pipes[3][2];
81    pid_t pid;
82    int i;
83
84    /* Prepare communication pipe */
85    for(i = 0; i < 3; i++)
86    {
87        int ret;
88#if defined HAVE_PIPE
89        ret = pipe(pipes[i]);
90#elif defined HAVE__PIPE
91        ret = _pipe(pipes[i], 512, _O_BINARY | O_NOINHERIT);
92#endif
93        if(ret < 0)
94        {
95            perror("pipe");
96            return -1;
97        }
98    }
99
100    pid = run_process(opts, pipes);
101    if(pid < 0)
102    {
103        /* FIXME: close pipes */
104        fprintf(stderr, "error launching `%s'\n", opts->newargv[0]);
105        return -1;
106    }
107
108    child->pid = pid;
109    for(i = 0; i < 3; i++)
110    {
111        close(pipes[i][1]);
112        child->fd[i] = pipes[i][0];
113    }
114
115    return 0;
116}
117
118#if !defined HAVE_SETENV
119static void setenv(char const *name, char const *value, int overwrite)
120{
121    char *str;
122
123    if(!overwrite && getenv(name))
124        return;
125
126    str = malloc(strlen(name) + 1 + strlen(value) + 1);
127    sprintf(str, "%s=%s", name, value);
128    putenv(str);
129}
130#endif
131
132static int run_process(struct opts *opts, int pipes[][2])
133{
134    char buf[64];
135#if defined HAVE_FORK
136    static int const files[] = { DEBUG_FILENO, STDERR_FILENO, STDOUT_FILENO };
137    char *libpath, *tmp;
138    int pid, j, len = strlen(opts->oldargv[0]);
139#   if defined __APPLE__
140#       define EXTRAINFO ""
141#       define PRELOAD "DYLD_INSERT_LIBRARIES"
142    setenv("DYLD_FORCE_FLAT_NAMESPACE", "1", 1);
143#   elif defined __osf__
144#       define EXTRAINFO ":DEFAULT"
145#       define PRELOAD "_RLD_LIST"
146#   else
147#       define EXTRAINFO ""
148#       define PRELOAD "LD_PRELOAD"
149#   endif
150#elif HAVE_WINDOWS_H
151    PROCESS_INFORMATION pinfo;
152    STARTUPINFO sinfo;
153    HANDLE pid;
154    void *epaddr;
155    int ret;
156#endif
157
158#if defined HAVE_FORK
159    /* Fork and launch child */
160    pid = fork();
161    if(pid < -1)
162        perror("fork");
163    if(pid != 0)
164        return pid;
165
166    /* We loop in reverse order so that files[0] is done last,
167     * just in case one of the other dup2()ed fds had the value */
168    for(j = 3; j--; )
169    {
170        close(pipes[j][0]);
171        if(pipes[j][1] != files[j])
172        {
173            dup2(pipes[j][1], files[j]);
174            close(pipes[j][1]);
175        }
176    }
177#endif
178
179#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
180    if(opts->maxmem >= 0)
181    {
182        struct rlimit rlim;
183        rlim.rlim_cur = opts->maxmem * 1000000;
184        rlim.rlim_max = opts->maxmem * 1000000;
185        setrlimit(ZZUF_RLIMIT_MEM, &rlim);
186    }
187#endif
188
189#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
190    if(opts->maxcpu >= 0)
191    {
192        struct rlimit rlim;
193        rlim.rlim_cur = opts->maxcpu;
194        rlim.rlim_max = opts->maxcpu + 5;
195        setrlimit(ZZUF_RLIMIT_CPU, &rlim);
196    }
197#endif
198
199    /* Set environment variables */
200    sprintf(buf, "%i", opts->seed);
201    setenv("ZZUF_SEED", buf, 1);
202    sprintf(buf, "%g", opts->minratio);
203    setenv("ZZUF_MINRATIO", buf, 1);
204    sprintf(buf, "%g", opts->maxratio);
205    setenv("ZZUF_MAXRATIO", buf, 1);
206
207#if defined HAVE_FORK
208    /* Make sure there is space for everything we might do. */
209    libpath = malloc(len + strlen(LIBDIR "/" LT_OBJDIR SONAME EXTRAINFO) + 1);
210    strcpy(libpath, opts->oldargv[0]);
211
212    /* If the binary name contains a '/', we look for a libzzuf in the
213     * same directory. Otherwise, we only look into the system directory
214     * to avoid shared library attacks. Write the result in libpath. */
215    tmp = strrchr(libpath, '/');
216    if(tmp)
217    {
218        strcpy(tmp + 1, LT_OBJDIR SONAME);
219        if(access(libpath, R_OK) < 0)
220            strcpy(libpath, LIBDIR "/" SONAME);
221    }
222    else
223        strcpy(libpath, LIBDIR "/" SONAME);
224
225    /* OSF1 only */
226    strcat(libpath, EXTRAINFO);
227
228    /* Do not clobber previous LD_PRELOAD values */
229    tmp = getenv(PRELOAD);
230    if(tmp && *tmp)
231    {
232        char *bigbuf = malloc(strlen(tmp) + strlen(libpath) + 2);
233        sprintf(bigbuf, "%s:%s", tmp, libpath);
234        free(libpath);
235        libpath = bigbuf;
236    }
237
238    setenv(PRELOAD, libpath, 1);
239    free(libpath);
240
241    if(execvp(opts->newargv[0], opts->newargv))
242    {
243        perror(opts->newargv[0]);
244        exit(EXIT_FAILURE);
245    }
246
247    exit(EXIT_SUCCESS);
248    /* no return */
249    return 0;
250#elif HAVE_WINDOWS_H
251    pid = GetCurrentProcess();
252
253    memset(&sinfo, 0, sizeof(sinfo));
254    sinfo.cb = sizeof(sinfo);
255    DuplicateHandle(pid, (HANDLE)_get_osfhandle(pipes[0][1]), pid,
256        /* FIXME */ &sinfo.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
257    DuplicateHandle(pid, (HANDLE)_get_osfhandle(pipes[1][1]), pid,
258                    &sinfo.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS);
259    DuplicateHandle(pid, (HANDLE)_get_osfhandle(pipes[2][1]), pid,
260                    &sinfo.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
261    sinfo.dwFlags = STARTF_USESTDHANDLES;
262    ret = CreateProcess(NULL, opts->newargv[0], NULL, NULL, FALSE,
263                        CREATE_SUSPENDED, NULL, NULL, &sinfo, &pinfo);
264    if(!ret)
265        return -1;
266
267    /* Get the child process's entry point address */
268    epaddr = (void *)get_entry_point(opts->newargv[0],
269                                     pinfo.dwProcessId);
270    if(!epaddr)
271        return -1;
272
273    /* Insert the replacement code */
274    ret = dll_inject(pinfo.hProcess, epaddr, SONAME);
275    if(ret < 0)
276    {
277        TerminateProcess(pinfo.hProcess, -1);
278        return -1;
279    }
280
281    ret = ResumeThread(pinfo.hThread);
282    if(ret < 0)
283    {
284        TerminateProcess(pinfo.hProcess, -1);
285        return -1;
286    }
287
288    return (long int)pinfo.hProcess;
289#endif
290}
291
292#if defined HAVE_WINDOWS_H
293static void rep32(uint8_t *buf, void *addr)
294{
295    while(buf++)
296        if (memcmp(buf, "____", 4) == 0)
297        {
298            memcpy(buf, &addr, 4);
299            return;
300        }
301}
302
303static int dll_inject(void *process, void *epaddr, char const *lib)
304{
305    static uint8_t const loader[] =
306        /* Load the injected DLL into memory */
307        "\xb8____"       /* mov %eax, <library_name_address> */
308        "\x50"           /* push %eax */
309        "\xb8____"       /* mov %eax, <LoadLibraryA> */
310        "\xff\xd0"       /* call %eax */
311        /* Restore the clobbered entry point code using our backup */
312        "\xb8\0\0\0\0"   /* mov %eax,0 */
313        "\x50"           /* push %eax */
314        "\xb8____"       /* mov %eax, <jumper_length> */
315        "\x50"           /* push %eax */
316        "\xb8____"       /* mov %eax, <backuped_entry_point_address> */
317        "\x50"           /* push %eax */
318        "\xb8____"       /* mov %eax, <original_entry_point_address> */
319        "\x50"           /* push %eax */
320        "\xb8____"       /* mov %eax, <GetCurrentProcess> */
321        "\xff\xd0"       /* call %eax */
322        "\x50"           /* push %eax */
323        "\xb8____"       /* mov %eax, <WriteProcessMemory> */
324        "\xff\xd0"       /* call %eax */
325        /* Jump to the original entry point */
326        "\xb8____"       /* mov %eax, <original_entry_point_address> */
327        "\xff\xe0";      /* jmp %eax */
328
329    static uint8_t const jumper[] =
330        /* Jump to the injected loader */
331        "\xb8____"       /* mov eax, <loader_address> */
332        "\xff\xe0";      /* jmp eax */
333
334    /* code:
335     * +---------------+--------------------+--------------+-------------+
336     * |     loader    | entry point backup | library name |   jumper    |
337     * |  len(loader)  |    len(jumper)     |   len(lib)   | len(jumper) |
338     * +---------------+--------------------+--------------+-------------+ */
339    uint8_t code[1024];
340
341    void *kernel32;
342    uint8_t *loaderaddr;
343    size_t liblen, loaderlen, jumperlen;
344    DWORD tmp;
345
346    liblen = strlen(lib) + 1;
347    loaderlen = sizeof(loader) - 1;
348    jumperlen = sizeof(jumper) - 1;
349    if (loaderlen + jumperlen + liblen > 1024)
350        return -1;
351
352    /* Allocate memory in the child for our injected code */
353    loaderaddr = VirtualAllocEx(process, NULL, loaderlen + jumperlen + liblen,
354                                MEM_COMMIT, PAGE_EXECUTE_READWRITE);
355    if(!loaderaddr)
356        return -1;
357
358    /* Create the first shellcode (jumper).
359     *
360     * The jumper's job is simply to jump at the second shellcode's location.
361     * It is written at the original entry point's location, which will in
362     * turn be restored by the second shellcode.
363     */
364    memcpy(code + loaderlen + jumperlen + liblen, jumper, jumperlen);
365    rep32(code + loaderlen + jumperlen + liblen, loaderaddr);
366
367    /* Create the second shellcode (loader, backuped entry point, and library
368     * name).
369     *
370     * The loader's job is to load the library by calling LoadLibraryA(),
371     * restore the original entry point using the backup copy, and jump
372     * back to the original entry point as if the process had just started.
373     *
374     * The second shellcode is written at a freshly allocated memory location.
375     */
376    memcpy(code, loader, loaderlen);
377    memcpy(code + loaderlen + jumperlen, lib, liblen);
378
379    /* Backup the old entry point code */
380    ReadProcessMemory(process, epaddr, code + loaderlen,
381                      jumperlen, &tmp);
382    if(tmp != jumperlen)
383        return -1;
384
385    /* FIXME: the GetProcAddress calls assume the library was loaded at
386     * the same address in the child process. This is wrong since Vista
387     * and its address space randomisation. */
388    kernel32 = LoadLibrary("kernel32.dll");
389    if(!kernel32)
390        return -1;
391
392    rep32(code, loaderaddr + loaderlen + jumperlen);
393    rep32(code, GetProcAddress(kernel32, "LoadLibraryA"));
394    rep32(code, (void *)(uintptr_t)jumperlen);
395    rep32(code, loaderaddr + loaderlen);
396    rep32(code, epaddr);
397    rep32(code, GetProcAddress(kernel32, "GetCurrentProcess"));
398    rep32(code, GetProcAddress(kernel32, "WriteProcessMemory"));
399    rep32(code, epaddr);
400    FreeLibrary(kernel32);
401
402    /* Write our shellcodes into the target process */
403    WriteProcessMemory(process, epaddr, code + loaderlen + jumperlen + liblen,
404                       jumperlen, &tmp);
405    if(tmp != jumperlen)
406        return -1;
407
408    WriteProcessMemory(process, loaderaddr, code,
409                       loaderlen + jumperlen + liblen, &tmp);
410    if(tmp != loaderlen + jumperlen + liblen)
411        return -1;
412
413    return 0;
414}
415
416/* Find the process's entry point address offset. The information is in
417 * the file's PE header. */
418static intptr_t get_entry_point(char const *name, DWORD pid)
419{
420    PIMAGE_DOS_HEADER dos;
421    PIMAGE_NT_HEADERS nt;
422    intptr_t ret = 0;
423    void *file, *map, *base;
424
425    file = CreateFile(name, GENERIC_READ, FILE_SHARE_READ,
426                      NULL, OPEN_EXISTING, 0, NULL);
427    if(file == INVALID_HANDLE_VALUE)
428        return ret;
429
430    map = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
431    if(!map)
432    {
433        CloseHandle(file);
434        return ret;
435    }
436
437    base = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
438    if(!base)
439    {
440        CloseHandle(map);
441        CloseHandle(file);
442        return ret;
443    }
444
445    /* Sanity checks */
446    dos = (PIMAGE_DOS_HEADER)base;
447    nt = (PIMAGE_NT_HEADERS)((char *)base + dos->e_lfanew);
448    if(dos->e_magic == IMAGE_DOS_SIGNATURE /* 0x5A4D */
449      && nt->Signature == IMAGE_NT_SIGNATURE /* 0x00004550 */
450      && nt->FileHeader.Machine == IMAGE_FILE_MACHINE_I386
451      && nt->OptionalHeader.Magic == 0x10b /* IMAGE_NT_OPTIONAL_HDR32_MAGIC */)
452    {
453        ret = get_base_address(pid);
454        /* Base address not found in the running process. Falling back
455         * to the header's information, which is unreliable because of
456         * Vista's address space randomisation. */
457        if (!ret)
458            ret = (intptr_t)nt->OptionalHeader.BaseOfCode;
459
460        ret += (intptr_t)nt->OptionalHeader.AddressOfEntryPoint;
461    }
462
463    UnmapViewOfFile(base);
464    CloseHandle(map);
465    CloseHandle(file);
466
467    return ret;
468}
469
470/* Find the process's base address once it is loaded in memory (the header
471 * information is unreliable because of Vista's ASLR). */
472static intptr_t get_base_address(DWORD pid)
473{
474    MODULEENTRY32 entry;
475    intptr_t ret = 0;
476    void *list;
477    int k;
478
479    list = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
480    entry.dwSize = sizeof(entry);
481    for(k = Module32First(list, &entry); k; k = Module32Next(list, &entry))
482    {
483        /* FIXME: how do we select the correct module? */
484        ret = (intptr_t)entry.modBaseAddr;
485    }
486    CloseHandle(list);
487
488    return ret;
489}
490
491#endif
Note: See TracBrowser for help on using the repository browser.