WinAPI钩子Trampoline函数异常求助:Hook生效但MessageBox不显示
问题:Hook MessageBoxA后无法弹出消息框
我尝试通过Trampoline函数Hook WinAPI的MessageBoxA,用包含jmp指令的补丁跳转到Trampoline函数,Trampoline中保留了原API的初始字节,之后通过jmp跳回原函数补丁后的位置。运行程序时能输出Hooked MessageBoxA called!,说明Hook已生效,但无法看到消息框。
原代码
#include <windows.h> #include <iostream> struct HookedFunction { FARPROC functionOriginalAddress; BYTE functionOriginalBytes[12]; SIZE_T bytesRead; SIZE_T bytesWritten; int modifyTimes; }; HookedFunction* MessageBoxAStruct; LPVOID trampolineAddress; // Define the hooked MessageBoxA function int WINAPI HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { // Custom behavior std::cout << "Hooked MessageBoxA called!" << std::endl; // Call the original MessageBoxA using the trampoline auto trampoline = (decltype(&MessageBoxA))trampolineAddress; return trampoline(hWnd, lpText, lpCaption, uType); } void HookMessageBoxA() { printf("hooking...\n"); MessageBoxAStruct = new HookedFunction; MessageBoxAStruct->bytesRead = 0; MessageBoxAStruct->bytesWritten = 0; // Get the handle to user32.dll (if it's already loaded) HMODULE library = GetModuleHandle(L"user32.dll"); if (library == NULL) { std::cout << "Handle error - couldn't get the address: " << GetLastError() << std::endl; return; } if (MessageBoxAStruct->modifyTimes != 1) { printf("hooking...\n"); FARPROC MessageBoxAAddress = GetProcAddress(library, "MessageBoxA"); // Check if the address is valid if (MessageBoxAAddress == NULL) { std::cout << "Failed to get address of MessageBoxA: " << GetLastError() << std::endl; return; } MessageBoxAStruct->functionOriginalAddress = MessageBoxAAddress; // Save the first 12 bytes of the original MessageBoxA function - will need for unhooking if (!ReadProcessMemory(GetCurrentProcess(), MessageBoxAAddress, MessageBoxAStruct->functionOriginalBytes, sizeof(MessageBoxAStruct->functionOriginalBytes), &MessageBoxAStruct->bytesRead)) { std::cout << "Failed to read original bytes of MessageBoxA: " << GetLastError() << std::endl; return; } MessageBoxAStruct->modifyTimes = 1; // Allocate memory for the trampoline function trampolineAddress = VirtualAlloc(NULL, sizeof(MessageBoxAStruct->functionOriginalBytes) + 14, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (trampolineAddress == NULL) { std::cout << "Failed to allocate memory for the trampoline: " << GetLastError() << std::endl; return; } // Copy original bytes to the trampoline if (!WriteProcessMemory(GetCurrentProcess(), trampolineAddress, MessageBoxAStruct->functionOriginalBytes, sizeof(MessageBoxAStruct->functionOriginalBytes), &MessageBoxAStruct->bytesWritten)) { std::cout << "Failed to write trampoline: " << GetLastError() << std::endl; return; } // Add a jump from the trampoline back to the original function's next instruction DWORD64 jumpBackAddress = (DWORD64)MessageBoxAAddress + sizeof(MessageBoxAStruct->functionOriginalBytes); unsigned char jumpBackPatch[] = { 0x48, 0xb8, // mov rax, 0x00, 0x00, 0x00, 0x00, // <address to jump back to> 0x00, 0x00, 0x00, 0x00, // <address to jump back to> 0xFF, 0xE0 // jmp rax }; *(DWORD64*)&jumpBackPatch[2] = jumpBackAddress; if (!WriteProcessMemory(GetCurrentProcess(), (LPVOID)((DWORD64)trampolineAddress + sizeof(MessageBoxAStruct->functionOriginalBytes)), jumpBackPatch, sizeof(jumpBackPatch), &MessageBoxAStruct->bytesWritten)) { std::cout << "Failed to write jump back in trampoline: " << GetLastError() << std::endl; return; } } // Create a patch "mov rax, <address of new MessageBoxA>;; jmp rax" unsigned char patch[] = { 0x48, 0xb8, // mov rax, 0x00, 0x00, 0x00, 0x00, // <address of hook> 0x00, 0x00, 0x00, 0x00, // <address of hook> 0xFF, 0xE0 // jmp rax }; *(DWORD64*)&patch[2] = (DWORD64)HookedMessageBoxA; // Change memory protection to allow writing DWORD oldProtect; if (!VirtualProtect(MessageBoxAStruct->functionOriginalAddress, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProtect)) { std::cout << "Failed to change memory protection: " << GetLastError() << std::endl; return; } if (!WriteProcessMemory(GetCurrentProcess(), (LPVOID)MessageBoxAStruct->functionOriginalAddress, patch, sizeof(patch), &MessageBoxAStruct->bytesWritten)) { std::cout << "Failed to write patch: " << GetLastError() << std::endl; return; } // Restore original memory protection if (!VirtualProtect(MessageBoxAStruct->functionOriginalAddress, sizeof(patch), oldProtect, &oldProtect)) { std::cout << "Failed to restore memory protection: " << GetLastError() << std::endl; return; } printf("Hooked successfully\n"); std::cout << "finished hooking...\n\n" << std::endl; } int main() { HookMessageBoxA(); // Example call to the hooked function MessageBoxA(NULL, "Test", "Test", MB_OK); return 0; }
问题原因
- 指令截断错误:固定截取12字节原函数指令,但
MessageBoxA的前12字节可能包含不完整的x64指令。x64汇编指令长度不固定,截断指令会导致Trampoline执行时逻辑错误,原函数后续流程崩溃,无法弹出消息框。 - 跳转地址计算错误:跳转回原函数的地址是用原地址加12字节计算的,但如果前12字节包含的指令总长度不等于12,这个地址会指向错误位置,破坏原函数执行流程。
- Trampoline内存分配大小错误:跳转回原函数的补丁实际是12字节,但代码中按
12+14字节分配内存,虽不直接影响功能,但逻辑上存在冗余。
修复方案
1. 确保截取完整指令序列
不能固定截取字节数,需保证截取的是完整的指令序列。x64下MessageBoxA的初始指令总长度通常超过12字节,这里改为截取14字节以覆盖完整初始指令。
2. 修正跳转地址与内存分配
根据实际截取的指令长度计算跳转回原函数的地址,同时修正Trampoline的内存分配大小。
修复后的代码示例
#include <windows.h> #include <iostream> struct HookedFunction { FARPROC functionOriginalAddress; // 改为截取14字节,确保包含完整初始指令 BYTE functionOriginalBytes[14]; SIZE_T bytesRead; SIZE_T bytesWritten; int modifyTimes; }; HookedFunction* MessageBoxAStruct; LPVOID trampolineAddress; int WINAPI HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) { std::cout << "Hooked MessageBoxA called!" << std::endl; auto trampoline = (decltype(&MessageBoxA))trampolineAddress; return trampoline(hWnd, lpText, lpCaption, uType); } void HookMessageBoxA() { printf("hooking...\n"); MessageBoxAStruct = new HookedFunction; MessageBoxAStruct->bytesRead = 0; MessageBoxAStruct->bytesWritten = 0; MessageBoxAStruct->modifyTimes = 0; HMODULE library = GetModuleHandle(L"user32.dll"); if (library == NULL) { std::cout << "Handle error - couldn't get the address: " << GetLastError() << std::endl; return; } if (MessageBoxAStruct->modifyTimes != 1) { FARPROC MessageBoxAAddress = GetProcAddress(library, "MessageBoxA"); if (MessageBoxAAddress == NULL) { std::cout << "Failed to get address of MessageBoxA: " << GetLastError() << std::endl; return; } MessageBoxAStruct->functionOriginalAddress = MessageBoxAAddress; // 读取前14字节原指令 if (!ReadProcessMemory(GetCurrentProcess(), MessageBoxAAddress, MessageBoxAStruct->functionOriginalBytes, sizeof(MessageBoxAStruct->functionOriginalBytes), &MessageBoxAStruct->bytesRead)) { std::cout << "Failed to read original bytes of MessageBoxA: " << GetLastError() << std::endl; return; } MessageBoxAStruct->modifyTimes = 1; // 计算Trampoline所需内存:14字节原指令 + 12字节跳转补丁 size_t trampolineSize = sizeof(MessageBoxAStruct->functionOriginalBytes) + 12; trampolineAddress = VirtualAlloc(NULL, trampolineSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (trampolineAddress == NULL) { std::cout << "Failed to allocate memory for the trampoline: " << GetLastError() << std::endl; return; } // 复制原指令到Trampoline if (!WriteProcessMemory(GetCurrentProcess(), trampolineAddress, MessageBoxAStruct->functionOriginalBytes, sizeof(MessageBoxAStruct->functionOriginalBytes), &MessageBoxAStruct->bytesWritten)) { std::cout << "Failed to write trampoline: " << GetLastError() << std::endl; return; } // 计算跳转回原函数的地址:原地址 + 截取的指令长度 DWORD64 jumpBackAddress = (DWORD64)MessageBoxAAddress + sizeof(MessageBoxAStruct->functionOriginalBytes); unsigned char jumpBackPatch[] = { 0x48, 0xb8, // mov rax, [address] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0 // jmp rax }; *(DWORD64*)&jumpBackPatch[2] = jumpBackAddress; // 写入跳转补丁到Trampoline末尾 if (!WriteProcessMemory(GetCurrentProcess(), (LPVOID)((DWORD64)trampolineAddress + sizeof(MessageBoxAStruct->functionOriginalBytes)), jumpBackPatch, sizeof(jumpBackPatch), &MessageBoxAStruct->bytesWritten)) { std::cout << "Failed to write jump back in trampoline: " << GetLastError() << std::endl; return; } } // 构建跳转Hook的补丁 unsigned char patch[] = { 0x48, 0xb8, // mov rax, [HookedMessageBoxA地址] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0 // jmp rax }; *(DWORD64*)&patch[2] = (DWORD64)HookedMessageBoxA; // 修改内存保护 DWORD oldProtect; if (!VirtualProtect(MessageBoxAStruct->functionOriginalAddress, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProtect)) { std::cout << "Failed to change memory protection: " << GetLastError() << std::endl; return; } // 写入补丁 if (!WriteProcessMemory(GetCurrentProcess(), (LPVOID)MessageBoxAStruct->functionOriginalAddress, patch, sizeof(patch), &MessageBoxAStruct->bytesWritten)) { std::cout << "Failed to write patch: " << GetLastError() << std::endl; return; } // 恢复内存保护 if (!VirtualProtect(MessageBoxAStruct->functionOriginalAddress, sizeof(patch), oldProtect, &oldProtect)) { std::cout << "Failed to restore memory protection: " << GetLastError() << std::endl; return; } printf("Hooked successfully\n"); std::cout << "finished hooking...\n\n" << std::endl; } int main() { HookMessageBoxA(); MessageBoxA(NULL, "Test", "Test", MB_OK); return 0; }
额外说明
如果要更严谨,建议使用反汇编库(如Capstone)自动分析指令长度,确保截取的是完整的指令序列,避免手动指定字节数带来的兼容性问题。
内容的提问来源于stack exchange,提问作者randomasker1234
相关产品推荐
相关产品推荐

