如何进行 NOP 填充¶
NOP 填充一块汇编代码的第一步是让代码所在的内存块可写。不过,写入 NOP 命令时,同一内存页面上的代码可能正在执行,所以你还想确保内存仍然可执行。你可以通过把内存保护设置为 PAGE_EXECUTE_READWRITE 同时完成这两件事。内存保护正确后,你可以写入 NOP 命令,大功告成。让内存保持可写在技术上无害,但完成后恢复原始保护是好习惯。
假设你有第 6 章描述的写入和保护内存的工具,你可以编写像清单 8-2 所示的函数,向游戏内存写入 NOP 命令。(打开项目的 NOPExample.cpp 文件跟着做。)
template<int SIZE>
void writeNop(DWORD address)
auto oldProtection =
protectMemory<BYTE[SIZE]>(address,
PAGE_EXECUTE_READWRITE);
for (int i = 0; i < SIZE; i++)
writeMemory<BYTE>(address + i, 0x90);
protectMemory<BYTE[SIZE]>(address,
oldProtection);
}
清单 8-2:正确的 NOP 填充,包含内存保护
在这个示例中,writeNop() 函数设置合适的内存保护,写入等于 SIZE 的 NOP 命令数量,并重新应用原始内存保护级别。
writeNop() 函数把要放置的 NOP 指令数量作为模板参数,因为内存函数在编译时需要一个大小正确的类型。传入整数 SIZE 告诉内存函数在编译时操作 BYTE[SIZE] 类型的对象。要在运行时指定动态大小,只需去掉循环,改为调用 protectMemory
用要放置 NOP 的地址和要放置的 NOP 命令数量调用这个函数:
请记住,NOP 命令的数量应该与要移除命令的字节大小匹配。这次对 writeNop() 的调用向地址 0xDEADBEEF 写入两条 NOP 命令。