2024年3月17日发(作者:绝地求生配置要求2022)
一、
序言对大多数的Windows开发者来说,如何在Win32系统中对API函数的调用
进行拦截一直是项极富挑战性的课题,因为这将是对你所掌握的计算机知识较为
全面的考验,尤其是一些在如今使用RAD进行软件开发时并不常用的知识,这
包括了操作系统原理、汇编语言甚至是关于机器指令代码的(听上去真是有点恐
怖,不过这是事实)。
当前广泛使用的Windows操作系统中,像Win 9x和Win NT/2K,都提供了一种
比较稳健的机制来使得各个进程的内存地址空间之间是相互独立,也就是说一个
进程中的某个有效的内存地址对另一个进程来说是无意义的,这种内存保护措施
大大增加了系统的稳定性。不过,这也使得进行系统级的API拦截的工作的难
度也大大加大了。
当然,我这里所指的是比较文雅的拦截方式,通过修改可执行文件在内存中的映
像中有关代码,实现对API调用的动态拦截;而不是采用比较暴力的方式,直
接对可执行文件的磁盘存储中机器代码进行改写。
二、
API钩子系统一般框架通常,我们把拦截API的调用的这个过程称为是安装一个
API钩子(API Hook)。一个API钩子至少有两个模块组成:一个是钩子服务器
(Hook Server)模块,一般为EXE的形式;一个是钩子驱动器(Hook Driver)
模块,一般为DLL的形式。
服务器主要负责向目标进程注入驱动器,使得驱动器工作在目标进程的地址空间
中,这是关键的第一步。驱动器则负责实际的API拦截工作,以便在我们所关
心的API函数调用的前后能做一些我们需要的工作。
一个大家比较常见的API钩子的例子就是一些实时翻译软件(像金山词霸)中
必备的的功能:屏幕抓词,它主要是对一些GDI 函数进行了拦截,获取它们的
输入参数中的字符串,然后在自己的窗口中显示出来。针对上述的两个部分,有
以下两点需要我们重点考虑的: 选用何种DLL注入技术 采用何种API拦截机
制
三、
注入技术的选用由于在Win32系统中各个进程的地址是互相独立的,因此我们无
法在一个进程中对另一个进程的代码进行有效的修改。而你要完成API钩子的
工作就必须进行这种操作。因此,我们必须采取某种独特的手段,使得API钩
子(准确的说是钩子驱动器)能够成为目标进程中的一部分,才有较大的可能来
对目标进程数据和代码进行有控制的修改。
通常有以下几种注入方式:
1.利用注册表如果我们准备拦截的进程连接了,也就是使用了User32
中的API(一般图形界面的应用程序都符合这个条件),那么就可以简单把你
的钩子驱动器DLL的名字作为值添加在下面注册表的键下:
HKEY_LOCAL_MACHINESoftwareMicrosoftWindowsNTCurrentVersionW
indowsAppInit_DLLs 值的形式可以为单个DLL的文件名,或者是一组DLL
的文件名,相邻的名称之间用逗号或空格间隔。所有由该值标识的DLL将在
符合条件的应用程序启动的时候装载。这是一个操作系统内建的机制,相对
其他方式来说危险性较小,但它有一些比较明显的缺点: 该方法仅适用于
NT/2K操作系统。看看键的名称你就应该明白 为了激活或停止钩子的注入,
必须重新启动Windows。这个就似乎太不方便了 不能用此方法向没有使用
User32的应用程序注入DLL,例如控制台应用程序 不管需要与否,钩子DLL
将注入每一个GUI应用程序,这将导致整个系统性能的下降
2.
建立系统范围的Windows钩子要向某个进程注入DLL,一个十分普遍也是比较
简单的方法就是建立在标准的Windows钩子的基础上。Windows钩子一般是在
DLL中实现的,这是一个全局性的Windows钩子的基本要求,这也符合我们的
需要。当我们成功地调用SetWindowsHookEx函数之后,便在系统中安装了某种
类型的消息钩子,这个钩子可以是针对某个进程,也可以是针对系统中的所有进
程。一旦某个进程中产生了该类型的消息,操作系统会自动把该钩子所在的DLL
映像到该进程的地址空间中,从而使得消息回调函数(在SetWindowsHookEx的
参数中指定)能够对此消息进行适当的处理,在这里,我们所感兴趣的当然不是
对消息进行什么处理,因此在消息回调函数中只需把消息钩子向后传递就可以
了,但是我们所需的DLL已经成功地注入了目标进程的地址空间,从而可以完
成后续工作。
我们知道,不同进程中使用的DLL之间是不能直接共享数据的,因为它们活动
在不同的地址空间中。但在Windows钩子DLL中,有一些数据,例如Windows
钩子句柄HHook,这是由SetWindowsHookEx函数返回值得到的,并且作为参数
将在CallNextHookEx函数和UnhookWindoesHookEx函数中使用,显然使用
SetWindowsHookEx函数的进程和使用CallNextHookEx函数的进程一般不会是
同一个进程,因此我们必须能够使句柄在所有的地址空间中都是有效的有意义
的,也就是说,它的值必须必须在这些钩子DLL所挂钩的进程之间是共享的。
为了达到这个目的,我们就应该把它存储在一个共享的数据区域中。
在VC++中我们可以采用预编译指令#pragma data_seg在DLL文件中创建一个新
的段,并且在DEF文件中把该段的属性设置为“shared”,这样就建立了一个共
享数据段。对于使用Delphi的人来说就没有这么幸运了:没有类似的比较简单
的方法(或许是有的,但我没有找到)。不过我们还是可以利用内存映像技术来
申请使用一块各进程可以共享的内存区域,主要是利用了CreateFileMapping和
MapViewOfFile这两个函数。这倒是一个通用的方法,适合所有的开发语言,只
要它能使用Windows的API。
在Borland的BCB中有一个指令#pragma codeseg与VC++中的#pragma data_seg
发布者:admin,转转请注明出处:http://www.yc00.com/xitong/1710685278a1799248.html
评论列表(0条)