基于Detours的Windows API Hook实现

 

运行平台:Windows10 64位(事实上NT内核的系统都可以)

监控进程:32位的进程

主要方法:Detours库 + 创建远程线程 + Socket

核心代码:

image-20190518125031601

[TOC]

一、Detours库

Detours是微软提供的开源库,提供了可以用于hook系统API的工具。

基本原理是:Detours将Target函数前几个指令替换为一个无条件跳转,跳转到用户定义的Detours函数。被拦截的函数保存在Trampoline函数中,此外Trampoline函数还保存了Target函数所移除的指令以及一个无条件跳转,可以跳转到Target函数未被移除的部分(执行部分)。当执行完Target函数时,会跳转到用户定义的Detours函数。Detours函数执行适当的代码后返回,或调用Trampoline函数,将流程返回到拦截之前;或直接返回。下图展示了拦截前后的执行流程:

image-20190518125823257

相应的Trampoline函数与Target函数变化过程:

image-20190518125850112

二、实现DLL注入

DLL可以在进程运行时动态的加载,DLL的DllMain函数会在DLL第一次被加载时执行,所以,将钩子函数写入DllMain中,再将DLL注入目标程序,挂钩就成功了。

DLL注入目前常用方法有以下几种:

1、修改注册表来注入DLL

只有调用了User32.dll的进程才有这种注入,某些CUI程序无法完成此注入。另外,这种注入是在进程的早期进行的,因此在调用其他DLL的函数时,可能由于所调用的DLL还未载入,从而导致蓝屏。

2、使用CreateRemoteThread函数对运行中的进程注入DLL

Windows提供的CreateRemoteThread函数,可以让我们在另一个进程中创建一个线程。并在所创建的线程中执行LoadLibrary函数来加载DLL,从而实现DLL注入。此方法复杂,要求知识较高,但有更好的灵活性和可靠性。

3、使用SetWindowsHookEx函数对应用程序挂钩(HOOK)迫使程序加载DLL

该方法虽然简单,但是只能针对Windows消息进行注入。而且要等相应类型的事件发生才能完成注入,不能立即进行注入。

4、替换应用程序一定会使用的DLL

这是很多木马程序常常选择的方法,有一个很大的缺点在于:如果后续程序更新导致替换掉的DLL增加了很多新的导出函数,而我们注入的DLL没有及时为这些新增的导出函数添加转发器,将会导致相应程序不能正常运行。

5、把DLL作为调试器来注入

利用OllyDbg工具调试目标进程,把DLL作为调试器来注入。需要编写一些汇编指令,也要根据不同的CPU平台量身定做。

在综合比较了以上各方法之后,我们最终决定通过CreateRemoteThread实现DLL注入。通过CreateRemoteThread注入DLL的过程如下:

image-20190518130008457

三、进程间通信

在注入DLL后,可以通过GetCurrentProcessId函数获取当前进程(即被监控进程)的PID。然后通过Socket进行进程间通信,将获取到的信息传到我们定义的监控进程中,进而写到数据库中,作为后面机器学习模型的输入数据。

关于进程间通信,常用方法有:

1、命名管道

进程间通信的常用方法,可以在许多并不相关的进程之间进行通讯。依赖Windows平台实现,可重定向至TCP/IP,NetBUEI,NETBIOS等协议。因此应用程序无需关心具体的实现细节,一切交给Windows来处理。

2、消息队列

实现较为简单,但是可能导致系统的可用性降低、复杂度提高,以及一致性问题。

3、共享内存

编程难度大,无经验的程序员难以同步数据,容易造成数据混乱。

4、套接字(Socket)

Socket可用于不同主机间的进程通信,也可用于同一主机间的不同进程通信,且通信是双向的。

比较后发现,命名管道和Socket都可以用于此项目中。在多个进程等待通信时,命名管道要手工写并发调度,而TCP协议会自动阻塞,更便于处理。所以这里选择了了TCP协议,也即Socket这一进程间通信方式。