HOOK API 在多线程时应该注意的问题点

在使用INLINE HOOK API实现对系统API的拦截时,正常情况下并没有太大问题,但一旦涉及到多线程,不管是修改IAT还是JMP,2种方法均会出现不可预料的问题,特别是在HOOK一些复杂的大型系统软件时,会被时不时的一个内存错误搞得心浮气躁。

HOOK API数量越多,需要注意的内容越多,最近实现HOOK FileSystem API,由于是Kernel32中的函数,所有涉及到文件(filename,filehandle)的API均要被HOOK,同时需要在HOOK中实现overlapped功能,而这个功能点相对比较复杂,连带涉及到完成端口、事件等函数的HOOK。

经过上百个版本的更新调试,目前初步稳定下来,下面列出需要注意的地方,主要是与FileSystem API相关的,其他API(如网络API、内存API等)可能不太适用:

1)从稳定性方面考虑,建议使用JMP方式实现HOOK API,修改IAT的方式不是不可以,而是太容易被其他程序修改而导致不稳定的问题了,大部分杀毒软件均会对IAT进行修改同时保护,所以这种方法要么容易报病毒,要么HOOK失败,同时系统也会对IAT进行修补,对于小型系统或者小量API可能不出现问题,但是对于FileSystem API,个人不推荐。

2)对Vista、WIN7以上的兼容:一开始在XP中进行HOOK时,一切正常,当切换到WIN7后,发现HOOK不起作用,这是由于VISTA、WIN7以上的APP在原来调用Kernel32.DLL的函数时,由于系统AppPatch的功能,已被自动重定向到KernelBase.dll中,而微软有很奇怪的做法,当然估计也是由于兼容性的考虑(即在EXE右键属性中选择兼容XP,VISTA运行时,AppPatch会自动选择重定向哪些API),某些API同时存在于Kernel32和KernelBase中,而不知道是不是重定向功能的不完善还是其他什么原因,同一个App中两个模块即使调用同一个API,也有可能分别跳转到Kernel32和KernelBase中,适用JMP的方式进行HOOK API时,由于通过GetProcAddress获取函数地址,所以均会被自动重定向到KernelBase中,因此需要自己实现一个GetProcAddress函数。

3)多线程同步问题:个人建议使用临界区RTL_CRITICAL_SECTION,不管是开发效率还是运行效率,临界区是首选,对于HOOK后的API中使用不同进程资源的情况,也是在使用临界区进行同步处理后,再去使用其他方法实现的。

4)多线程HOOK后经常卡死的问题,WINDOWS的FileSystem API存在嵌套调用的问题,如CopyFile内部会调用CreateFile等函数,Kernel32的CreateFile会调用KernelBase中的CreateFile,GetFileSize函数内部可能会调用GetFileSizeEx的函数等等,如果在这些函数中同时使用一个内核对象进行同步,必定导致卡死,因此需要考虑这些问题,比如在调用CopyFile时,需要和CreateFile使用不同的内核对象进行同步,而在Kernel32的CreateFile中,需要对KernelBase的CreateFile进行解除HOOK。

5)在HOOK和UNHOOK过程中,需要使用同一个内核对象对WriteProcessMemory进行同步保护,否则将导致进程中某些不可预料的地址内存数据损坏的问题(这个应该是WriteProcessMemory函数内部不支持多线程导致的)。

6)在使用RTL_CRITICAL_SECTION或其他同步函数中,锁的进入和离开函数,尽量使用inline,减少SP的跳转可增加速度,可大大增加多线程切换寄存器的稳定性;

题外话:从硬件上讲CPU只有一套寄存器,但是对于系统运行环境来说,每一个线程(甚至是纤程,即协程或微线程)都有一套虚拟寄存器,这样才能实现不同线程切换时还原所谓的“CPU运行现场”,而每一次SP的跳转从目前各种编译器技术上来讲均会进行所谓的“现场保护”,也就是push各种寄存器,inline函数就是为了减少这种操作的,在于内核打交道的过程中,有些操作是可能被中断的,减少“运行现场”的切换次数必定是大大增强稳定性!

上一篇:【原】tomcat 7 启动报错:java.lang.NoSuchMethodError: javax.servlet.ServletContext.getSessionCookieConfig()Ljavax/servlet/SessionCookieConfig的解决


下一篇:java.lang.NoSuchMethodError: