From b77afc7583ac41988856939b139deb077415967e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Szkud=C5=82apski?= Date: Mon, 20 Aug 2012 12:27:26 +0000 Subject: [PATCH] add relocate_hook to improve api hooking, fix dll name string comparison (no case sensitive), fix used after free on win32, add more hooks related to async file access --- msvc/config.h | 3 ++ src/libzzuf/lib-win32.c | 67 +++++++++++++++++++++++++++++++++++-- src/libzzuf/libzzuf.c | 2 +- src/libzzuf/sys.c | 73 ++++++++++++++++++++++++++++++++--------- src/myfork.c | 4 +-- 5 files changed, 128 insertions(+), 21 deletions(-) diff --git a/msvc/config.h b/msvc/config.h index 5a0f514..4e89397 100644 --- a/msvc/config.h +++ b/msvc/config.h @@ -53,6 +53,9 @@ /* #undef HAVE_GETC_UNLOCKED */ /* #undef HAVE_GETDELIM */ /* #undef HAVE_GETLINE */ +#define HAVE_CREATEIOCOMPLETIONPORT 1 +#define HAVE_GETQUEUEDCOMPLETIONSTATUS 1 +#define HAVE_GETOVERLAPPEDRESULT 1 #define HAVE_GETPAGESIZE 1 /* #undef HAVE_GETTIMEOFDAY */ /* #undef HAVE_GLIBC_FILE */ diff --git a/src/libzzuf/lib-win32.c b/src/libzzuf/lib-win32.c index 001f07c..ec15424 100644 --- a/src/libzzuf/lib-win32.c +++ b/src/libzzuf/lib-win32.c @@ -61,6 +61,15 @@ static BOOL (__stdcall *ORIG(ReadFile))(HANDLE, LPVOID, DWORD, LPDWORD, static BOOL (__stdcall *ORIG(ReadFileEx))(HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE); #endif +#if defined HAVE_CREATEIOCOMPLETIONPORT +static HANDLE (__stdcall *ORIG(CreateIoCompletionPort))(HANDLE, HANDLE, ULONG_PTR, DWORD); +#endif +#if defined HAVE_GETQUEUEDCOMPLETIONSTATUS +static BOOL (__stdcall *ORIG(GetQueuedCompletionStatus))(HANDLE, LPDWORD, PULONG_PTR, LPOVERLAPPED *, DWORD); +#endif +#if defined HAVE_GETOVERLAPPEDRESULT +static BOOL (__stdcall *ORIG(GetOverlappedResult))(HANDLE, LPOVERLAPPED, LPDWORD, BOOL); +#endif #if defined HAVE_CREATEFILEMAPPINGA static HANDLE (__stdcall *ORIG(CreateFileMappingA))(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCSTR); @@ -200,6 +209,57 @@ BOOL __stdcall NEW(ReadFileEx)(HANDLE hFile, LPVOID lpBuffer, } #endif +#if defined HAVE_CREATEIOCOMPLETIONPORT +HANDLE __stdcall NEW(CreateIoCompletionPort)(HANDLE FileHandle, HANDLE ExistingCompletionPort, ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads) +{ + HANDLE ret; + + ret = ORIG(CreateIoCompletionPort)(FileHandle, ExistingCompletionPort, CompletionKey, NumberOfConcurrentThreads); + + debug("GetQueuedCompletionStatus(0x%08x, 0x%08x, 0x%08x, %d) = 0x%08x", + FileHandle, ExistingCompletionPort, CompletionKey, NumberOfConcurrentThreads, ret); + + if (!_zz_ready || !_zz_iswatched(FileHandle) /*|| !_zz_hostwatched(hFile)*/ || _zz_islocked(FileHandle) || !_zz_isactive(FileHandle)) + return ret; + + if (ret != NULL) + { + debug("handle %#08x is registered", ret); + _zz_register(ret); + } + + return ret; +} +#endif + +#if defined HAVE_GETQUEUEDCOMPLETIONSTATUS +BOOL __stdcall NEW(GetQueuedCompletionStatus)(HANDLE CompletionPort, LPDWORD lpNumberOfBytes, PULONG_PTR lpCompletion, LPOVERLAPPED *lpOverlapped, DWORD dwMilliseconds) +{ + BOOL ret; + + ret = ORIG(GetQueuedCompletionStatus)(CompletionPort, lpNumberOfBytes, lpCompletion, lpOverlapped, dwMilliseconds); + + debug("GetQueuedCompletionStatus(0x%08x, { %d }, %p, %p, %d) = %s", + CompletionPort, *lpNumberOfBytes, lpCompletion, lpOverlapped, dwMilliseconds, (ret ? "TRUE" : "FALSE")); + + return ret; +} +#endif + +#if defined HAVE_GETOVERLAPPEDRESULT +BOOL __stdcall NEW(GetOverlappedResult)(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, BOOL bWait) +{ + BOOL ret; + + ret = ORIG(GetOverlappedResult)(hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait); + + debug("GetOverlappedResult(0x%#08x, %p, %p, %s) = %s", + hFile, lpOverlapped, lpNumberOfBytesTransferred, (bWait ? "TRUE" : "FALSE"), (ret ? "TRUE" : "FALSE")); + + return ret; +} +#endif + #if defined HAVE_CREATEFILEMAPPINGA HANDLE __stdcall NEW(CreateFileMappingA)(HANDLE hFile, LPSECURITY_ATTRIBUTES lpAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, @@ -298,11 +358,14 @@ zzuf_table_t table_win32[] = DIVERT(CloseHandle), DIVERT(CreateFileA), DIVERT(CreateFileW), + DIVERT(ReadFile), + DIVERT(ReadFileEx), + DIVERT(CreateIoCompletionPort), + DIVERT(GetQueuedCompletionStatus), + DIVERT(GetOverlappedResult), DIVERT(CreateFileMappingA), DIVERT(CreateFileMappingW), DIVERT(MapViewOfFile), - DIVERT(ReadFile), - DIVERT(ReadFileEx), DIVERT_END }; #endif diff --git a/src/libzzuf/libzzuf.c b/src/libzzuf/libzzuf.c index 07377d2..186075c 100644 --- a/src/libzzuf/libzzuf.c +++ b/src/libzzuf/libzzuf.c @@ -233,7 +233,7 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, PVOID impLoad) _zz_init(); break; case DLL_PROCESS_DETACH: - _zz_fini(); + //_zz_fini(); DeleteCriticalSection(&_zz_pipe_cs); break; } diff --git a/src/libzzuf/sys.c b/src/libzzuf/sys.c index f5acf59..4d30cc9 100644 --- a/src/libzzuf/sys.c +++ b/src/libzzuf/sys.c @@ -105,12 +105,11 @@ static int modrm_sib_size(uint8_t* code) if (modrm == 0x05) /* [(rip) + sdword] */ return 1 + 4; - /* Does the instruciton have a SIB byte ? */ + /* Does this instruction have a SIB byte ? */ return 1 + (!!(((modrm & 0x7) == 0x4) && ((modrm >> 6) != 0x3))) + modrm_size[modrm >> 6]; } -/* zz_lde is a _very_ simple length disassemble engine. - * x64 is not tested and should not work. */ +/* zz_lde is a _very_ simple length disassemble engine. */ static int zz_lde(uint8_t *code) { int insn_size = 0; @@ -130,8 +129,9 @@ static int zz_lde(uint8_t *code) /* Simple instructions should be placed here */ switch (opcd) { - case 0x68: insn_size += 4; break; /* PUSH Iv */ - case 0x6a: insn_size += 1; break; /* PUSH Ib */ + case 0x68: return (insn_size + 4); /* PUSH Iv */ + case 0x6a: return (insn_size + 1); /* PUSH Ib */ + case 0x90: return insn_size; /* NOP */ default: break; } @@ -143,28 +143,25 @@ static int zz_lde(uint8_t *code) { case 0x89: /* mov Ev, Gv */ case 0x8b: /* mov Gv, Ev */ - insn_size += modrm_sib_size(code + insn_size); - break; + return (insn_size + modrm_sib_size(code + insn_size)); case 0x80: /* Group#1 Eb, Ib */ case 0x82: /* Group#1 Eb, Ib */ case 0x83: /* Group#1 Ev, Ib */ - insn_size += (modrm_sib_size(code + insn_size) + 1); - break; + return (insn_size + (modrm_sib_size(code + insn_size) + 1)); case 0x81: /* Group#1 Ev, Iz */ - insn_size += (modrm_sib_size(code + insn_size) + 4); - break; + return (insn_size + (modrm_sib_size(code + insn_size) + 4)); case 0xff: if ((code[insn_size] & 0x38) == 0x30) /* PUSH Ev */ - insn_size += modrm_sib_size(code + insn_size); + return (insn_size + modrm_sib_size(code + insn_size)); break; default: break; } - return insn_size; + return 0; } /* This function returns the required size to insert a patch */ @@ -260,6 +257,46 @@ static int make_trampoline(uint8_t *code, size_t patch_size, uint8_t **trampolin #endif } +/* + * Sometimes Windows APIs are a stub and contain only a JMP to the real function. + * To avoid to relocate a JMP, we use the destination address. + */ +static int relocate_hook(uint8_t **code) +{ + uint8_t *cur_code = *code; + +#ifdef _M_AMD64 + // we ignore the REX prefix + if ((*cur_code & 0xf8) == 0x48) + ++cur_code; +#endif + + /* JMP Jd */ + if (*cur_code == 0xe9) + { + *cur_code += (5 + *(uint32_t *)(cur_code + 1)); + return 0; + } + + /* JMP [(rip)+addr] */ + else if (!memcmp(cur_code, "\xff\x25", 2)) + { +#ifdef _M_AMD64 + uint8_t **dst_addr = (uint8_t **)(cur_code + 6 + *(uint32_t *)(cur_code + 2)); + *code = *dst_addr; +#elif _M_IX86 + /* UNTESTED ! */ + uint8_t **dst_addr = *(uint32_t *)(*cur_code + 2); + *code = *dst_addr; +#else +# error Unsupported architecture ! +#endif + return 0; + } + + return -1; +} + /* This function allows to hook any API. To do so, it disassembles the beginning of the * targeted function and looks for, at least, 5 bytes (size of JMP Jd). * Then it writes a JMP Jv instruction to make the new_api executed. @@ -273,6 +310,10 @@ static int hook_inline(uint8_t *old_api, uint8_t *new_api, uint8_t **trampoline_ uint8_t *trampoline = NULL; size_t trampoline_size = 0; DWORD old_prot; + uint8_t *reloc_old_api = old_api; + + while ((relocate_hook(&reloc_old_api)) >= 0) + old_api = reloc_old_api; *trampoline_api = NULL; @@ -344,18 +385,18 @@ static void insert_funcs(void) if ((lib = LoadLibraryA(diversion->lib)) == NULL) { fprintf(stderr, "unable to load %s\n", diversion->lib); - return; + continue; } } if ((old_api = (uint8_t *)GetProcAddress(lib, diversion->name)) == NULL) { fprintf(stderr, "unable to get pointer to %s\n", diversion->name); - return; + continue; } if (hook_inline(old_api, diversion->new, &trampoline_api) < 0) { fprintf(stderr, "hook_inline failed while hooking %s!%s\n", diversion->lib, diversion->name); - return; + continue; } *diversion->old = trampoline_api; } diff --git a/src/myfork.c b/src/myfork.c index df126e6..509805d 100644 --- a/src/myfork.c +++ b/src/myfork.c @@ -485,7 +485,7 @@ static int dll_inject(PROCESS_INFORMATION *pinfo, char const *lib) if (SuspendThread(thread) == (DWORD)-1) goto _return; /* Resolve LoadLibraryA from the target process memory context */ - rldlib = get_proc_address(process, pid, "LoadLibraryA"); + if ((rldlib = get_proc_address(process, pid, "LoadLibraryA")) == NULL) goto _return; if ((rpl = VirtualAllocEx(process, NULL, pl_len, MEM_COMMIT, PAGE_EXECUTE_READWRITE)) == NULL) goto _return; @@ -544,7 +544,7 @@ static void *get_proc_address(void *process, DWORD pid, const char *func) uint32_t exportaddr; uint8_t *base = entry.modBaseAddr; - if (strcmp("kernel32.dll", entry.szModule)) + if (stricmp("kernel32.dll", entry.szModule)) continue; ReadProcessMemory(process, base, &dos, sizeof(dos), &tmp);