我正在尝试挂钩Windows API函数FindWindowA().我成功地使用下面的代码完成了它而没有"hotpatching"它:我已经覆盖了函数开头的字节.调用myHook()并在调用FindWindowA()时显示一个消息框.
user32.dll启用了hotpatching,我想在实际函数之前覆盖NOP,而不是覆盖函数本身.但是,当我将hotpatching设置为TRUE时,下面的代码将不起作用.FindWindowA()执行时它什么都不做.
#include#include void myHook() { MessageBoxA(NULL, "Hooked", "Hook", MB_ICONINFORMATION); } int main(int argc, char *argv[]) { BOOLEAN hotpatching = FALSE; LPVOID fwAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "FindWindowA"); LPVOID fwHotpatchingAddress = (LPVOID)((DWORD)fwAddress - 5); LPVOID myHookAddress = &myHook; DWORD jmpOffset = (DWORD)&myHook - (DWORD)(!hotpatching ? fwAddress : fwHotpatchingAddress) - 5; // -5 because "JMP offset" = 5 bytes (1 + 4) printf("fwAddress: %X\n", fwAddress); printf("fwHotpatchingAddress: %X\n", fwHotpatchingAddress); printf("myHookAddress: %X\n", myHookAddress); printf("jmpOffset: %X\n", jmpOffset); printf("Ready?\n\n"); getchar(); char JMP[1] = {0xE9}; char RETN[1] = {0xC3}; LPVOID offset0 = NULL; LPVOID offset1 = NULL; LPVOID offset2 = NULL; if (!hotpatching) offset0 = fwAddress; else offset0 = fwHotpatchingAddress; offset1 = (LPVOID)((DWORD)offset0 + 1); offset2 = (LPVOID)((DWORD)offset1 + 4); DWORD oldProtect = 0; VirtualProtect(offset0, 6, PAGE_EXECUTE_READWRITE, &oldProtect); memcpy(fwAddress, JMP, 1); memcpy(offset1, &jmpOffset, 4); memcpy(offset2, RETN, 1); VirtualProtect(offset0, 6, oldProtect, &oldProtect); printf("FindWindowA() Patched"); getchar(); FindWindowA(NULL, "Test"); getchar(); return 0; }
你能告诉我什么是错的吗?
谢谢.
编译器和链接器准备了支持Hotpatching的可执行映像,以允许在使用时替换映像.应用以下两个更改(x86):
函数入口点设置为2字节无操作mov edi, edi
(/ hotpatch).
每个函数入口点(/ FUNCTIONPADMIN)前面有五个连续的nop .
为了说明这一点,这里是一个支持hotpaching功能的典型反汇编列表:
(2) 768C8D66 90 nop 768C8D67 90 nop 768C8D68 90 nop 768C8D69 90 nop 768C8D6A 90 nop (1) 768C8D6B 8B FF mov edi,edi (3) 768C8D6D 55 push ebp 768C8D6E 8B EC mov ebp,esp
(1)
使用2字节无操作指定函数入口点.(2)
是链接器提供的填充,(3)
是非平凡函数实现开始的地方.
要挂钩函数,您必须(2)
通过跳转到钩子函数来覆盖jmp myHook
,并通过替换(1)
相对跳转来使该代码可达jmp $-5
.
钩子函数必须使堆栈处于一致状态.它应该被声明为__declspec(naked)
阻止编译器生成函数prolog和epilog代码.最后一条指令必须按照钩子函数的调用约定执行堆栈清理,或者跳转到指定地址的钩子函数(3)
.