/** * windows dyamic hook util functions wrappers * v0.3.3, developed by devseed * * macros: * WINHOOK_IMPLEMENT, include defines of each function * WINHOOK_SHARED, make function export * WINHOOK_STATIC, make function static * WINHOOK_NOINLINE, don't use inline function * WINHOOK_NO3RDLIB, don't use 3rd lib for inlinehook * WINHOOK_USEDYNBIND, use dynamic binding for winapi api */ #ifndef _WINHOOK_H #define _WINHOOK_H #define WINHOOK_VERSION 330 #ifdef USECOMPAT #include "commdef_v100.h" #else #include "commdef.h" #endif // USECOMPAT // define specific macro #ifdef WINHOOK_API #undef WINHOOK_API #endif #ifdef WINHOOK_API_DEF #undef WINHOOK_API_DEF #endif #ifdef WINHOOK_API_EXPORT #undef WINHOOK_API_EXPORT #endif #ifdef WINHOOK_API_INLINE #undef WINHOOK_API_INLINE #endif #ifdef WINHOOK_STATIC #define WINHOOK_API_DEF static #else #define WINHOOK_API_DEF extern #endif // WINHOOK_STATIC #ifdef WINHOOK_SHARED #define WINHOOK_API_EXPORT EXPORT #else #define WINHOOK_API_EXPORT #endif // WINHOOK_SHARED #ifdef WINHOOK_NOINLINE #define WINHOOK_API_INLINE #else #define WINHOOK_API_INLINE INLINE #endif // WINHOOK_NOINLINE #define WINHOOK_API WINHOOK_API_DEF WINHOOK_API_EXPORT WINHOOK_API_INLINE #ifdef __cplusplus extern "C" { #endif #include /** * start a exe and inject dll into exe * @return pid */ WINHOOK_API DWORD winhook_startexeinject(LPCSTR exepath, LPSTR cmdstr, LPCSTR dllpath); /** * start a exe by CreateProcess * @return pid */ WINHOOK_API DWORD winhook_startexe(LPCSTR exepath, LPSTR cmdstr) { return winhook_startexeinject(exepath, cmdstr, NULL); } /** * get the process handle by exename */ WINHOOK_API HANDLE winhook_getprocess(LPCWSTR exename); /** * get the other process image base */ WINHOOK_API size_t winhook_getimagebase(HANDLE hprocess); /** * dynamic inject a dll into a process */ WINHOOK_API BOOL winhook_injectdll(HANDLE hprocess, LPCSTR dllname); /** * alloc a console for the program */ WINHOOK_API void winhook_installconsole(); /** * patch addr by buf with bufsize */ WINHOOK_API BOOL winhook_patchmemoryex(HANDLE hprocess,LPVOID addr, const void* buf, size_t bufsize); WINHOOK_API BOOL winhook_patchmemory(LPVOID addr, const void* buf, size_t bufsize) { return winhook_patchmemoryex(GetCurrentProcess(), addr, buf, bufsize); } /** * batch patch memories */ WINHOOK_API BOOL winhook_patchmemorysex(HANDLE hprocess, LPVOID addrs[], void* bufs[], size_t bufsizes[], int n); WINHOOK_API BOOL winhook_patchmemorys(LPVOID addrs[], void* bufs[], size_t bufsizes[], int n) { return winhook_patchmemorysex(GetCurrentProcess(), addrs, bufs, bufsizes, n); } /** * patch memory with pattern, * @param pattern * skip '#' line, + for reative address, then multi byte code (hex) * 00400000: ff 90 * +3f00: 90 90 90 90 * +3f06: 90; +3f08: 90 * @return patch bytes number, error < 0 */ WINHOOK_API int winhook_patchmemorypattern(const char *pattern); /** * patch memory with pattern 1337 by x64dbg, use rva * can use ';' instead of '\r' '\n' */ WINHOOK_API int winhook_patchmemory1337ex(HANDLE hprocess, const char* pattern, size_t base, BOOL revert); WINHOOK_API int winhook_patchmemory1337(const char* pattern, size_t base, BOOL revert) { return winhook_patchmemory1337ex(GetCurrentProcess(), pattern, base, revert); } /** * patch memory with pattern ips(International Patching System) * specifications at https://zerosoft.zophar.net/ips.php * addr is relative to base, big endian */ WINHOOK_API int winhook_patchmemoryipsex(HANDLE hprocess, const char* pattern, size_t base); WINHOOK_API int winhook_patchmemoryips(const char* pattern, size_t base) { return winhook_patchmemoryipsex(GetCurrentProcess(), pattern, base); } /** * search the pattern like "ab 12 ?? 34" * @return the matched address */ WINHOOK_API void* winhook_searchmemory(void* addr, size_t memsize, const char* pattern, size_t *pmatchsize); WINHOOK_API void* winhook_searchmemoryex(HANDLE hprocess, void* addr, size_t memsize, const char* pattern, size_t* pmatchsize); /** * winhook_iathookmodule is for windows dll, * @param moduleDllName is which dll to hook iat */ WINHOOK_API BOOL winhook_iathookpe(LPCSTR targetDllName, void* mempe, PROC pfnOrg, PROC pfnNew); WINHOOK_API BOOL winhook_iathookmodule(LPCSTR targetDllName, LPCSTR moduleDllName, PROC pfnOrg, PROC pfnNew) { return winhook_iathookpe(targetDllName, GetModuleHandle(moduleDllName), pfnOrg, pfnNew); } /** * iat dynamiclly hook, * replace the @param pfgNew with @param pfnOrg function * @param targetDllName like "user32.dll", "kernel32.dll" */ WINHOOK_API BOOL winhook_iathook(LPCSTR targetDllName, PROC pfnOrg, PROC pfgNew) { return winhook_iathookmodule(targetDllName, NULL, pfnOrg, pfgNew); } /** * inline hooks wrapper, * @param pfnTargets -> @param pfnNews, save origin pointers in @param pfnOlds * @return: success hook numbers */ WINHOOK_API int winhook_inlinehooks(PVOID pfnTargets[], PVOID pfnNews[], PVOID pfnOlds[], int n); WINHOOK_API int winhook_inlineunhooks(PVOID pfnTargets[], PVOID pfnNews[], PVOID pfnOlds[], int n); #ifdef WINHOOK_IMPLEMENTATION #include #include #include #include #include #include #ifdef WINHOOK_USEDYNBIND #ifndef WINDYN_IMPLEMENTATION #define WINDYN_IMPLEMENTATION #endif // WINDYN_IMPLEMENTATION #ifndef WINDYN_STATIC #define WINDYN_STATIC #endif // WINDYN_STATIC #ifdef USECOMPAT #include "windyn_v150.h" #else #include "windyn.h" #endif // USECOMPAT #define strlen inl_strlen #define _stricmp inl_stricmp #define _wcsicmp inl_wcsicmp #define GetModuleHandleA windyn_GetModuleHandleA #define LoadLibraryA windyn_LoadLibraryA #define GetProcAddress windyn_GetProcAddress #define VirtualAllocEx windyn_VirtualAllocEx #endif // WINHOOK_USEDYNBIND // loader functions DWORD winhook_startexeinject(LPCSTR exepath, LPSTR cmdstr, LPCSTR dllpath) { STARTUPINFOA si = {0}; PROCESS_INFORMATION pi = {0}; si.cb = sizeof(STARTUPINFOA); if (!CreateProcessA(exepath, cmdstr,NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) return 0; if (dllpath) // inject dll to process { size_t n = 0; HANDLE hprocess = pi.hProcess; HANDLE hthread = pi.hThread; LPVOID injectaddr = VirtualAllocEx(hprocess, 0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE); size_t oepva = 0; // prepare shellcode CONTEXT context = { 0 }; context.ContextFlags = CONTEXT_ALL; GetThreadContext(hthread, &context); #ifdef _WIN64 uint8_t injectcode[] = {0x50,0x53,0x51,0x52,0xe8,0x2d,0x00,0x00,0x00,0x48,0x8d,0x58,0xf7,0x48,0x83,0xec,0x28,0x48,0x8b,0x8b,0x43,0x00,0x00,0x00,0x48,0x8b,0x83,0x4b,0x00,0x00,0x00,0xff,0xd0,0x48,0x83,0xc4,0x28,0x48,0x8b,0x83,0x3b,0x00,0x00,0x00,0x49,0x89,0xc7,0x5a,0x59,0x5b,0x58,0x41,0xff,0xe7,0x48,0x8b,0x04,0x24,0xc3,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90 }; oepva = context.Rip; context.Rip = (ULONGLONG)injectaddr; #else uint8_t injectcode[] = {0x50,0x53,0xe8,0x1e,0x00,0x00,0x00,0x8d,0x58,0xf9,0x8b,0x83,0x2d,0x00,0x00,0x00,0x50,0x8b,0x83,0x31,0x00,0x00,0x00,0xff,0xd0,0x8b,0x83,0x29,0x00,0x00,0x00,0x89,0xc7,0x5b,0x58,0xff,0xe7,0x8b,0x04,0x24,0xc3,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90 }; oepva = context.Eip; // origin eip at RtlUserThreadStart context.Eip = (DWORD)injectaddr; #endif SetThreadContext(hthread, &context); char name_kernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', '\0'}; HMODULE kernel32 = GetModuleHandleA(name_kernel32); char name_LoadLibraryA[] = { 'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'A', '\0' }; FARPROC pfnLoadlibraryA = GetProcAddress(kernel32, name_LoadLibraryA); size_t* pretva = (size_t*)(injectcode + sizeof(injectcode) - 3 * sizeof(size_t)); size_t *pdllnameva = (size_t*)(injectcode + sizeof(injectcode) - 2 * sizeof(size_t)); size_t* ploadlibraryva = (size_t*)(injectcode + sizeof(injectcode) - 1 * sizeof(size_t)); *pretva = (size_t)oepva; *pdllnameva = (size_t)((size_t)injectaddr + sizeof(injectcode)); *ploadlibraryva = (size_t)pfnLoadlibraryA; uint8_t* addr = (uint8_t*)injectaddr; WriteProcessMemory(hprocess, addr, injectcode, sizeof(injectcode), (SIZE_T*)&n); // copy shellcode addr += sizeof(injectcode); WriteProcessMemory(hprocess, addr, dllpath, strlen(dllpath) + 1, (SIZE_T*)&n); // copy dll name } ResumeThread(pi.hThread); CloseHandle(pi.hThread); return pi.dwProcessId; } HANDLE winhook_getprocess(LPCWSTR exename) { // Create toolhelp snapshot. DWORD pid = 0; HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); PROCESSENTRY32 process; ZeroMemory(&process, sizeof(process)); process.dwSize = sizeof(process); // Walkthrough all processes. if (Process32First(snapshot, &process)) { do { if (_wcsicmp((const wchar_t*)process.szExeFile, exename) == 0) { pid = process.th32ProcessID; break; } } while (Process32Next(snapshot, &process)); } CloseHandle(snapshot); if (pid != 0) return OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); return NULL; // Not found } size_t winhook_getimagebase(HANDLE hprocess) { //if (hprocess == GetCurrentProcess()) return (size_t)GetModuleHandleA(NULL); HMODULE modules[1024]; // Array that receives the list of module handles DWORD nmodules = 0; char modulename[MAX_PATH] = {0}; if (!EnumProcessModules(hprocess, modules, sizeof(modules), &nmodules)) return 0; // impossible to read modules if (!GetModuleFileNameExA(hprocess, modules[0], modulename, sizeof(modulename))) return 0; // impossible to get module info return (size_t)modules[0]; // module 0 is apparently always the EXE itself } BOOL winhook_injectdll(HANDLE hprocess, LPCSTR dllname) { LPVOID addr = VirtualAllocEx(hprocess, 0, 0x100, MEM_COMMIT, PAGE_READWRITE); SIZE_T count; if (addr == NULL) return FALSE; WriteProcessMemory(hprocess, addr, dllname, strlen(dllname)+1, (SIZE_T*)&count); char name_kernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', '\0' }; HMODULE kernel32 = GetModuleHandleA(name_kernel32); char name_LoadLibraryA[] = { 'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'A', '\0' }; FARPROC pfnLoadlibraryA = GetProcAddress(kernel32, name_LoadLibraryA); HANDLE hthread = CreateRemoteThread(hprocess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnLoadlibraryA, addr, 0, NULL); if (hthread == NULL) return FALSE; WaitForSingleObject(hthread, -1); VirtualFreeEx(hprocess, addr, 0x100, MEM_COMMIT); return TRUE; } void winhook_installconsole() { AllocConsole(); freopen("CONOUT$", "w", stdout); } // dynamic hook functions BOOL winhook_patchmemoryex(HANDLE hprocess, LPVOID addr, const void* buf, size_t bufsize) { if (addr == NULL || buf == NULL) return FALSE; DWORD oldprotect; BOOL ret = VirtualProtectEx(hprocess, addr, bufsize, PAGE_EXECUTE_READWRITE, &oldprotect); if (ret) { size_t n = 0; WriteProcessMemory(hprocess, addr, buf, bufsize, (SIZE_T*)&n); VirtualProtectEx(hprocess, addr, bufsize, oldprotect, &oldprotect); } return ret; } BOOL winhook_patchmemorysex(HANDLE hprocess, LPVOID addrs[], void* bufs[], size_t bufsizes[], int n) { int ret = 0; for (int i = 0; i < n; i++) { ret += winhook_patchmemoryex(hprocess, addrs[i], bufs[i], bufsizes[i]); } return ret; } int winhook_patchmemorypattern(const char *pattern) { if (!pattern) return -1; size_t imagebase = (size_t)GetModuleHandleA(NULL); int res = 0; int flag_rel = 0; int j = 0; while (pattern[j]) j++; int patternlen = j; DWORD oldprotect; for(int i=0; i='0' && c<='9') c -= '0'; else if (c>='A' && c<='Z') c = c -'A' + 10; else if (c>='a' && c<='z') c = c -'a' + 10; else if (c=='\r' || c=='\n') {flag_nextline=1;break;} else if (c==' ') continue; else return -2; addr = (addr<<4) + c; } if(flag_nextline) continue; if(flag_rel) addr += imagebase; int n = 0; int v = 0; int start = i++; for(int j=0;j<2;j++) { n = 0; for(;pattern[i]!='\n' && i='0' && c<='9') c -= '0'; else if (c>='A' && c<='Z') c = c - 'A' + 10; else if (c>='a' && c<='z') c = c - 'a' + 10; else if (c==';') break; else continue; n++; if (j != 0) { v = (v << 4) + c; if (!(n & 1)) { *(uint8_t*)(addr + (n>>1) -1) = v; v = 0; res++; } } } if(n&1) return -3; if (j == 0) { i = start; VirtualProtect((void*)addr, n>>1, PAGE_EXECUTE_READWRITE, &oldprotect); } else VirtualProtect((void*)addr, n>>1, oldprotect, &oldprotect); } flag_rel = 0; } return res; } int winhook_patchmemory1337ex(HANDLE hprocess, const char* pattern, size_t base, BOOL revert) { #define IS_ENDLINE(c) (c==';' || c=='\r' || c=='\n') enum FLAG1337 { RVA1337, OLDBYTE1337, NEWBYTE1337 } flag1337 = RVA1337; if (hprocess == NULL) return -1; int res = 0; int i = 0; while (pattern[i]) i++; int patternlen = i; i = 0; while (pattern[i] != '>') i++; // title line while (!IS_ENDLINE(pattern[i])) i++; while (IS_ENDLINE(pattern[i])) i++; size_t rva = 0; uint8_t oldbyte = 0, newbyte = 0; for (; i < patternlen; i++) { char c = pattern[i]; if (c == ':') // oldbyte indicator { flag1337 = OLDBYTE1337; } else if (c == '-') // newbyte indicator { if (pattern[i + 1] != '>') return -1; flag1337 = NEWBYTE1337; i++; } else if (IS_ENDLINE(c)) // flush patch { if (flag1337 == RVA1337) continue; uint8_t* patchbyte = revert ? &oldbyte : &newbyte; winhook_patchmemoryex(hprocess, (LPVOID)(base + rva), patchbyte, 1); flag1337 = RVA1337; rva = 0; oldbyte = 0; newbyte = 0; res++; } else if (c == ' ') { continue; } else { if (c >= '0' && c <= '9') c -= '0'; else if (c >= 'A' && c <= 'Z') c = c - 'A' + 10; else if (c >= 'a' && c <= 'z') c = c - 'a' + 10; else continue; switch (flag1337) { case RVA1337: rva = (rva << 4) | (uint8_t)c; break; case OLDBYTE1337: oldbyte = (oldbyte << 4) | (uint8_t)c; break; case NEWBYTE1337: newbyte = (newbyte << 4) | (uint8_t)c; break; } } } return res; } int winhook_patchmemoryipsex(HANDLE hprocess, const char* pattern, size_t base) { #define BYTE3_TO_UINT_BIGENDIAN(bp) \ (((unsigned int)(bp)[0] << 16) & 0x00FF0000) | \ (((unsigned int)(bp)[1] << 8) & 0x0000FF00) | \ ((unsigned int)(bp)[2] & 0x000000FF) #define BYTE2_TO_UINT_BIGENDIAN(bp) \ (((unsigned int)(bp)[0] << 8) & 0xFF00) | \ ((unsigned int) (bp)[1] & 0x00FF) if(strncmp(pattern, "PATCH", 5) !=0 ) return -1; int res = 0; const uint8_t* p = (uint8_t*)pattern + 5; while (strncmp((char*)p, "EOF", 3) != 0) { unsigned int offset = BYTE3_TO_UINT_BIGENDIAN(p); unsigned int size = BYTE2_TO_UINT_BIGENDIAN(p + 3); p += 5; if (size == 0) // use RLE compress { unsigned int size_rle = BYTE2_TO_UINT_BIGENDIAN(p); return -2; // not implemented yet } else { size_t addr = base + offset; winhook_patchmemoryex(hprocess, (LPVOID)addr, p, size); p += size; res += size; } } return res; } void* winhook_searchmemory(void* addr, size_t memsize, const char* pattern, size_t* pmatchsize) { size_t i = 0; int matchend = 0; void* matchaddr = NULL; while (i < memsize) { int j = 0; int matchflag = 1; matchend = 0; while (pattern[j]) { if (pattern[j] == 0x20) { j++; continue; } char _c1 = (((char*)addr)[i+matchend]>>4) & 0x0f; _c1 = _c1 < 10 ? _c1 + '0' : (_c1 - 10) + 'A'; char _c2 = (((char*)addr)[i+matchend]&0xf) & 0x0f; _c2 = _c2 < 10 ? _c2 + '0' : (_c2 - 10) + 'A'; if (pattern[j] != '?') { if (_c1 != pattern[j] && _c1 + 0x20 != pattern[j]) { matchflag = 0; break; } } if (pattern[j + 1] != '?') { if (_c2 != pattern[j+1] && _c2 + 0x20 != pattern[j+1]) { matchflag = 0; break; } } j += 2; matchend++; } if (matchflag) { matchaddr = (void*)((uint8_t*)addr + i); break; } i++; } if (pmatchsize) *pmatchsize = matchend; return matchaddr; } void* winhook_searchmemoryex(HANDLE hprocess, void* addr, size_t memsize, const char* pattern, size_t* pmatchsize) { void* buf = VirtualAlloc(NULL, memsize, MEM_COMMIT, PAGE_READWRITE); size_t bufsize = 0; ReadProcessMemory(hprocess, addr, buf, memsize, (SIZE_T*)&bufsize); void* matchaddr = winhook_searchmemory(buf, memsize, pattern, pmatchsize); VirtualFree(buf, 0, MEM_RELEASE); if (!matchaddr) return matchaddr; size_t offset = (size_t)matchaddr - (size_t)buf; return (void*)((uint8_t*)addr + offset); } BOOL winhook_iathookpe(LPCSTR targetDllName, void* mempe, PROC pfnOrg, PROC pfnNew) { size_t imagebase = (size_t)mempe; PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)imagebase; PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS) ((uint8_t*)imagebase + pDosHeader->e_lfanew); PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader; PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader; PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory; PIMAGE_DATA_DIRECTORY pImpEntry = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; PIMAGE_IMPORT_DESCRIPTOR pImpDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(imagebase + pImpEntry->VirtualAddress); DWORD dwOldProtect = 0; for (; pImpDescriptor->Name; pImpDescriptor++) { // find the dll IMPORT_DESCRIPTOR LPCSTR pDllName = (LPCSTR)(imagebase + pImpDescriptor->Name); if (!_stricmp(pDllName, targetDllName)) // ignore case { PIMAGE_THUNK_DATA pFirstThunk = (PIMAGE_THUNK_DATA)(imagebase + pImpDescriptor->FirstThunk); // find the iat function va for (; pFirstThunk->u1.Function; pFirstThunk++) { if (pFirstThunk->u1.Function == (size_t)pfnOrg) { VirtualProtect((LPVOID)&pFirstThunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect); pFirstThunk->u1.Function = (size_t)pfnNew; VirtualProtect((LPVOID)&pFirstThunk->u1.Function, 4, dwOldProtect, &dwOldProtect); return TRUE; } } } } return FALSE; } #ifndef WINHOOK_NO3RDLIB #ifndef MINHOOK_IMPLEMENTATION #define MINHOOK_IMPLEMENTATION #define MINHOOK_STATIC #endif // MINHOOK_IMPLEMENTATION #ifdef USECOMPAT #include "stb_minhook_v1331.h" #else #include "stb_minhook.h" #endif int winhook_inlinehooks(PVOID pfnTargets[], PVOID pfnNews[], PVOID pfnOlds[], int n) { int i; MH_Initialize(); for(i=0; i