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

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

Add a few comments in the code for new Win32 strategies.

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