fix tmp file creation on win32, start to implement handling of win32 exception with GetExitCodeProcess

This commit is contained in:
Kévin Szkudłapski
2012-07-11 15:31:23 +00:00
committed by wisk
parent 8349e4746d
commit bc1087d8b0
3 changed files with 509 additions and 458 deletions
+453 -450
View File
@@ -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
+7
View File
@@ -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
View File
@@ -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