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使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...
随机推荐
- SqlBulkCopy效率低下原因分析
看到标题 应该会奇怪 SqlBulkCopy 为什么会效率低下 场景:接手项目 数据库SQLSERVER2008R2, 目前有一张流水表单表数据超过4亿,表中建有索引,有其他模块对这个表进行查询操作 ...
- win10 uwp Window.Current.Dispatcher中Current为null
本文说的是进行网络中异步界面出现的错误,可能带有一定的主观性和局限性,说的东西可能不对或者不符合每个人的预期.如果觉得我有讲的不对的,就多多包含,或者直接关掉这篇文章,但是请勿生气或者发怒吐槽,可以在 ...
- 基于8211lib库对s57电子海图的解析和存储
电子海图是为适用航海需要而绘制的包含海域地理信息和航海信息的一种数字化的专题地图,符合国际标准的电子海图数据统称为S-57电子海图.本文主要在S-57电子海图数据的理论模型和数据结构的基础上,实现对S ...
- 正确理解Mysql的列索引和多列索引
MySQL数据库提供两种类型的索引,如果没正确设置,索引的利用效率会大打折扣却完全不知问题出在这. CREATE TABLE test ( id INT NOT NULL, last_ ...
- ALSA和Pulseaudio
小记一下,Deadbeef如果使用ALSA作为音频输出的话,会导致其他说有使用pulseaudio的程序[如Chrome]没声音.....[但是SMplayer使用ALSA的话不会...]
- (转)java的动态代理机制详解
原文出自:http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 在学习Spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一 ...
- springCloud四:熔断器ribbon--Hystrix
注:前文概念部分摘抄自博客园 纯洁的微笑 熔断器 雪崩效应 在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应.服务雪崩 ...
- 【机器学习】支持向量机(SVM)
感谢中国人民大学胡鹤老师,课程深入浅出,非常好 关于SVM 可以做线性分类.非线性分类.线性回归等,相比逻辑回归.线性回归.决策树等模型(非神经网络)功效最好 传统线性分类:选出两堆数据的质心,并做中 ...
- Jni中C++和Java的参数传递(转)
如何使用JNI的一些基本方法和过程在网上多如牛毛,如果你对Jni不甚了解,不知道Jni是做什么的,如何建立一个基本的jni程序,或许可以参考下面下面这些文章:利用VC++6.0实现JNI的最简单的例子 ...
- hdu 3091 Necklace(状态压缩类似于TSP问题)
Necklace Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 327680/327680 K (Java/Others)Total ...