fix tmp file creation on win32, start to implement handling of win32 exception with GetExitCodeProcess
This commit is contained in:
+453
-450
@@ -1,85 +1,85 @@
|
||||
/*
|
||||
* zzuf - general purpose fuzzer
|
||||
* Copyright (c) 2002-2010 Sam Hocevar <sam@hocevar.net>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software. It comes without any warranty, to
|
||||
* the extent permitted by applicable law. You can redistribute it
|
||||
* and/or modify it under the terms of the Do What The Fuck You Want
|
||||
* To Public License, Version 2, as published by Sam Hocevar. See
|
||||
* http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* myfork.c: launcher
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define _INCLUDE_POSIX_SOURCE /* for STDERR_FILENO on HP-UX */
|
||||
|
||||
#if defined HAVE_STDINT_H
|
||||
# include <stdint.h>
|
||||
#elif defined HAVE_INTTYPES_H
|
||||
# include <inttypes.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#if defined HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#if defined HAVE_WINDOWS_H
|
||||
# include <windows.h>
|
||||
# include <imagehlp.h>
|
||||
# include <tlhelp32.h>
|
||||
#endif
|
||||
#if defined HAVE_IO_H
|
||||
# include <io.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <fcntl.h> /* for O_BINARY */
|
||||
#if defined HAVE_SYS_RESOURCE_H
|
||||
# include <sys/resource.h> /* for RLIMIT_AS */
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "opts.h"
|
||||
#include "random.h"
|
||||
#include "fd.h"
|
||||
#include "fuzz.h"
|
||||
#include "myfork.h"
|
||||
#include "md5.h"
|
||||
#include "timer.h"
|
||||
|
||||
/* Handle old libtool versions */
|
||||
#if !defined LT_OBJDIR
|
||||
# define LT_OBJDIR ".libs/"
|
||||
#endif
|
||||
|
||||
#if defined RLIMIT_AS
|
||||
# define ZZUF_RLIMIT_MEM RLIMIT_AS
|
||||
#elif defined RLIMIT_VMEM
|
||||
# define ZZUF_RLIMIT_MEM RLIMIT_VMEM
|
||||
#elif defined RLIMIT_DATA
|
||||
# define ZZUF_RLIMIT_MEM RLIMIT_DATA
|
||||
#else
|
||||
# undef ZZUF_RLIMIT_MEM
|
||||
#endif
|
||||
|
||||
#if defined RLIMIT_CPU
|
||||
# define ZZUF_RLIMIT_CPU RLIMIT_CPU
|
||||
#else
|
||||
# undef ZZUF_RLIMIT_CPU
|
||||
#endif
|
||||
|
||||
static int run_process(struct child *child, struct opts *, int[][2]);
|
||||
|
||||
#if defined HAVE_WINDOWS_H
|
||||
static void rep32(uint8_t *buf, void *addr);
|
||||
static int dll_inject(PROCESS_INFORMATION *, char const *);
|
||||
static void *get_proc_address(void *, DWORD, char const *);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* zzuf - general purpose fuzzer
|
||||
* Copyright (c) 2002-2010 Sam Hocevar <sam@hocevar.net>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software. It comes without any warranty, to
|
||||
* the extent permitted by applicable law. You can redistribute it
|
||||
* and/or modify it under the terms of the Do What The Fuck You Want
|
||||
* To Public License, Version 2, as published by Sam Hocevar. See
|
||||
* http://sam.zoy.org/wtfpl/COPYING for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* myfork.c: launcher
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define _INCLUDE_POSIX_SOURCE /* for STDERR_FILENO on HP-UX */
|
||||
|
||||
#if defined HAVE_STDINT_H
|
||||
# include <stdint.h>
|
||||
#elif defined HAVE_INTTYPES_H
|
||||
# include <inttypes.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#if defined HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#if defined HAVE_WINDOWS_H
|
||||
# include <windows.h>
|
||||
# include <imagehlp.h>
|
||||
# include <tlhelp32.h>
|
||||
#endif
|
||||
#if defined HAVE_IO_H
|
||||
# include <io.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <fcntl.h> /* for O_BINARY */
|
||||
#if defined HAVE_SYS_RESOURCE_H
|
||||
# include <sys/resource.h> /* for RLIMIT_AS */
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "opts.h"
|
||||
#include "random.h"
|
||||
#include "fd.h"
|
||||
#include "fuzz.h"
|
||||
#include "myfork.h"
|
||||
#include "md5.h"
|
||||
#include "timer.h"
|
||||
|
||||
/* Handle old libtool versions */
|
||||
#if !defined LT_OBJDIR
|
||||
# define LT_OBJDIR ".libs/"
|
||||
#endif
|
||||
|
||||
#if defined RLIMIT_AS
|
||||
# define ZZUF_RLIMIT_MEM RLIMIT_AS
|
||||
#elif defined RLIMIT_VMEM
|
||||
# define ZZUF_RLIMIT_MEM RLIMIT_VMEM
|
||||
#elif defined RLIMIT_DATA
|
||||
# define ZZUF_RLIMIT_MEM RLIMIT_DATA
|
||||
#else
|
||||
# undef ZZUF_RLIMIT_MEM
|
||||
#endif
|
||||
|
||||
#if defined RLIMIT_CPU
|
||||
# define ZZUF_RLIMIT_CPU RLIMIT_CPU
|
||||
#else
|
||||
# undef ZZUF_RLIMIT_CPU
|
||||
#endif
|
||||
|
||||
static int run_process(struct child *child, struct opts *, int[][2]);
|
||||
|
||||
#if defined HAVE_WINDOWS_H
|
||||
static void rep32(uint8_t *buf, void *addr);
|
||||
static int dll_inject(PROCESS_INFORMATION *, char const *);
|
||||
static void *get_proc_address(void *, DWORD, char const *);
|
||||
#endif
|
||||
|
||||
int myfork(struct child *child, struct opts *opts)
|
||||
{
|
||||
int pipes[3][2];
|
||||
@@ -178,371 +178,374 @@ int myfork(struct child *child, struct opts *opts)
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !defined HAVE_SETENV
|
||||
static void setenv(char const *name, char const *value, int overwrite)
|
||||
{
|
||||
char *str;
|
||||
|
||||
if(!overwrite && getenv(name))
|
||||
return;
|
||||
|
||||
str = malloc(strlen(name) + 1 + strlen(value) + 1);
|
||||
sprintf(str, "%s=%s", name, value);
|
||||
putenv(str);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int run_process(struct child *child, struct opts *opts, int pipes[][2])
|
||||
{
|
||||
char buf[64];
|
||||
#if defined HAVE_FORK
|
||||
static int const files[] = { DEBUG_FILENO, STDERR_FILENO, STDOUT_FILENO };
|
||||
char *libpath, *tmp;
|
||||
int pid, j, len = strlen(opts->oldargv[0]);
|
||||
# if defined __APPLE__
|
||||
# define EXTRAINFO ""
|
||||
# define PRELOAD "DYLD_INSERT_LIBRARIES"
|
||||
/* Only enforce flat namespace in preload mode */
|
||||
if (opts->opmode == OPMODE_PRELOAD)
|
||||
setenv("DYLD_FORCE_FLAT_NAMESPACE", "1", 1);
|
||||
# elif defined __osf__
|
||||
# define EXTRAINFO ":DEFAULT"
|
||||
# define PRELOAD "_RLD_LIST"
|
||||
# elif defined __sun && defined __i386
|
||||
# define EXTRAINFO ""
|
||||
# define PRELOAD "LD_PRELOAD_32"
|
||||
# else
|
||||
# define EXTRAINFO ""
|
||||
# define PRELOAD "LD_PRELOAD"
|
||||
# endif
|
||||
#elif HAVE_WINDOWS_H
|
||||
PROCESS_INFORMATION pinfo;
|
||||
STARTUPINFO sinfo;
|
||||
HANDLE pid;
|
||||
char *cmdline;
|
||||
int i, ret, len;
|
||||
#endif
|
||||
|
||||
#if defined HAVE_FORK
|
||||
/* Fork and launch child */
|
||||
pid = fork();
|
||||
if(pid < 0)
|
||||
perror("fork");
|
||||
if(pid != 0)
|
||||
return pid;
|
||||
|
||||
/* We loop in reverse order so that files[0] is done last,
|
||||
* just in case one of the other dup2()ed fds had the value */
|
||||
for(j = 3; j--; )
|
||||
{
|
||||
close(pipes[j][0]);
|
||||
if(pipes[j][1] != files[j])
|
||||
{
|
||||
dup2(pipes[j][1], files[j]);
|
||||
close(pipes[j][1]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
|
||||
if(opts->maxmem >= 0)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
rlim.rlim_cur = opts->maxmem * 1048576;
|
||||
rlim.rlim_max = opts->maxmem * 1048576;
|
||||
setrlimit(ZZUF_RLIMIT_MEM, &rlim);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
|
||||
if(opts->maxcpu >= 0)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
rlim.rlim_cur = opts->maxcpu;
|
||||
rlim.rlim_max = opts->maxcpu + 5;
|
||||
setrlimit(ZZUF_RLIMIT_CPU, &rlim);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set environment variables */
|
||||
#if defined _WIN32
|
||||
sprintf(buf, "%i", _get_osfhandle(pipes[0][1]));
|
||||
#else
|
||||
sprintf(buf, "%i", pipes[0][1]);
|
||||
#endif
|
||||
setenv("ZZUF_DEBUGFD", buf, 1);
|
||||
sprintf(buf, "%i", opts->seed);
|
||||
setenv("ZZUF_SEED", buf, 1);
|
||||
sprintf(buf, "%g", opts->minratio);
|
||||
setenv("ZZUF_MINRATIO", buf, 1);
|
||||
sprintf(buf, "%g", opts->maxratio);
|
||||
setenv("ZZUF_MAXRATIO", buf, 1);
|
||||
|
||||
#if defined HAVE_FORK
|
||||
/* Make sure there is space for everything we might do. */
|
||||
libpath = malloc(len + strlen(LIBDIR "/" LT_OBJDIR SONAME EXTRAINFO) + 1);
|
||||
strcpy(libpath, opts->oldargv[0]);
|
||||
|
||||
/* If the binary name contains a '/', we look for a libzzuf in the
|
||||
* same directory. Otherwise, we only look into the system directory
|
||||
* to avoid shared library attacks. Write the result in libpath. */
|
||||
tmp = strrchr(libpath, '/');
|
||||
if(tmp)
|
||||
{
|
||||
strcpy(tmp + 1, LT_OBJDIR SONAME);
|
||||
if(access(libpath, R_OK) < 0)
|
||||
strcpy(libpath, LIBDIR "/" SONAME);
|
||||
}
|
||||
else
|
||||
strcpy(libpath, LIBDIR "/" SONAME);
|
||||
|
||||
/* OSF1 only */
|
||||
strcat(libpath, EXTRAINFO);
|
||||
|
||||
/* Do not clobber previous LD_PRELOAD values */
|
||||
tmp = getenv(PRELOAD);
|
||||
if(tmp && *tmp)
|
||||
{
|
||||
char *bigbuf = malloc(strlen(tmp) + strlen(libpath) + 2);
|
||||
sprintf(bigbuf, "%s:%s", tmp, libpath);
|
||||
free(libpath);
|
||||
libpath = bigbuf;
|
||||
}
|
||||
|
||||
/* Only preload the library in preload mode */
|
||||
if (opts->opmode == OPMODE_PRELOAD)
|
||||
setenv(PRELOAD, libpath, 1);
|
||||
free(libpath);
|
||||
|
||||
if(execvp(child->newargv[0], child->newargv))
|
||||
{
|
||||
perror(child->newargv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
/* no return */
|
||||
return 0;
|
||||
#elif HAVE_WINDOWS_H
|
||||
pid = GetCurrentProcess();
|
||||
|
||||
/* Inherit standard handles */
|
||||
memset(&sinfo, 0, sizeof(sinfo));
|
||||
sinfo.cb = sizeof(sinfo);
|
||||
sinfo.hStdInput = INVALID_HANDLE_VALUE;
|
||||
sinfo.hStdOutput = (HANDLE)_get_osfhandle(pipes[2][1]);
|
||||
sinfo.hStdError = (HANDLE)_get_osfhandle(pipes[1][1]);
|
||||
sinfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
/* Build the commandline */
|
||||
for (i = 0, len = 0; child->newargv[i]; i++)
|
||||
len += strlen(child->newargv[i]) + 1;
|
||||
cmdline = malloc(len);
|
||||
for (i = 0, len = 0; child->newargv[i]; i++)
|
||||
{
|
||||
strcpy(cmdline + len, child->newargv[i]);
|
||||
len += strlen(child->newargv[i]) + 1;
|
||||
cmdline[len - 1] = ' ';
|
||||
}
|
||||
cmdline[len - 1] = '\0';
|
||||
|
||||
/* Create the process in suspended state */
|
||||
ret = CreateProcess(child->newargv[0], cmdline, NULL, NULL, TRUE,
|
||||
CREATE_SUSPENDED, NULL, NULL, &sinfo, &pinfo);
|
||||
free(cmdline);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
LPTSTR buf;
|
||||
DWORD err = GetLastError();
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, err, 0, (LPTSTR)&buf, 0, NULL);
|
||||
fprintf(stderr, "error launching `%s': %s\n", child->newargv[0], buf);
|
||||
LocalFree(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Insert the replacement code */
|
||||
ret = dll_inject(&pinfo, SONAME);
|
||||
if(ret < 0)
|
||||
{
|
||||
TerminateProcess(pinfo.hProcess, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ResumeThread(pinfo.hThread);
|
||||
if(ret < 0)
|
||||
{
|
||||
TerminateProcess(pinfo.hProcess, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (long int)pinfo.hProcess;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined HAVE_WINDOWS_H
|
||||
|
||||
static int dll_inject(PROCESS_INFORMATION *pinfo, char const *lib)
|
||||
{
|
||||
int res = -1;
|
||||
|
||||
/* This payload allows us to load arbitrary module located at the end of this buffer */
|
||||
static uint8_t const ldr[] =
|
||||
{
|
||||
"\x60" /* pushad */
|
||||
"\xEB\x0E" /* jmp short 0x11 */
|
||||
"\xB8____" /* mov eax,LoadLibraryA */
|
||||
"\xFF\xD0" /* call eax */
|
||||
"\x85\xC0" /* test eax,eax */
|
||||
"\x75\x01" /* jnz 0xf */
|
||||
"\xCC" /* int3 */
|
||||
"\x61" /* popad */
|
||||
"\xC3" /* ret */
|
||||
"\xE8\xED\xFF\xFF\xFF" /* call dword 0x3 */
|
||||
};
|
||||
|
||||
/* We use this code to make the targeted process waits for us */
|
||||
static uint8_t const wait[] = "\xeb\xfe"; /* jmp $-1 */
|
||||
size_t wait_len = sizeof(wait) - 1;
|
||||
uint8_t orig_data[2];
|
||||
|
||||
void *process = pinfo->hProcess;
|
||||
void *thread = pinfo->hThread;
|
||||
DWORD pid = pinfo->dwProcessId;
|
||||
void *rldlib = NULL;
|
||||
DWORD written = 0;
|
||||
DWORD old_prot = 0;
|
||||
|
||||
/* Payload */
|
||||
void *rpl = NULL;
|
||||
uint8_t *pl = NULL;
|
||||
size_t pl_len = sizeof(ldr) - 1 + strlen(lib) + 1;
|
||||
|
||||
CONTEXT ctxt;
|
||||
DWORD oep; /* Original Entry Point */
|
||||
|
||||
/* Use the main thread to inject our library */
|
||||
ctxt.ContextFlags = CONTEXT_FULL;
|
||||
if (!GetThreadContext(thread, &ctxt)) goto _return;
|
||||
|
||||
/* Make the target program waits when it reachs the original entry point, because we can't do many thing from the windows loader */
|
||||
oep = ctxt.Eax;
|
||||
if (!ReadProcessMemory(process, (LPVOID)oep, orig_data, sizeof(orig_data), &written) || written != sizeof(orig_data)) goto _return; /* save original opcode */
|
||||
if (!WriteProcessMemory(process, (LPVOID)oep, wait, wait_len , &written) || written != wait_len) goto _return; /* write jmp short $-1 */
|
||||
if (!FlushInstructionCache(process, (LPVOID)oep, wait_len)) goto _return;
|
||||
if (ResumeThread(thread) == (DWORD)-1) goto _return;
|
||||
|
||||
/* Stop when the program reachs the oep */
|
||||
while (oep != ctxt.Eip)
|
||||
{
|
||||
if (!GetThreadContext(thread, &ctxt)) goto _return;
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
if (SuspendThread(thread) == (DWORD)-1) goto _return;
|
||||
|
||||
/* Resolve LoadLibraryA from the target process memory context */
|
||||
rldlib = get_proc_address(process, pid, "LoadLibraryA");
|
||||
|
||||
if ((rpl = VirtualAllocEx(process, NULL, pl_len, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == NULL) goto _return;
|
||||
|
||||
/* Emulate a call to the ldr code, thus the ret instruction from ldr will get eip back to the original entry point */
|
||||
ctxt.Esp -= 4;
|
||||
if (!WriteProcessMemory(process, (LPVOID)ctxt.Esp, &oep, sizeof(oep), &written) || written != sizeof(oep)) goto _return;
|
||||
ctxt.Eip = (DWORD)rpl;
|
||||
if (!SetThreadContext(thread, &ctxt)) goto _return;
|
||||
|
||||
/* Forge the payload */
|
||||
if ((pl = (uint8_t *)malloc(pl_len)) == NULL) goto _return;
|
||||
memcpy(pl, ldr, sizeof(ldr) - 1);
|
||||
memcpy(pl + 4, &rldlib, sizeof(rldlib)); /* Write the address of LoadLibraryA */
|
||||
strcpy((char *)(pl + sizeof(ldr) - 1), lib); /* Write the first parameter of LoadLibraryA */
|
||||
|
||||
if (!WriteProcessMemory(process, rpl, pl, pl_len, &written) || written != pl_len) goto _return;
|
||||
|
||||
/* Restore original opcode */
|
||||
if (!WriteProcessMemory(process, (LPVOID)oep, orig_data, sizeof(orig_data), &written) || written != sizeof(orig_data)) goto _return;
|
||||
|
||||
if (!FlushInstructionCache(process, rpl, pl_len)) goto _return;
|
||||
if (!FlushInstructionCache(process, (LPVOID)oep, sizeof(orig_data))) goto _return;
|
||||
|
||||
res = 0;
|
||||
_return:
|
||||
if (pl != NULL) free(pl);
|
||||
|
||||
/* We must not free remote allocated memory since they will be used after the process will be resumed */
|
||||
return res;
|
||||
}
|
||||
|
||||
static void *get_proc_address(void *process, DWORD pid, const char *func)
|
||||
{
|
||||
char buf[1024];
|
||||
size_t buflen = strlen(func) + 1;
|
||||
|
||||
MODULEENTRY32 entry;
|
||||
void *ret = 0;
|
||||
DWORD tmp;
|
||||
void *list;
|
||||
int i, k;
|
||||
|
||||
list = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
|
||||
entry.dwSize = sizeof(entry);
|
||||
for(k = Module32First(list, &entry); k; k = Module32Next(list, &entry))
|
||||
{
|
||||
IMAGE_DOS_HEADER dos;
|
||||
IMAGE_NT_HEADERS nt;
|
||||
IMAGE_EXPORT_DIRECTORY expdir;
|
||||
|
||||
uint32_t exportaddr;
|
||||
uint8_t *base = entry.modBaseAddr;
|
||||
|
||||
if (strcmp("kernel32.dll", entry.szModule))
|
||||
continue;
|
||||
|
||||
ReadProcessMemory(process, base, &dos, sizeof(dos), &tmp);
|
||||
ReadProcessMemory(process, base + dos.e_lfanew, &nt, sizeof(nt), &tmp);
|
||||
|
||||
exportaddr = nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
if (!exportaddr)
|
||||
continue;
|
||||
|
||||
ReadProcessMemory(process, base + exportaddr, &expdir, sizeof(expdir), &tmp);
|
||||
|
||||
for (i = 0; i < (int)expdir.NumberOfNames; i++)
|
||||
{
|
||||
uint32_t nameaddr, funcaddr;
|
||||
uint16_t j;
|
||||
|
||||
/* Look for our function name in the list of names */
|
||||
ReadProcessMemory(process, base + expdir.AddressOfNames
|
||||
+ i * sizeof(DWORD),
|
||||
&nameaddr, sizeof(nameaddr), &tmp);
|
||||
ReadProcessMemory(process, base + nameaddr, buf, buflen, &tmp);
|
||||
|
||||
if (strcmp(buf, func))
|
||||
continue;
|
||||
|
||||
/* If we found a function with this name, return its address */
|
||||
ReadProcessMemory(process, base + expdir.AddressOfNameOrdinals
|
||||
+ i * sizeof(WORD),
|
||||
&j, sizeof(j), &tmp);
|
||||
ReadProcessMemory(process, base + expdir.AddressOfFunctions
|
||||
+ j * sizeof(DWORD),
|
||||
&funcaddr, sizeof(funcaddr), &tmp);
|
||||
|
||||
ret = base + funcaddr;
|
||||
goto _finished;
|
||||
}
|
||||
}
|
||||
|
||||
_finished:
|
||||
CloseHandle(list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined HAVE_SETENV
|
||||
static void setenv(char const *name, char const *value, int overwrite)
|
||||
{
|
||||
char *str;
|
||||
|
||||
if(!overwrite && getenv(name))
|
||||
return;
|
||||
|
||||
str = malloc(strlen(name) + 1 + strlen(value) + 1);
|
||||
sprintf(str, "%s=%s", name, value);
|
||||
putenv(str);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int run_process(struct child *child, struct opts *opts, int pipes[][2])
|
||||
{
|
||||
char buf[64];
|
||||
#if defined HAVE_FORK
|
||||
static int const files[] = { DEBUG_FILENO, STDERR_FILENO, STDOUT_FILENO };
|
||||
char *libpath, *tmp;
|
||||
int pid, j, len = strlen(opts->oldargv[0]);
|
||||
# if defined __APPLE__
|
||||
# define EXTRAINFO ""
|
||||
# define PRELOAD "DYLD_INSERT_LIBRARIES"
|
||||
/* Only enforce flat namespace in preload mode */
|
||||
if (opts->opmode == OPMODE_PRELOAD)
|
||||
setenv("DYLD_FORCE_FLAT_NAMESPACE", "1", 1);
|
||||
# elif defined __osf__
|
||||
# define EXTRAINFO ":DEFAULT"
|
||||
# define PRELOAD "_RLD_LIST"
|
||||
# elif defined __sun && defined __i386
|
||||
# define EXTRAINFO ""
|
||||
# define PRELOAD "LD_PRELOAD_32"
|
||||
# else
|
||||
# define EXTRAINFO ""
|
||||
# define PRELOAD "LD_PRELOAD"
|
||||
# endif
|
||||
#elif HAVE_WINDOWS_H
|
||||
PROCESS_INFORMATION pinfo;
|
||||
STARTUPINFO sinfo;
|
||||
HANDLE pid;
|
||||
char *cmdline;
|
||||
int i, ret, len;
|
||||
#endif
|
||||
|
||||
#if defined HAVE_FORK
|
||||
/* Fork and launch child */
|
||||
pid = fork();
|
||||
if(pid < 0)
|
||||
perror("fork");
|
||||
if(pid != 0)
|
||||
return pid;
|
||||
|
||||
/* We loop in reverse order so that files[0] is done last,
|
||||
* just in case one of the other dup2()ed fds had the value */
|
||||
for(j = 3; j--; )
|
||||
{
|
||||
close(pipes[j][0]);
|
||||
if(pipes[j][1] != files[j])
|
||||
{
|
||||
dup2(pipes[j][1], files[j]);
|
||||
close(pipes[j][1]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_MEM
|
||||
if(opts->maxmem >= 0)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
rlim.rlim_cur = opts->maxmem * 1048576;
|
||||
rlim.rlim_max = opts->maxmem * 1048576;
|
||||
setrlimit(ZZUF_RLIMIT_MEM, &rlim);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined HAVE_SETRLIMIT && defined ZZUF_RLIMIT_CPU
|
||||
if(opts->maxcpu >= 0)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
rlim.rlim_cur = opts->maxcpu;
|
||||
rlim.rlim_max = opts->maxcpu + 5;
|
||||
setrlimit(ZZUF_RLIMIT_CPU, &rlim);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set environment variables */
|
||||
#if defined _WIN32
|
||||
sprintf(buf, "%i", _get_osfhandle(pipes[0][1]));
|
||||
#else
|
||||
sprintf(buf, "%i", pipes[0][1]);
|
||||
#endif
|
||||
setenv("ZZUF_DEBUGFD", buf, 1);
|
||||
sprintf(buf, "%i", opts->seed);
|
||||
setenv("ZZUF_SEED", buf, 1);
|
||||
sprintf(buf, "%g", opts->minratio);
|
||||
setenv("ZZUF_MINRATIO", buf, 1);
|
||||
sprintf(buf, "%g", opts->maxratio);
|
||||
setenv("ZZUF_MAXRATIO", buf, 1);
|
||||
|
||||
#if defined HAVE_FORK
|
||||
/* Make sure there is space for everything we might do. */
|
||||
libpath = malloc(len + strlen(LIBDIR "/" LT_OBJDIR SONAME EXTRAINFO) + 1);
|
||||
strcpy(libpath, opts->oldargv[0]);
|
||||
|
||||
/* If the binary name contains a '/', we look for a libzzuf in the
|
||||
* same directory. Otherwise, we only look into the system directory
|
||||
* to avoid shared library attacks. Write the result in libpath. */
|
||||
tmp = strrchr(libpath, '/');
|
||||
if(tmp)
|
||||
{
|
||||
strcpy(tmp + 1, LT_OBJDIR SONAME);
|
||||
if(access(libpath, R_OK) < 0)
|
||||
strcpy(libpath, LIBDIR "/" SONAME);
|
||||
}
|
||||
else
|
||||
strcpy(libpath, LIBDIR "/" SONAME);
|
||||
|
||||
/* OSF1 only */
|
||||
strcat(libpath, EXTRAINFO);
|
||||
|
||||
/* Do not clobber previous LD_PRELOAD values */
|
||||
tmp = getenv(PRELOAD);
|
||||
if(tmp && *tmp)
|
||||
{
|
||||
char *bigbuf = malloc(strlen(tmp) + strlen(libpath) + 2);
|
||||
sprintf(bigbuf, "%s:%s", tmp, libpath);
|
||||
free(libpath);
|
||||
libpath = bigbuf;
|
||||
}
|
||||
|
||||
/* Only preload the library in preload mode */
|
||||
if (opts->opmode == OPMODE_PRELOAD)
|
||||
setenv(PRELOAD, libpath, 1);
|
||||
free(libpath);
|
||||
|
||||
if(execvp(child->newargv[0], child->newargv))
|
||||
{
|
||||
perror(child->newargv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
/* no return */
|
||||
return 0;
|
||||
#elif HAVE_WINDOWS_H
|
||||
pid = GetCurrentProcess();
|
||||
|
||||
/* Inherit standard handles */
|
||||
memset(&sinfo, 0, sizeof(sinfo));
|
||||
sinfo.cb = sizeof(sinfo);
|
||||
sinfo.hStdInput = INVALID_HANDLE_VALUE;
|
||||
sinfo.hStdOutput = (HANDLE)_get_osfhandle(pipes[2][1]);
|
||||
sinfo.hStdError = (HANDLE)_get_osfhandle(pipes[1][1]);
|
||||
sinfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
/* Build the commandline */
|
||||
for (i = 0, len = 0; child->newargv[i]; i++)
|
||||
len += strlen(child->newargv[i]) + 1;
|
||||
cmdline = malloc(len);
|
||||
for (i = 0, len = 0; child->newargv[i]; i++)
|
||||
{
|
||||
strcpy(cmdline + len, child->newargv[i]);
|
||||
len += strlen(child->newargv[i]) + 1;
|
||||
cmdline[len - 1] = ' ';
|
||||
}
|
||||
cmdline[len - 1] = '\0';
|
||||
|
||||
/* Create the process in suspended state */
|
||||
ret = CreateProcess(child->newargv[0], cmdline, NULL, NULL, TRUE,
|
||||
CREATE_SUSPENDED, NULL, NULL, &sinfo, &pinfo);
|
||||
free(cmdline);
|
||||
|
||||
child->process_handle = pinfo.hProcess;
|
||||
child->pid = pinfo.dwProcessId;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
LPTSTR buf;
|
||||
DWORD err = GetLastError();
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, err, 0, (LPTSTR)&buf, 0, NULL);
|
||||
fprintf(stderr, "error launching `%s': %s\n", child->newargv[0], buf);
|
||||
LocalFree(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Insert the replacement code */
|
||||
ret = dll_inject(&pinfo, SONAME);
|
||||
if(ret < 0)
|
||||
{
|
||||
TerminateProcess(pinfo.hProcess, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ResumeThread(pinfo.hThread);
|
||||
if(ret < 0)
|
||||
{
|
||||
TerminateProcess(pinfo.hProcess, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (long int)pinfo.hProcess;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined HAVE_WINDOWS_H
|
||||
|
||||
static int dll_inject(PROCESS_INFORMATION *pinfo, char const *lib)
|
||||
{
|
||||
int res = -1;
|
||||
|
||||
/* This payload allows us to load arbitrary module located at the end of this buffer */
|
||||
static uint8_t const ldr[] =
|
||||
{
|
||||
"\x60" /* pushad */
|
||||
"\xEB\x0E" /* jmp short 0x11 */
|
||||
"\xB8____" /* mov eax,LoadLibraryA */
|
||||
"\xFF\xD0" /* call eax */
|
||||
"\x85\xC0" /* test eax,eax */
|
||||
"\x75\x01" /* jnz 0xf */
|
||||
"\xCC" /* int3 */
|
||||
"\x61" /* popad */
|
||||
"\xC3" /* ret */
|
||||
"\xE8\xED\xFF\xFF\xFF" /* call dword 0x3 */
|
||||
};
|
||||
|
||||
/* We use this code to make the targeted process waits for us */
|
||||
static uint8_t const wait[] = "\xeb\xfe"; /* jmp $-1 */
|
||||
size_t wait_len = sizeof(wait) - 1;
|
||||
uint8_t orig_data[2];
|
||||
|
||||
void *process = pinfo->hProcess;
|
||||
void *thread = pinfo->hThread;
|
||||
DWORD pid = pinfo->dwProcessId;
|
||||
void *rldlib = NULL;
|
||||
DWORD written = 0;
|
||||
DWORD old_prot = 0;
|
||||
|
||||
/* Payload */
|
||||
void *rpl = NULL;
|
||||
uint8_t *pl = NULL;
|
||||
size_t pl_len = sizeof(ldr) - 1 + strlen(lib) + 1;
|
||||
|
||||
CONTEXT ctxt;
|
||||
DWORD oep; /* Original Entry Point */
|
||||
|
||||
/* Use the main thread to inject our library */
|
||||
ctxt.ContextFlags = CONTEXT_FULL;
|
||||
if (!GetThreadContext(thread, &ctxt)) goto _return;
|
||||
|
||||
/* Make the target program waits when it reachs the original entry point, because we can't do many thing from the windows loader */
|
||||
oep = ctxt.Eax;
|
||||
if (!ReadProcessMemory(process, (LPVOID)oep, orig_data, sizeof(orig_data), &written) || written != sizeof(orig_data)) goto _return; /* save original opcode */
|
||||
if (!WriteProcessMemory(process, (LPVOID)oep, wait, wait_len , &written) || written != wait_len) goto _return; /* write jmp short $-1 */
|
||||
if (!FlushInstructionCache(process, (LPVOID)oep, wait_len)) goto _return;
|
||||
if (ResumeThread(thread) == (DWORD)-1) goto _return;
|
||||
|
||||
/* Stop when the program reachs the oep */
|
||||
while (oep != ctxt.Eip)
|
||||
{
|
||||
if (!GetThreadContext(thread, &ctxt)) goto _return;
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
if (SuspendThread(thread) == (DWORD)-1) goto _return;
|
||||
|
||||
/* Resolve LoadLibraryA from the target process memory context */
|
||||
rldlib = get_proc_address(process, pid, "LoadLibraryA");
|
||||
|
||||
if ((rpl = VirtualAllocEx(process, NULL, pl_len, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == NULL) goto _return;
|
||||
|
||||
/* Emulate a call to the ldr code, thus the ret instruction from ldr will get eip back to the original entry point */
|
||||
ctxt.Esp -= 4;
|
||||
if (!WriteProcessMemory(process, (LPVOID)ctxt.Esp, &oep, sizeof(oep), &written) || written != sizeof(oep)) goto _return;
|
||||
ctxt.Eip = (DWORD)rpl;
|
||||
if (!SetThreadContext(thread, &ctxt)) goto _return;
|
||||
|
||||
/* Forge the payload */
|
||||
if ((pl = (uint8_t *)malloc(pl_len)) == NULL) goto _return;
|
||||
memcpy(pl, ldr, sizeof(ldr) - 1);
|
||||
memcpy(pl + 4, &rldlib, sizeof(rldlib)); /* Write the address of LoadLibraryA */
|
||||
strcpy((char *)(pl + sizeof(ldr) - 1), lib); /* Write the first parameter of LoadLibraryA */
|
||||
|
||||
if (!WriteProcessMemory(process, rpl, pl, pl_len, &written) || written != pl_len) goto _return;
|
||||
|
||||
/* Restore original opcode */
|
||||
if (!WriteProcessMemory(process, (LPVOID)oep, orig_data, sizeof(orig_data), &written) || written != sizeof(orig_data)) goto _return;
|
||||
|
||||
if (!FlushInstructionCache(process, rpl, pl_len)) goto _return;
|
||||
if (!FlushInstructionCache(process, (LPVOID)oep, sizeof(orig_data))) goto _return;
|
||||
|
||||
res = 0;
|
||||
_return:
|
||||
if (pl != NULL) free(pl);
|
||||
|
||||
/* We must not free remote allocated memory since they will be used after the process will be resumed */
|
||||
return res;
|
||||
}
|
||||
|
||||
static void *get_proc_address(void *process, DWORD pid, const char *func)
|
||||
{
|
||||
char buf[1024];
|
||||
size_t buflen = strlen(func) + 1;
|
||||
|
||||
MODULEENTRY32 entry;
|
||||
void *ret = 0;
|
||||
DWORD tmp;
|
||||
void *list;
|
||||
int i, k;
|
||||
|
||||
list = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
|
||||
entry.dwSize = sizeof(entry);
|
||||
for(k = Module32First(list, &entry); k; k = Module32Next(list, &entry))
|
||||
{
|
||||
IMAGE_DOS_HEADER dos;
|
||||
IMAGE_NT_HEADERS nt;
|
||||
IMAGE_EXPORT_DIRECTORY expdir;
|
||||
|
||||
uint32_t exportaddr;
|
||||
uint8_t *base = entry.modBaseAddr;
|
||||
|
||||
if (strcmp("kernel32.dll", entry.szModule))
|
||||
continue;
|
||||
|
||||
ReadProcessMemory(process, base, &dos, sizeof(dos), &tmp);
|
||||
ReadProcessMemory(process, base + dos.e_lfanew, &nt, sizeof(nt), &tmp);
|
||||
|
||||
exportaddr = nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
if (!exportaddr)
|
||||
continue;
|
||||
|
||||
ReadProcessMemory(process, base + exportaddr, &expdir, sizeof(expdir), &tmp);
|
||||
|
||||
for (i = 0; i < (int)expdir.NumberOfNames; i++)
|
||||
{
|
||||
uint32_t nameaddr, funcaddr;
|
||||
uint16_t j;
|
||||
|
||||
/* Look for our function name in the list of names */
|
||||
ReadProcessMemory(process, base + expdir.AddressOfNames
|
||||
+ i * sizeof(DWORD),
|
||||
&nameaddr, sizeof(nameaddr), &tmp);
|
||||
ReadProcessMemory(process, base + nameaddr, buf, buflen, &tmp);
|
||||
|
||||
if (strcmp(buf, func))
|
||||
continue;
|
||||
|
||||
/* If we found a function with this name, return its address */
|
||||
ReadProcessMemory(process, base + expdir.AddressOfNameOrdinals
|
||||
+ i * sizeof(WORD),
|
||||
&j, sizeof(j), &tmp);
|
||||
ReadProcessMemory(process, base + expdir.AddressOfFunctions
|
||||
+ j * sizeof(DWORD),
|
||||
&funcaddr, sizeof(funcaddr), &tmp);
|
||||
|
||||
ret = base + funcaddr;
|
||||
goto _finished;
|
||||
}
|
||||
}
|
||||
|
||||
_finished:
|
||||
CloseHandle(list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
* opts.h: configuration handling
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <Windows.h>
|
||||
#endif
|
||||
|
||||
struct opts
|
||||
{
|
||||
enum opmode
|
||||
@@ -54,6 +58,9 @@ struct opts
|
||||
} status;
|
||||
|
||||
pid_t pid;
|
||||
#ifdef _WIN32
|
||||
HANDLE process_handle;
|
||||
#endif
|
||||
int fd[3]; /* 0 is debug, 1 is stderr, 2 is stdout */
|
||||
int bytes, seed;
|
||||
double ratio;
|
||||
|
||||
+49
-8
@@ -114,6 +114,8 @@ static void usage(void);
|
||||
|
||||
#if defined _WIN32
|
||||
# include <Windows.h>
|
||||
# include <fcntl.h> /* _O_RDWR */
|
||||
# include <io.h> /* _open */
|
||||
static CRITICAL_SECTION _zz_pipe_cs;
|
||||
#endif
|
||||
|
||||
@@ -713,10 +715,12 @@ static void spawn_children(struct opts *opts)
|
||||
if (!fpin)
|
||||
continue;
|
||||
|
||||
sprintf(tmpname, "%s/zzuf.%i.XXXXXX", tmpdir, (int)getpid());
|
||||
#ifdef _WIN32
|
||||
fdout = mktemp(tmpname);
|
||||
#
|
||||
sprintf(tmpname, "%s/zzuf.$i.XXXXXX", tmpdir, GetCurrentProcessId());
|
||||
fdout = _open(mktemp(tmpname), _O_RDWR, 0600);
|
||||
#else
|
||||
sprintf(tmpname, "%s/zzuf.%i.XXXXXX", tmpdir, (int)getpid());
|
||||
fdout = mkstemp(tmpname);
|
||||
#endif
|
||||
if (fdout < 0)
|
||||
@@ -889,6 +893,29 @@ static void clean_children(struct opts *opts)
|
||||
else
|
||||
fprintf(stderr, "exit %i\n", WEXITSTATUS(status));
|
||||
}
|
||||
#elif defined _WIN32
|
||||
{
|
||||
DWORD exit_code;
|
||||
if (GetExitCodeProcess(opts->child[i].process_handle, &exit_code))
|
||||
{
|
||||
if (exit_code == STILL_ACTIVE) continue; /* The process is still active, we don't do anything */
|
||||
|
||||
/*
|
||||
* The main problem with GetExitCodeProcess is it returns either returned parameter value of
|
||||
* ExitProcess/TerminateProcess, or the unhandled exception (which is what we're looking for)
|
||||
*/
|
||||
switch (exit_code)
|
||||
{
|
||||
case EXCEPTION_ACCESS_VIOLATION: fprintf(stderr, "child(%d) unhandled exception: Access Violation", opts->child[i].pid); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->child[i].status != STATUS_RUNNING)
|
||||
{
|
||||
TerminateProcess(opts->child[i].process_handle, 0);
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* waitpid() is not available. Don't kill the process. */
|
||||
continue;
|
||||
@@ -985,10 +1012,15 @@ static void read_children(struct opts *opts)
|
||||
for(i = 0, j = 0; i < (size_t)opts->maxchild; i += (j == 2), j = (j + 1) % 3)
|
||||
{
|
||||
struct child_overlapped * co;
|
||||
HANDLE h;
|
||||
HANDLE h = (opts->child[i].fd[j] == -1) ? INVALID_HANDLE_VALUE : (HANDLE)_get_osfhandle(opts->child[i].fd[j]);
|
||||
|
||||
if(opts->child[i].status != STATUS_RUNNING)
|
||||
if(opts->child[i].status != STATUS_RUNNING
|
||||
|| opts->child[i].fd[j] == -1
|
||||
|| h == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
fd_number--;
|
||||
continue;
|
||||
}
|
||||
|
||||
co = malloc(sizeof(*co));
|
||||
ZeroMemory(co, sizeof(*co));
|
||||
@@ -997,15 +1029,24 @@ static void read_children(struct opts *opts)
|
||||
co->fd_no = j;
|
||||
co->opts = opts;
|
||||
|
||||
h = (HANDLE)_get_osfhandle(opts->child[i].fd[j]);
|
||||
if(!ReadFileEx(h, co->buf, sizeof(co->buf), (LPOVERLAPPED)co, read_child))
|
||||
{
|
||||
/* End of file reached */
|
||||
close(opts->child[i].fd[j]);
|
||||
opts->child[i].fd[j] = -1;
|
||||
|
||||
/* FIXME: handle error */
|
||||
ReadFileEx(h, co->buf, sizeof(co->buf), (LPOVERLAPPED)co, read_child);
|
||||
if(opts->child[i].fd[0] == -1
|
||||
&& opts->child[i].fd[1] == -1
|
||||
&& opts->child[i].fd[2] == -1)
|
||||
opts->child[i].status = STATUS_EOF;
|
||||
}
|
||||
cur_child_handle++;
|
||||
}
|
||||
|
||||
if (fd_number == 0) return;
|
||||
|
||||
/* FIXME: handle error */
|
||||
WaitForMultipleObjectsEx(fd_number, children_handle, FALSE, INFINITE, TRUE);
|
||||
WaitForMultipleObjectsEx(fd_number, children_handle, FALSE, 1000, TRUE);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
Reference in New Issue
Block a user