VirtualProtect是绕不过去的一步,因为目标函数入口内存页默认为PAGE_EXECUTE_READ(只读+可执行),直接写入会触发CPU硬件级ACCESS_VIOLATION异常;必须先调用VirtualProtect将页属性临时改为可写(如PAGE_READWRITE),修改后再恢复,否则系统强制终止。

为什么 VirtualProtect 是绕不过去的一步
目标函数入口所在的内存页默认是 PAGE_EXECUTE_READ(只读+可执行),直接用 WriteProcessMemory 或指针写入会触发 ACCESS_VIOLATION。这不是权限不足,而是 CPU 硬件级保护——你不能在“只读”页上改字节。必须先调 VirtualProtect 把页属性临时改成可写,改完再恢复,否则系统直接抛异常。
VirtualProtect 调用时最常踩的三个坑
错误不是出在会不会调用,而是参数和时机没对齐:
-
lpAddress必须是对齐到页边界(0x1000)的地址,不能直接传GetProcAddress返回的函数地址;得先用VirtualQuery查它所属页的基址 -
flNewProtect不能只设PAGE_EXECUTE_READWRITE,x64 下某些系统(如 Win10 1809+)会拒绝该组合,得用PAGE_READWRITE+ 后续再补执行权(VirtualProtect调两次) - 改完立刻调
FlushInstructionCache,否则 CPU 指令预取缓存里还是旧指令,大概率跳到野地址或执行被截断的半条指令
x86 和 x64 下 VirtualProtect 行为差异
同一个调用,在两种架构下表现不同,容易误判失败原因:
- x86:允许单次
VirtualProtect同时设PAGE_EXECUTE_READWRITE,成功概率高 - x64:部分版本(尤其是启用了 CFG 或 HVCI 的系统)会静默拒绝
PAGE_EXECUTE_READWRITE,返回TRUE但实际没生效;此时需分两步:VirtualProtect(..., PAGE_READWRITE, ...)→WriteProcessMemory→VirtualProtect(..., PAGE_EXECUTE_READ, ...) - 注意
dwSize参数:哪怕只改 5 字节,也得传整页大小(0x1000),否则 Windows 可能只改了部分页、留下“半写”状态
Hook 安装后不崩溃,但行为错乱怎么办
常见于跳转指令 patch 后没处理好指令边界或缓存,典型现象是:函数能进 detour,但返回后程序卡死或栈失衡。这时候要确认:
立即学习“C++免费学习笔记(深入)”;
- 是否备份了被覆盖的原始指令(比如 x64 下改前 12–14 字节,不是固定 5 字节)
-
VirtualProtect恢复权限时,是否用的和修改前完全一致的flOldProtect值(建议用VirtualQuery先读出来存着) - trampoline 内存是否也调了
VirtualProtect设为可执行——很多人只改了原函数页,忘了跳板页默认是不可执行的
真正麻烦的不是改不了内存,而是改完之后 CPU 还在跑旧缓存、页表还没刷新、或者权限恢复时漏掉某个环节。这些点不显眼,但一漏就蓝屏或飞指针。


















