运行时本地函数Inline Hook实现报错求助
本地函数Inline Hook实现问题分析与修正
核心问题梳理
你的代码存在几个关键错误,导致Hook失败:
- 函数地址错误偏移:本地函数不存在DLL导出的Thunk层,不需要读取开头字节计算偏移,直接使用
testFunction的原始地址即可。 - 调用约定不匹配:
testFunction默认是__cdecl调用约定,但Hook函数和函数指针误用__stdcall,会造成栈不平衡引发崩溃。 - 跳转地址计算错误:因原函数地址被错误偏移,导致Hook跳转和跳板跳转的地址计算全部失效。
- 指令截断风险:硬复制5字节到跳板前,未确保原函数开头指令的完整性(简单场景下本地函数开头通常是完整的短指令,可直接处理)。
修正后的完整代码
#include <Windows.h> #include <stdio.h> // 统一使用__cdecl调用约定,匹配testFunction的默认约定 typedef int(__cdecl* tdOrigFunction)(int name); tdOrigFunction testFunctionATrampoline; int __cdecl HookedTestFunction(int name) { printf("Hooked testFunction\n"); // 调用跳板执行原函数逻辑 int ret = testFunctionATrampoline(name); printf("原函数返回值:%d\n", ret); return 2; } int Error(const char* msg) { printf("%s (%u)\n", msg, GetLastError()); return 1; } int __cdecl testFunction(int name) { printf("testFunction\n"); printf("testFunction\n"); printf("testFunction\n"); printf("testFunction\n"); printf("testFunction\n"); printf("testFunction%d\n", name); return 1; } int main() { BYTE* origFunctionAddress = (BYTE*)testFunction; BYTE* trampolineAddress = NULL; // 分配可执行的跳板内存 trampolineAddress = (BYTE*)VirtualAlloc(NULL, 32, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (trampolineAddress == NULL) { return Error("Failed to allocate memory for trampoline"); } // 1. 复制原函数开头5字节到跳板(确保原函数前5字节是完整指令,这里testFunction开头满足) memcpy(trampolineAddress, origFunctionAddress, 5); // 2. 在跳板末尾添加跳回原函数剩余部分的指令 // 计算跳转偏移:原函数5字节后的地址 - 跳板地址 - 跳转指令长度(5) uintptr_t jumpBackOffset = (uintptr_t)(origFunctionAddress + 5) - (uintptr_t)(trampolineAddress + 5) - 5; *(BYTE*)(trampolineAddress + 5) = 0xE9; // JMP指令 *(uintptr_t*)(trampolineAddress + 6) = jumpBackOffset; // 3. 修改原函数开头的内存保护属性,允许写入 DWORD oldProtectVal; if (!VirtualProtect(origFunctionAddress, 5, PAGE_EXECUTE_READWRITE, &oldProtectVal)) { return Error("Failed to change memory protection"); } // 4. 在原函数开头写入跳转到Hook函数的指令 uintptr_t jumpToHookOffset = (uintptr_t)HookedTestFunction - (uintptr_t)(origFunctionAddress + 5) - 5; *(BYTE*)origFunctionAddress = 0xE9; // JMP指令 *(uintptr_t*)(origFunctionAddress + 1) = jumpToHookOffset; // 5. 恢复原函数内存保护属性 VirtualProtect(origFunctionAddress, 5, oldProtectVal, &oldProtectVal); // 绑定跳板到函数指针 testFunctionATrampoline = (tdOrigFunction)trampolineAddress; // 测试Hook效果 int ret = testFunction(1); printf("Hook函数返回值:%d\n", ret); // 释放跳板内存(可选,程序结束前释放即可) VirtualFree(trampolineAddress, 0, MEM_RELEASE); return 0; }
关键修改说明
- 统一调用约定:将函数指针和Hook函数都改为
__cdecl,和本地函数的默认约定保持一致。 - 移除错误的地址偏移:直接使用
testFunction的原始地址,不再处理多余的相对跳转计算。 - 正确计算跳转偏移:无论是跳板跳回原函数,还是原函数跳转到Hook函数,都基于正确的地址计算相对偏移。
- 内存操作优化:调整VirtualAlloc的大小更合理,确保跳板有足够空间存放指令。
内容的提问来源于stack exchange,提问作者houxinlin
相关产品推荐
相关产品推荐

