使用push/retn方法Hook函数失败问题排查(VS2017+Win10)
排查Push/Retn Hook未生效问题(Win10 + VS2017环境)
我在Win10+VS2017环境里复现了你的代码,确实会出现编译运行正常但Hook未生效的情况,核心原因和解决方法如下:
问题根源分析
你的Hook思路本身是可行的:用push hookproc地址 + retn的指令替换原函数开头,让调用原函数时跳转到Hook函数。但VS2017的默认编译器优化会破坏这个逻辑:
- 函数内联优化:因为
main函数里直接调用func(),编译器默认会把func的代码内联到main中,导致你修改func地址处的指令根本不会被执行,程序实际跑的是内联后的MessageBox代码。 - 另外,虽然你加了
#pragma comment(linker, "/SECTION:.text,ERW")来设置.text段可写,但部分场景下编译器的额外处理可能让这个设置未完全生效,不过这不是本次问题的主因。
解决方法及修改后的代码
针对内联优化和内存保护问题,修改代码如下:
#include "pch.h" #include <iostream> #include<Windows.h> #pragma comment(linker, "/SECTION:.text,ERW") // 用__declspec(noinline)禁止编译器内联原函数 __declspec(noinline) void func() { MessageBox(0, L"not hook", L"info", MB_OK); } void hookproc() { MessageBox(0, L"hook success", L"info", MB_OK); } void hook_code() { BYTE* lpFunc1 = (BYTE*)func; DWORD oldProtect; // 手动修改内存保护属性,确保目标地址可写(双重保险) VirtualProtect(lpFunc1, 6, PAGE_EXECUTE_READWRITE, &oldProtect); // 写入Push+Retn指令 lpFunc1[0] = 0x68; // push指令机器码 *(ULONG_PTR*)&lpFunc1[1] = (ULONG_PTR)hookproc; // Hook函数地址 lpFunc1[5] = 0xC3; // retn指令机器码 // 恢复原内存保护属性(可选,但建议保持内存安全) VirtualProtect(lpFunc1, 6, oldProtect, &oldProtect); } int main() { hook_code(); func(); return 0; }
关键修改点说明
__declspec(noinline):强制编译器不将func内联到main中,确保main里调用的是func的实际内存地址,这样你修改的指令才会被执行。VirtualProtect:手动修改func所在内存页的保护属性为PAGE_EXECUTE_READWRITE,确保你能正常写入指令。虽然链接器选项已经尝试设置.text段可写,但手动调用VirtualProtect能避免编译器或系统的额外限制,更可靠。
验证方法
修改后编译运行,应该会弹出"hook success"对话框。如果还是有问题,可以用VS的调试器:
- 在
hook_code函数末尾加断点,查看lpFunc1地址处的指令是否被成功修改为push 0xXXXXXXX(hookproc的地址)和retn。 - 在
main里的func()调用处加断点,查看跳转地址是否是func的实际地址,而不是内联的代码。
内容的提问来源于stack exchange,提问作者赖敬之
相关产品推荐
相关产品推荐

