Win32API起始处的mov edi, edi与用户空间InlineHook
在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的更多相关文章
- 函数开始处的MOV EDI, EDI的作用
调试程序调试到系统库函数的代码时,总会发现系统函数都是从一条MOV EDI, EDI指令开始的,紧接着这条指令下面才是标准的建立函数局部栈的代码.对系统DLL比如ntdll.dll进行反汇编,可以发现 ...
- Linux用户空间与内核空间(理解高端内存)
Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...
- Linux用户空间与内核空间
源:http://blog.csdn.net/f22jay/article/details/7925531 Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针 ...
- linux 用户空间与内核空间——高端内存详解
摘要:Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对 ...
- linux用户空间和内核空间(内核高端内存)_转
转自:Linux用户空间与内核空间(理解高端内存) 参考: 1. 进程内核栈.用户栈 2. 解惑-Linux内核空间 3. linux kernel学习笔记-5 内存管理 Linux 操作系统和驱 ...
- Linux用户空间与内核空间(理解高端内存)【转】
转自:http://www.cnblogs.com/wuchanming/p/4360277.html Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递 ...
- Linux系统调用具体解释(怎样从用户空间进入内核空间)
系统调用概述 计算机系统的各种硬件资源是有限的,在现代多任务操作系统上同一时候执行的多个进程都须要訪问这些资源,为了更好的管理这些资源进程是不同意直接操作的,全部对这些资源的訪问都必须有操作系统控制. ...
- Linux用户空间与内核地址空间
Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...
- linux 用户空间与内核空间——高端内存了解
Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...
随机推荐
- win10 uwp 自定义控件 SplitViewItem
本文主要是因为汉堡菜单里面列出的菜单很多重复的图标和文字,我把它作为控件,因为是随便写,可能存在错误,如果发现了,请和我说或关掉浏览器,请不要发不良言论. 我们使用汉堡菜单,经常需要一个 需要一个图标 ...
- 填个小坑,Vue不支持IE8及以下,跨域ajax不支持IE9
这特么就尴尬了,说好的Vue支持IE8及以下的呢,引入jquery,测试IE个浏览器,IE9仍然显示不正常, 然而命令行测试Vue仍然存在, 数据回不来!数据回不来!数据回不来! 好吧 肉包子打狗$ ...
- nexus3 添加第三方本地文件jar到仓库
因为nexus3和nexus2手动上传第三方jar有点区别 故记录一下. 如上传京东 open-api-sdk-2.0.jar 首先创建一个目录 方便执行上传的时候url参数 也可以不创建 mkdir ...
- 【ASP.NET MVC 学习笔记】- 04 依赖注入(DI)
本文参考:http://www.cnblogs.com/willick/p/3223042.html 1.在一个类内部,不通过创建对象的实例而能够获得某个实现了公开接口的对象的引用.这种"需 ...
- Python Nose框架编写测试用例方法
1. 关于Nose nose项目是于2005年发布的,也就是 py.test改名后的一年.它是由 Jason Pellerin 编写的,支持与 py.test 相同的测试习惯做法,但是这个包更容易安装 ...
- 使用 paddle来进行文本生成
paddle 简单介绍 paddle 是百度在2016年9月份开源的深度学习框架. 就我最近体验的感受来说的它具有几大优点: 1. 本身内嵌了许多和实际业务非常贴近的模型比如个性化推荐,情感分析,词向 ...
- session失效问题
具体设置很简单,方法有三种: ()在主页面或者公共页面中加入:session.setMaxInactiveInterval();参数600单位是秒,即在没有10分钟活动后,session将失效. 这里 ...
- 找到python官方标准库文档
python中有很多标准库.我们没法记住全部标准库,但是可以在:https://docs.python.org/3/py-modindex.html 中查看标准库的索引 在python的官方文档中,如 ...
- sqoop1.4.6从mysql导入hdfs\hive\hbase实例
//验证sqoop是否连接到mysql数据库sqoop list-tables --connect 'jdbc:mysql://n1/guizhou_test?useUnicode=true& ...
- 在ASP.NET Core中使用AOP来简化缓存操作
前言 关于缓存的使用,相信大家都是熟悉的不能再熟悉了,简单来说就是下面一句话. 优先从缓存中取数据,缓存中取不到再去数据库中取,取到了在扔进缓存中去. 然后我们就会看到项目中有类似这样的代码了. pu ...