API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在Windows操作系统里面使用了这个技术,如Windows兼容模式等。 API HOOK 技术并不是计算机病毒专有技术,但是计算机病毒经常使用这个技术来达到隐藏自己的目的。
API HOOK解释
在windows系统下编程,应该会接触到api函数的使用,常用的api函数大概有2000个左右。今天随着控件,stl等高效编程技术的出现,api的使用概率在普通的用户程序上 就变得越来越小了。当诸如控件这些现成的手段不能实现的功能时,我们还需要借助api。最初有些人对某些api函数的功能不太满意,就产生了如何修改这些 api,使之更好的服务于程序的想法,这样api hook就自然而然的出现了。我们可以通过api hook,改变一个系统api的原有功能。基本的方法就是通过hook“接触”到需要修改的api函数入口点, 改变它的地址指向新的自定义的函数。api hook并不属于msdn上介绍的13类hook中的任何一种。所以说,api hook并不是什么特别不同的hook,它也需要通过基本的hook提高自己的权限,跨越不同进程间访问的限制,达到修改api函数地址的目的。对于自身进程空间下使用到的api函数地址的修改,是不需要用到api hook技术就可以实现的。
API HOOK和PE文件的关系
api hook技术的难点,并不在于hook技术,而在于对PE结构的学习和理解。如何修改api函数的入口地址?这就需要学习pe可执行文件(.exe,.dll等)如何被系统映射到进程空间中,这需要学习pe格式的基本知识。windows已经提供了很多数据结构struct帮助我们访问pe格式,记住它们,我们就不要自己计算格式的具体字节位置这些繁琐的细节。但是从api hook的实现来看,pe格式的访问部分仍然是整个编程实现中最复杂的一部分,对于经常crack的朋友不在此列。
下面对于pe格式的介绍这一部分,学习了pe格式后再看这些就很容易了。
pe格式的基本组成:
DOS文件头
|
以4D5A的16进制即MZ开头
|
PE文件头
|
以50450000的PE00开头
|
可选头文件
|
包含下面的数据目录
|
数据目录
|
函数入口地址,基地址,内存,文件对齐粒度之类信息
|
节表
|
维护一个所有节的信息
|
节表1
|
具有同类特征的信息,文件的主体部分
|
节表2
|
|
...
|
|
节表n
|
我们需要从“可选头”尾的“数据目录”数组中的第二个元素——输入符号表的位置,它是一个IMAGE_DATA_DIRECTORY结构,从它中的VirtualAddress地址,“顺藤摸瓜”找到api函数的入口地点。
简单说明如下:
OriginalFirstThunk 指向IMAGE_THUNK_DATA结构数组,为方便只画了数组的一个元素,AddressOfData 指向IMAGE_IMPORT_BY_NAME结构。
IMAGE_IMPORT_DESCRIPTOR数组:每个引入的dll文件都对应数组中的一个元素,以全0的元素(20个bytes的0)表示数组的结束
IMAGE_THUNK_DATA32数组:同一组的以全0的元素(4个bytes的0)表示数组的结束,每个元素对应一个IMAGE_IMPORT_BY_NAME结构
IMAGE_IMPORT_BY_NAME:如..@Consts@initialization$qqrv. 表示
Unmangled Borland C++ Function: qualified function __fastcall Consts::initialization()
如果PE文件从kernel32.dll中引入10个函数,那么IMAGE_IMPORT_DESCRIPTOR 结构的 Name1域包含指向字符串"kernel32.dll"的RVA,同时每个IMAGE_THUNK_DATA 数组有10个元素。(RVA是指相对地址,每一个可执行文件在加载到内存空间前,都以一个基址作为起点,其他地址以基址为准,均以相对地址表示。这样系统加载程序到不同的内存空间时,都可以方便的算出地址)
上述这些结构可以在winnt.h,ImageHlp.h头文件里查到。
Hook解释
Hook是Windows中提供的一种用以替换DOS下“中断”的系统机制,中文译为“挂钩”或“钩子”。在对特定的系统事件进行hook后,一旦发生已hook事件,对该事件进行hook的程序就会受到系统的通知,这时程序就能在第一时间对该事件做出响应。
另一解释:钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口 的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特 定事件。
钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。
Hook原理
每一个Hook都有一个与之相关联的指针列表,称之为钩子链表,由系统来维护。这个列表的指针指向指定的,应用程序定义的,被Hook子程调用的回调函数, 也就是该钩子的各个处理子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息,或者修改 消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的 先获得控制权。
Windows 并不要求钩子子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。
钩子子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。
系统钩子与线程钩子
SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。
线程钩子用于监视指定线程的事件消息。线程钩子一般在当前线程或者当前线程派生的线程内。
系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含“钩子回调函数”的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。
几点说明:
(1)如果对于同一事件(如鼠标消息)既安装了线程钩子又安装了系统钩子,那么系统会自动先调用线程钩子,然后调用系统钩子。
(2)对同一事件消息可安装多个钩子处理过程,这些钩子处理过程形成了钩子链。当前钩子处理结束后应把钩子信息传递给下一个钩子函数。
Hook的应用模式
观察模式
最为常用,像Windows提供的SetWindowHook就是典型地为这类应用准备的。而且这也是最普遍的用法。这个模式的特点是,在事情发生的时候,发出一个通知信息。观察者只可以查看过程中的信息,根据自己关心的内容处理自己的业务,但是不可以更改原来的流程。
如全局钩子中,经常使用的鼠标消息、键盘消息的监视等应用。金山词霸屏幕取词的功能是一个典型的应用(具体技术可以参考此类文章)。
注入模式
这个模式和观察模式最大的不一样的地方在于,注入的代码是为了扩展原始代码的功能业务。插件模 式是此类模式的典型案例.不管瘦核心的插件系统(如Eclipse)还是胖核心的插件系统(如Delphi、Visual Studio等IDE环境),其对外提供的插件接口都是为了扩展本身系统的功能的.这种扩展的应用方式的典型特点,就是新的扩展代码和原来的代码会协调处 理同类业务。
替换模式
如果针对应用目的不同,可以叫修复模式或破解模式。前者是为了修改系统中的BUG,后者是为了破解原有系统的限制。很多黑客使用此种模式,将访问加密锁的DLL中的导出表,替换成自己的函数,这样跳过对软件的控制代码。这类应用的难点是,找出函数的参数。这类模式的特点是,原有的代码会被新的代码所替换。
前面三个是基本模式,还有很多和实际应用相关的模式。
集权模式
此 类模式的出现,大都是为了在全部系统中,统一处理某类事情。它的特点不在于注入的方式,而在于处理的模式。这个模式,大都应用到某类服务上,比如键盘服 务,鼠标服务,打印机服务等等特定服务上。通过统一接管此类服务的访问,限制或者协调对服务的访问。比如键盘锁功能的实现,就是暂时关闭键盘的所有应用。 这类模式的特点主要会和特点服务有关联。
修复模式
替换模式的一种,这里强调的是其应用的目的是为了修复或扩展原有系统的功能。
破解模式
替换模式的一种,这里强调的是其应用的目的是为了跳过原有系统的一部分代码。如加密检测代码,网络检测代码等等。
插件模式
注入模式的一种,在系统的内部直接依靠HOOK机制进行扩展业务功能。
共享模式
这类应用中,经常是为了获取对方的数据。必然我希望获取对方系统中,所有字符串的值。可以通过替换对方的内存管理器,导出所有字符串。这个应用比较特殊。不过其特点在于,目的是达到系统之间的数据共享。其实现,可能是观察模式,也可能是替换模式。
在VB6.0中的声明
公有声明:Public Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
私有声明:Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long