首先,读这篇文章之前,默认已经掌握了进程地址空间,dll加载,windows Hook技术。
1. 为什么需要dll注入?
如果一个进程的程序是我们自己编写的,我们可以在程序中隐式或者显式地加载需要的dll,不需要dll注入。但是,当一个进程的程序不是我们编写的,而我们又需要该程序加载指定的dll,以便进行某些操作,这是就需要dll注入。
我们知道,dll可以被多个进程加载,当一个进程加载dll时,dll被映射到该进程的地址空间。dll注入的最大目的,进入目标进程的地址空间,这样就可以操作目标进程中的对象了。
2. 通过Hook将dll注入进程
Hook技术是基于windows消息机制的。如果给某个消息安装了Hook,当该消息发生时,会先调用Hook函数。下面是设置Hook的函数原型:
HHOOK WINAPI SetWindowsHookEx(
_In_ int idHook,
_In_ HOOKPROC lpfn,
_In_ HINSTANCE hMod,
_In_ DWORD dwThreadId
);
该函数的详细细节可以参考msdn:http://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx
第一个参数idHook,表示要安装的Hook的类型,即我们要在哪个消息上安装Hook,也可以理解为消息类型。
第二个参数lpfn,是一个函数指针,即窗口处理idHook指定类型的消息时,先调用该函数。
第三个参数hMod,lpfn函数所在的模块,即lpfn所在dll的句柄。
第四个参数dwThreadId,需要在哪个线程上安装Hook。如果为0,则在所有线程上安装。注意dwThreadId是系统级变量,也就说可以是任何进程中的任何线程。
通过以上函数安装Hook后,对整个系统大致产生如下效果:当Id为dwThreadId的线程(当dwThreadId=0时,指系统中所有线程)中有消息类型为idHood的消息需要处理时,先调用hMod模块中的lpfn函数。
现在我们需要应对以下情景:我们需要将MyDll.dll注入到进程B中。注入之前,我们要确定进程B中使用了windows消息,因为我们即将采用Hook的方法来注入dll,如果进程B中没有消息,就不会调用Hook函数,无法注入。我们在MyDll.dll中export了GetMsgProc函数,该函数将被作为Hook函数。注入工作在我们自己编写的程序中完成,我们自己变成的程序为进程A。需要涉及到:进程A、进程B、MyDll.dll、GetMsgProc。
由于进程A是我们自己编写的,因此进程A可以载入MyDll.dll,假设载入后的模块句柄为hInstDll。于是在进程A中可以使用SetWindowsHookEx函数了,
HHook hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hInstDll, 0);
WH_GETMESSAGE是Hook类型,csdn解释如下:Installs a hook procedure that monitors messages posted to a message queue,即只要有消息被post到消息队列,Hook函数就会被调用。
最后一个参数是0,我们不知道B进程中的线程,所以干脆设定为所有线程(这样做会损失系统其他程序的一些性能,其他不需要改dll的程序,也会加载)。
现在来看看B进程的执行情况:
- 进程B中的一个线程准备向一个窗口post一条消息。
- 系统检查该线程是否已经安装了WH_GETMESSAGE Hook,检查结果为确实安装了这种Hook。
- 进程B准备调用Hook函数,即GetMsgProc函数。但是该函数在MyDll.dll中,而MyDll.dll并没有加载到进程B。
- 进程B加载MyDll.dll。
- 调用GetMsgProc函数。
当然,还有很多复杂的步骤。我们关心的dll注入在第四步完成了。这就是采用Hook来注入dll的技术。dll一旦被注入进程B以后,该dll内的所有函数(不仅仅是GetMsgProc函数)都可以被进程B中的所有线程调用。
3. dll注入的作用
相信喜欢玩游戏的都知道有个东西叫外挂,很多外挂就是采用dll注入的方法,进入到游戏进程中,修改进程中的某些数据(比如金币、攻击力、防御力等)。dll注入还可以进行API拦截。
结束语:一句话,如果要进入一个进程的地址空间,可以考虑dll注入。