许多编译器允许我们通过为NOP保留一些空间来修补正在运行的二进制文件,这些空间将被分支指令替换以重定向到新修补的函数。例如,MSVC通过在函数开始时为mov edi, edi
保留2个字节并为jmp new_func
保留6个填充字节来支持hot-patchability,在gcc中,您有-fpatchable-function-entry
option < / p>
-fpatchable-function-entry=N[,M]
- 在每个函数的开头立即生成
N
NOP,函数入口点位于第M
个NOP之前。如果省略M
,则默认为0,因此函数条目指向第一个NOP处的地址。 NOP指令保留了额外的空间,只要代码段可写,就可以在运行时修补任何所需的仪器。
但是,如果旧函数有错误,为什么我们不能仅仅破坏它并用跳转指令覆盖它的入口呢?或者,如果新功能比旧功能小,我们可以就地编写。即使在极少数情况下,我们想要恢复为旧版本,我们也可以在修补函数之前保留一些空间,然后将覆盖的字节备份在那里,或者只是从可执行文件中重新加载它们。
为什么在修补时需要保留旧的二进制文件?