昨天提到了如何用Hook Ole32 提供的两个API来实现Hook Drag & Drop的过程。算是给EasyHook库做一个广告吧。今天给大家讲讲EasyHook的实现原理。

API Hooking有两种,内核态级别的和用户态级别的。内核态级别的API Hooking也是很多病毒的实现原理,而且在微软不断强化其PatchGuard技术之后,已经越来越困难。做为建设四化的四有青年,我们还是把精力集中在用户态级别的API Hooking上好了。用户态的API Hooking其实就是为了在应用程序和Windows之间插一脚,从而达到用户调用的不再直接是Windows提供的函数,而是调用我们提供的函数。我们可以选择把调用转交给真正的Windows提供的函数,也可以选择自己处理掉,如果我们知道怎么处理的话。API Hooking非常强大可以应用在很多地方,比如监测(非安全领域,因为很容易绕过),虚拟化,软件破解等。要实现API Hooking有很多选择,基本上可以分为:

1、木马DLL

2、修改导出表

3、修改导入表

4、代码重写

实现的难易程度也基本上是上述的顺序。木马DLL是最好实现的,前年我因为需要解码nellymoser编码的音频,但是只有Flash播放器才有它的解码器。所以我就把winmm.dll这个系统提供多媒体功能的dll做了一个木马,实现了拦截wav输出的目的。木马DLL只需要你写一个同名的dll,导出相同名字和签名的函数即可。不过它的限制也最大,最大的原因是windows核心的DLL是不会从当前目录加载的(比如user32.dll),同时它还要求木马DLL必须预先放置在exe的同级目录下。

修改导出和导入表限制也很大。真正的王道是代码重写,不过难度陡增。在这个方面比较出名的有Detours(微软研究院的,不过已经没更新了),和madshi(DELPHI领域的,用户众多)。不过最近新出了一种非常强大的库,叫EasyHook。它具有非常独特的特性,支持.NET。也就是说,你可以用C#来写API钩子。这在以前是无法想象的。它的项目地址是:http://www.codeplex.com/easyhook。EasyHook其实不光是一个API Hook的库,它还提供了Remote Injection(远程注入)的功能。也就是说,你不但能够在Hook当前进程的API,还能去Hook其他进程的API。

所谓代码重写,是指修改DLL映射到当前进程的内存中的代码(二进制的机器指令)。每个API都会在运行时解析出一个地址,然后程序会用call指令去调用这个地址所包含的机器指令。代码重写就是把这个地址所在位置的机器指令重写了,从而达到从中插一脚的目的。简化后的流程为:

1、调用API地址所在处的指令

2、被改写的代码被执行(一般就是一个jmp指令,跳转到一个更开阔的地方去做更多的事情)

3、执行Hook

4、执行被覆盖的指令(一般是5个字节)

5、跳转回(API地址所在处+5个字节)的位置继续执行原代码

要完成这样一个流程已经很不容易。它需要你懂得80x86的汇编语言,而且要有很强的debug能力。但是这只是一个简化的流程,真正复杂的地方在于:

1、不可覆盖的代码的监测(比如jne就无法被覆盖)

2、Hook的可靠卸载

3、被覆盖指令长度的确定(一般是5个字节,但是那只是一般)

4、不同CPU指令集的支持(64位?)

5、Hook中调用了被Hook的API怎么办?

对于上述问题考虑的周到与否是衡量一个API Hook库的标准。很高兴的告诉你,EasyHook把上述问题都考虑进去了。而且依靠CLR Hosting API,EasyHook还实现了.NET的API钩子。原理就是EasyHook自己先把API调用拦截下来,然后用CLR Hosting API启动一个CLR的runtime,然后用runtime去加载一个GAC中的Assembly,然后用反射去调用你的代码,并且给你提供了足够的RuntimeInfo,使得你可以知道当前的环境,以及回调原始的API。不过仍然在Hook系统的API的时候,仍然要非常小心死锁的问题。

EasyHook也不是完美的。据我个人测试,它远程Hook .NET产生出来的exe有问题。不过这是它的Remote Injection部分的问题(很可能是CreateRemoteThread与CLR冲突导致的)。而且EasyHook确定指令长度那块内嵌的反汇编器来路不明(作者自述是从一个德文论坛上下载的程序反汇编来的),其实可以考虑用distorm代替。

其内部实现有一段关键的汇编代码叫trampoline(蹦床)。这段代码就是那被覆盖的5个字节要jmp到的一段代码,它负责调用你的Hook和善后。所以说如果你用的是.NET写的钩子,这是一个三级跳的过程:

1、应用程序调用API

2、API被调用(其实是我们的jmp指令被调用,因为头5个字节被覆盖了)

3、那个5个字节的jmp,跳转到了trampoline,这蹦床被jmp了,哈哈,所以弹了回去

4、trampoline跳转到了EasyHook的Thread Deadlock Barrier

5、EasyHook再用CLR Hosting API跳转到了我们的.NET代码

非常Hard Core。有机会给大家注释这段代码(我没告诉你EasyHook是开源的吗?),不过今天就写到这里了。

posted on 2008-10-31 23:08  taowen  阅读(7353)  评论(8编辑  收藏  举报