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

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

Fix copyright information and remove Id tag everywhere.

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