如何优化VEH Hook运行速度以解决注入进程后出现的FPS下降问题
VEH Hook性能下降原因及优化方案
性能下降核心原因
你当前使用的PAGE_GUARD页保护型VEH Hook的性能开销并不来自你写的Hook函数逻辑,而来自内核态到用户态的异常上下文切换开销:
- 你给目标函数所在的整个内存页加了保护,同页所有代码的执行都会触发异常,哪怕不是你要Hook的目标函数
- 每次触发页保护异常后还会触发一次单步异常来重新加保护,单次函数调用对应两次上下文切换,高频调用下开销会被指数放大
优化方案一:复制函数内存页实现无异常Hook(性能最优)
操作步骤如下:
- 获取原页信息
调用VirtualQuery查询目标函数地址的内存属性,拿到页基地址、页大小、原有保护属性:
MEMORY_BASIC_INFORMATION mbi; VirtualQuery((LPCVOID)target_fun, &mbi, sizeof(mbi)); SIZE_T page_size = mbi.RegionSize; LPVOID original_page_base = mbi.BaseAddress;
- 申请新的可执行内存页
调用VirtualAlloc申请和原页大小一致的内存空间,初始保护属性设为PAGE_READWRITE方便修改:
LPVOID cloned_page_base = VirtualAlloc(NULL, page_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
- 完整复制原页内容
直接内存拷贝把原页所有数据复制到新页:
memcpy(cloned_page_base, original_page_base, page_size);
- 修正相对寻址指令
计算原页和新页的偏移差,遍历新页内的所有汇编指令,修正call/jmp/RIP寻址 lea等相对偏移指令的操作数,保证指令跳转地址和原逻辑一致。 - 修改Hook点逻辑
找到目标函数在新页中的对应地址((uintptr_t)cloned_page_base + ((uintptr_t)target_fun - (uintptr_t)original_page_base)),在该地址开头写入跳转指令,跳转到你自己的Hook函数,你的Hook处理完成后再跳回新页中原函数的后续执行地址。 - 启用新页
将新页的保护属性修改为和原页一致的PAGE_EXECUTE_READ,然后修改原函数开头的指令为跳转指令,直接跳转到新页中的对应函数地址即可,全程不需要VEH介入,性能损失小于1%。
优化方案二:优化现有VEH Hook(适合不能修改原页内存的场景)
如果必须使用VEH规避内存校验,可以做以下优化:
- 替换PAGE_GUARD为硬件断点:使用dr0~dr3调试寄存器设置函数地址的执行断点,仅命中目标函数地址时才会触发异常,不会影响同页其他函数执行,也不需要单步异常重新加保护,开销直接降低70%以上
- 精简异常处理逻辑:移除异常处理函数内的所有系统API调用(比如你当前代码中单步异常里的
VirtualProtect),提前缓存所有需要用到的变量,减少异常处理执行时间 - 提高VEH优先级:调用
AddVectoredExceptionHandler时第一个参数传1,让你的异常处理函数最先被调用,避免遍历其他异常处理函数的额外开销
内容的提问来源于stack exchange,提问作者Fair
相关产品推荐
相关产品推荐

