原帖链接:http://bbs.csdn.net/topics/390940473?page=1#post-400726264
问题的来源于调试若干个系统的API函数,发现所有的系统API函数的开始出的汇编代码都会有如下的这个样子
76754A86 nop // 都会出现
76754A87 nop // 都会出现
76754A88 nop // 都会出现
76754A89 nop // 都会出现
76754A8A nop // 都会出现
76754A8B mov edi,edi // 都会出现
76754A8D push ebp
76754A8E mov ebp,esp
76754A90 pop ebp
76754A91 jmp 76754A98
解答1:
hot-patching 是有条件的,需要开始有空白的代码,一边写入跳转指令。
所以 微软在她们的 API 之前,添加一个著名的指令 mov edi,edi 。这条指令没有任何意义,但是可以被 短跳转指令覆盖,她们都是两个字节的指令。这样函数就可以被旁路了。
如果你需要长跳转,那么先来短跳转,跳转到这个指令的上面的 5 字节去。而这个 5 个字节正好可以在进行一次长跳转。
这就是为什么在 mov edi,edi 之前可以看到 5 个 nop 指令。
这就是为什么函数开始是 mov edi,edi 和她上面有 5 个 nop 指令了,这就是为 hot-patching 准备的。当你使用 VC 编译的时候,在代码生成的属性里,使用创建可热修补映像的时候,就可以满足 hot-patching 的要求了。
至于为什么使用 mov edi,edi 而不适用两个 nop,传说是因为 mov 效率比较高的原因。而 mov 指令上面的 5 个字节是 nop ,是因为这 5 个 nop 指令从不指令, API 函数都是从 mov edi,edi 开始执行的。
hot-patching 与 Detours 的区别:
这两者是不一致的,hot-patching 是有条件的,而 Detours 的条件更加宽松。
hot-patching 要求函数的开始有足够的空间执行 跳转指令,而 Detours 则要求整个函数的大小,至少能够装下跳转指令。
如果 可以 hot-patching ,那就可以不用 Detours ,因为 Detours 需要保存开始的被覆盖的指令。至少需要创建代码段,Detours 需要解析开始代码,知道第一个跳转指令大小之外的第一个代码的开始,一边保存被覆盖的代码和跳转回来的位置。相对于比较复杂。所以,如果你确认你的代码都是可以 hot-patching 的,那么就不需要 Detours 。
解答2:
做过hook,以下是我的猜想:
每个系统API前面都有这些代码确实是为了hot-patching,与detours库的原理差不多是一样的。
都是通过修改开始的几个字节以达到跳转到目标代码区域的目的。
比如要升级d3d9,系统中有d3d9_34.dll在使用,系统在升级时加载最新的d3d9_35.dll到所有加载了d3d9_34.dll的进程中,然后修改进程的d3d9_34.dll的所有API的前几个字节,使其跳转到d3d9_35.dll,完成热升级的目标。