在x86平台上,无论是在调试器中跟到系统DLL中时,还是反汇编某个系统DLL时,经常会发现很多API的第一条汇编指令都是mov edi, edi。根据经验来讲,C函数的汇编形式,应该是首先push ebp保存原始栈顶,然后mov ebp, esp构造新的栈帧,接下来sub esp, 0nnh分配局部变量空间,之后就是函数体了,结束时先mov esp, ebp恢复栈指针,然后pop ebp恢复栈顶指针,最后retn。

push    ebp             ; 保存原始栈顶指针到栈
mov ebp, esp ; 获取新栈顶指针,用以访问参数和局部变量
sub esp, 0NNh ; 分配局部变量空间,大小0xNN字节
...
... mov esp, ebp ; 恢复初始栈指针
pop ebp ; 恢复原始栈顶指针,调用者用来访问参数和局部变量
retn ; 返回到调用者

现在,前面多了一条mov edi, edi,是用来干什么的呢?看了网上好多论坛博客,众说纷纭。有的说是一条延时指令为了实现一个短的等待,有的说是为了对齐以便于Cache,等等。看了半天,都不是很有说服力,最终在MSDN Blog上找到一篇相关说明,应该是最有道理的。上面说,这条指令的添加主要是为了支持hotpatch,翻译过来好像是叫热修补,类似于Inline Hook。说到这里就要先提一下MSDN帮助文档上与此相关的说明,是关于hotpatch的两个编译链接选项:

首先是编译器选项/hotpatch:

When /hotpatch is used in a compilation, the compiler ensures that first instruction of each function is at least two bytes, which is required for hot patching.
To complete the preparation for making an image hotpatchable, after you use /hotpatch to compile, you must use /FUNCTIONPADMIN to link. When you compile and link an image by using one invocation of cl.exe, /hotpatch implies /functionpadmin.
Because instructions are always two bytes or larger on the ARM architecture, and because x64 compilation is always treated as if /hotpatch has been specified, you don't have to specify /hotpatch when you compile for these targets; however, you must still link by using /functionpadmin to create hotpatchable images for them. The /hotpatch compiler option only affects x86 compilation.

大致含义:

当/hotpatch用于编译时,编译器会确保每个函数的第一条指令至少占两个字节,这是热修补的要求。
要使生成的映像文件可以热修补,除了使用/hotpatch来编译,你还必须使用/FUNCTIONPADMIN进行链接。当你使用cl.exe一次性完成编译和链接时,/hotpatch隐含了/FUNCTIONPADMIN。
因为ARM架构的指令至少占两个字节,而且x64编译总是视为/hotpatch已指定,所以当编译目标为这两种架构时不必指定/hotpatch参数;然而,你仍然必须使用/FUNCTIONPADMIN来为它们生成可以热修补的映像文件。/hotpatch编译器选项仅影响x86编译。

连接器选项/FUNCTIONPADMIN:

The amount of padding to add to the beginning of each function, 5, 6, or 16. x86 images require five bytes of padding, x64 images require 6 bytes, and images built for the Itanium Processor Family require 16 bytes of padding at the beginning of each function.
By default, the compiler will add the correct amount of space to the image, based on the machine type of the image.
In order for the linker to produce a hotpatchable image, the .obj files must have been compiled with /hotpatch (Create Hotpatchable Image).
When you compile and link an image with a single invocation of cl.exe, /hotpatch implies /functionpadmin.

机器译文:

要添加到每个函数开头的填充量,为 5、6 或 16字节。在每个函数的开头,x86 映像需要填充 5 个字节,x64 映像需要填充 6 个字节,为 Itanium 处理器系列生成的映像需要填充 16 个字节。
默认情况下,编译器根据映像的计算机类型将正确的空格数添加到映像中。
为了使链接器生成可热修补的映像,.obj 文件必须已使用 /hotpatch 进行编译。
使用 cl.exe 的单个调用来编译和链接映像时,/hotpatch 隐含表示 /functionpadmin。

看完上面两段文字,应该会有所思。回过头来继续刚才的mov edi, edi,先来看一下函数开始几条指令的大小:

00000000    8BFF          mov     edi, edi        ; 2字节
00000002 55 push ebp ; 1字节
00000003 8BEC mov ebp, esp ; 2字节

我们可以看到正如上面帮助文档所讲,mov edi, edi占用了两个字节。那么为什么第一条指令不能少于两个字节呢?其实以前的MSDN上本来说的是确保第一条指令为两字节,后来更改为不少于两字节,估计早时就是针对x86上的mov edi, edi来说的。。根据hotpatch的设计思路,要在第一条指令处写入一个短跳转,x86平台为0xEB后跟一字节偏移立即数,通过这个短跳转跳转到函数间的填充区域,在填充区域写入一个长跳转,x86平台为0xE9后跟一个双字立即数偏移,通过这个长跳转就可以跳转到我们的Patch函数或者Hook函数。

    E902000000    jmp     07h             ; 函数间至少有5字节填充,将双字偏移2改为实际函数的相对地址    EBF9          jmp     0h              ; 原来的mov edi, edi,负向跳转7字节到长跳转指令

函数头部的第一条指令如果是像mov edi, edi这样的一条无作用指令,那么跳回前也不需要再进行等价操作来实现这条指令的作用了,直接跳转回mov edi, edi的下一条指令即可。看一看现在好多Inline Hook在实现的时候,并没有利用这种机制,而是直接覆盖掉函数的前5字节,然后自己实现被覆盖的指令。其实如果目标函数前三条指令是mov edi, edi、push ebp和mov ebp, esp的话,这样也是一个普适的方法,就是跳转回来之前需要自己实现push ebp和mov ebp, esp这两条指令。对于其他情况,需要有针对的特殊化处理。

综上所述,在Hook以mov edi, edi开头的函数时,还是使用短跳转加长跳转的方式实现起来最方便,完全用C语言即可实现,不需要再写汇编代码,还可以做成一个通用Hook函数。只是这种方便只存在于32位的大多数API中,还是有很大局限性的。

OK. That's all. 学识有限,大家有好的思路欢迎交流。

Win32API起始处的mov edi, edi与用户空间InlineHook的更多相关文章

  1. 函数开始处的MOV EDI, EDI的作用

    调试程序调试到系统库函数的代码时,总会发现系统函数都是从一条MOV EDI, EDI指令开始的,紧接着这条指令下面才是标准的建立函数局部栈的代码.对系统DLL比如ntdll.dll进行反汇编,可以发现 ...

  2. Linux用户空间与内核空间(理解高端内存)

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

  3. Linux用户空间与内核空间

    源:http://blog.csdn.net/f22jay/article/details/7925531 Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针 ...

  4. linux 用户空间与内核空间——高端内存详解

    摘要:Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对 ...

  5. linux用户空间和内核空间(内核高端内存)_转

    转自:Linux用户空间与内核空间(理解高端内存) 参考: 1. 进程内核栈.用户栈 2. 解惑-Linux内核空间 3. linux kernel学习笔记-5 内存管理   Linux 操作系统和驱 ...

  6. Linux用户空间与内核空间(理解高端内存)【转】

    转自:http://www.cnblogs.com/wuchanming/p/4360277.html Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递 ...

  7. Linux系统调用具体解释(怎样从用户空间进入内核空间)

    系统调用概述 计算机系统的各种硬件资源是有限的,在现代多任务操作系统上同一时候执行的多个进程都须要訪问这些资源,为了更好的管理这些资源进程是不同意直接操作的,全部对这些资源的訪问都必须有操作系统控制. ...

  8. Linux用户空间与内核地址空间

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

  9. linux 用户空间与内核空间——高端内存了解

    Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...

随机推荐

  1. 浅谈script标签中的async和defer

    script标签用于加载脚本与执行脚本,在前端开发中可以说是非常重要的标签了.直接使用script脚本的话,html会按照顺序来加载并执行脚本,在脚本加载&执行的过程中,会阻塞后续的DOM渲染 ...

  2. caffe源码 池化层 反向传播

    图示池化层(前向传播) 池化层其实和卷积层有点相似,有个类似卷积核的窗口按照固定的步长在移动,每个窗口做一定的操作,按照这个操作的类型可以分为两种池化层: 输入参数如下: 输入: 1 * 3 * 4 ...

  3. 简单说下C#变量的作用域

    变量的作用域分为局部变量和全局变量举个小例子 class Program { int i = 3;//这个变量i 需要实例化Program才能使用 static void Main(string[] ...

  4. win10 UWP 单元测试

    我们在写代码的时候不能保证我们写出来的代码是正确的,所以我们经常要单元测试. 单元测试和重构都是在做完一个小小函数一般就要进行一次,越早做就越好,可以比较早发现问题,这时我们还记得我们写的内容,不过比 ...

  5. 成为一名Java高级工程师你需要学什么

    宏观上: 1.技术广度方面至少要精通多门开源技术吧,研究过struts\spring等的源码.2.项目经验方面从头到尾跟过几个大项目,头是指需求阶段,包括需求调研.尾是指上线交付之后,包括维护阶段.3 ...

  6. Python装饰器,json,pickle

    装饰器 定义:本质是函数,装饰其它函数是为了给其添加新功能: 原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 实现装饰器知识储备: 1.函数即变量: 2.高阶函数 3.嵌套 ...

  7. github上传项目

    前置说明: 1.github上已经创建好的repositories,没有的可以自己创建一个 2.已经安装好的git,下载源推荐https://pan.baidu.com/s/1kU5OCOB#list ...

  8. 03-从零玩转JavaWeb-创建类与对象

    创建类创建对象 一.什么是成员变量 对象的一些状态特征使用成员变量表示   二.行为什么什么表示 对象的 行为 使用 方法 表示   三.如何创建对象 class 类名{0 ~ N个成员变量0 ~ N ...

  9. JAVA 的 Date、Calendar的常用用法

    一.Date与String的互转用法,这里需要用到SimpleDateFormat Date date = new Date();        //设置格式        SimpleDateFor ...

  10. Python Web框架篇:Django Model基础

    model是关于你的数据的单一的,确定的信息来源. 它包含您正在存储的数据的基本字段和行为.Django通过抽象化的模型层(models)为你的网络应用提供对于数据的结构化处理和操作处理,数据库相关的 ...