windows核心编程之DLL注入

来源:互联网 发布:锐捷交换机查mac 编辑:程序博客网 时间:2024/06/05 07:02

一.前言

        DLL注入的酷炫之处在于无需目标程序的源码,就可向目的程序注入代码。

        要理解DLL注入要先理解好进程的虚拟地址空间,每个进程都有自己独立的虚拟地址空间,一般来说,两个进程的虚拟地址空间是互相隔绝的,进程A不能访问到进程B的虚拟地址空间,进程A的虚拟地址空间被破坏也不会影响到其他进程的虚拟地址空间。

        windows核心编程中用一个例子来说明DLL注入的使用:一个进程想从另一个进程创建的窗口实例来派生一个子类窗口。

        windows中,要派生子类窗口,必须调用SetWindowLongPtr接口让父窗口的窗口过程指向新的子类窗口的窗口过程(WndProc)。如果用下面的代码调用SetWindowLongPtr来从一个窗口派生子类窗口,那所有发到或者发往hWnd窗口的消息,应用由MySubclassProc来处理,而不是由该窗口的标准窗口过程来处理:

SetWindowLongPtr(hWnd , GWLP_WNDPROC,MySubclassProc) ;

        换句话说,当系统需要向指定的WndProc派送消息的时候,会先查看窗口过程的地址,然后通过该地址来调用WndProc,在前面的例子中,由于系统发现与该窗口相关联的窗口过程地址是MySubclassProc,因此会直接调用MySublassProc。

        现在一个进程要向另一个进程的窗口派生子类窗口的问题是,两个进程的地址空间时隔离的,子类窗口的窗口过程并不能被父窗口调用。这里就是DLL注入的使用场景了,我们可以将MySubclassProc放在一个Dll中实现,然后将该DLL注入到父窗口所在的进程中,这时子类窗口的窗口过程就进入了父窗口的进程地址空间中了,这时就能在父窗口进程中通过调用SetWindowLongPtr中派生一个子类窗口了。


        Windows中几种常用的方式来注入DLL:

        (1)使用注册表来注入DLL

        (2)使用Windows挂钩来注入DLL

        (3)使用远程线程来注入DLL

        (4)使用木马DLL来注入DLL

        (5)把DLL作为调试器来注入

        (6)对子进程进行DLL注入

        其中(2)和(3)是比较通用的方法,本篇文章只对这两种方式进行详细讲解。


二.使用Windows挂钩来注入DLL

        首先看一下windows挂钩的使用方法:

        进程A调用了挂钩安装函数:

HHOOK hHook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hInstDll,0);

        其中第一个参数表示安装的挂钩类型,这里安装的是WH_GETMESSAGE挂钩,该挂钩是一个windows窗口消息挂钩。如果一个窗口安装了该挂钩,在该窗口处理一条消息时,系统应该调用挂钩过滤函数。挂钩过滤函数即为第二个参数GetMsgProc。第三个参数标识了一个DLL,即GetMsgProc所在的DLL,在windows中,hInstDll的值是进程地址空间中DLL被映射的虚拟内存地址。最后一个参数表示要给哪个线程安装挂钩,如果该参数为0,表示要给系统中的所有GUI线程安装挂钩。

        假设进程A给进程B的某个线程安装了WH_GETMESSAGE挂钩,则该挂钩的工作过程如下:

        (1)进程B中的一个线程准备向一个窗口派送一条消息;

        (2)系统检测到该线程已经安装了WH_GETMESSAGE挂钩,所以会先调用一次挂钩过滤程序;

        (3)应该挂钩过滤程序所在的DLL还没载入到进程B的地址空间中,所以系统会强制将该DLL注入到进程B的地址空间中。(这里就是DLL注入的发生时机

        (4)当DLL被注入到进程B的地址空间时,也会得到一个hInstDll,即DLL在进程B中的虚拟地址空间,系统会对它进行检查,看是否与该DLL在进程A中的位置相同(在前面进程A给进程B安装挂钩时传的hInstDLL)。如果相同,则在两个进程地址空间中,GetMsgProc的地址是一样的,否则的话,系统可以根据下面的公式推导出GetMsgProc函数在进程B的虚拟地址空间中的位置:

             GetMsgProc B = GetMsgProcA + (hInstDll B - hInstDll A) ;

       (5)系统在进程B的地址空间中调用挂钩过滤函数GetMsgProc() ;


        理解了windows挂钩的使用和工作工程后,现在来看看如何用windows挂钩解决前面的问题:在其他进程的窗口实例中派生一个子类窗口:

我们可以先给创建窗口的线程设置一个WH_GETMESSAGE的挂钩,然后当挂钩过滤函数GetMsgProc被调用时,我们就可以在GetMsgProc函数内调用SetWindowLongPtr来派生子类窗口。当然,子类窗口的窗口过程必须和GetMsgProc在同一个Dll中。


        当我们不需要该挂钩和DLL的时候,可以调用下面的windows接口来撤销挂钩和DLL的映射:

BOOL UnhookWindowsHookEx(HHOOK hHook);


        最后,如果想了解windows挂钩的详细信息,可看看MSDN中的相关介绍:

        https://msdn.microsoft.com/en-us/library/windows/desktop/ms632589(v=vs.85).aspx


三.使用远程线程来注入DLL

        使用远程线程来注入DLL是最灵活的一种方式。从根本上来说,DLL注入要求目标进程中的一个线程调用LoadLibrary来载入我们想要的DLL。在理解远程线程DLL注入的原理之前,我们必须先明白一点:

        大多数windows接口只允许调用进程操作自己的进程空间,这可以防止一个进程破坏另一个进程。但是windows也提供了一些函数来让一个进程对另一个进程进行操作,这些接口都有一个共同的参数Handle hProcess,用来指定要操作的进程。例如:

       

 CreateRemoteThread(HANDLE hProcess,...);  //在一个指定的进程中创建一个新的线程。        VirtualAllocEx(HANDLE hProcess,...); //在一个指定的进程的虚拟地址空间中分配一块内存        VirtualFreeEx(HANDLE hProcess,...) ; //在一个指定的进程的虚拟地址空间中释放内存        ReadProcessMemory(HANDLE hProcess,...);//对指定的进程的地址空间进行读        WriteProcessMemory(HANDLE hProcess,...);//对指定的进程的地址空间进行写

        远程线程注入DLL就是靠上面的几个接口来实现的。原理就是我们在自己的进程中为其他进程创建一个新的线程,并把该线程的起始函数设为LoadLibrary,线程的参数为DLL的路径。假设在进程A中,需要对进程B进行MyLib.dll注入,进程B的HANDLE为hProcessB,这在进程A中的代码类似于:

PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("kernel 32")),"LoadLibraryW");HANDLE hThread = CreateRemoteThread(hProcessB,NULL,0,pfnThreadRtn,L"C:\\MyLig.dll",0,NULL);

        当新线程在进程B中被创建的时候,会立即调用LoadLibraryW(),并传入DLL路径名的地址,这时DLL的注入就完成了,在DLLMain()函数里面我们就得到了线程的运行控制权。

        但是,上面的代码有一个问题,我们在进程A中调用的CreateRemoteThread()函数,其中把DLL的路径用字符串地址传进去,但是这个字符串地址是在进程A的地址空间的,当创建的线程在进程B的地址空间运行时,其按这个地址去取DLL的路径,实际上是不能取到字符串的,因为这时是在进程B中的地址空间中,我们并没有玩进程B的地址空间中该位置写入字符串,所以这个访问可以导致访问违规异常,进程B被终止。

        为了避免这种情况,我们需要先向进程B的地址空间的某个地址写入DLL的路径,然后在调用CreateRemoteThread的时候传入该地址。上面介绍的几个接口可以实现这个需求:

        先调用VirtualAllocEx()在进程B中分配一个虚拟内存,再用WriteProcessMemory()向该地址写入DLL的路径。当不需要该块内存时,可以用VirtualFreeEx来释放内存。

        最后,当我们不再需要该DLL的时候,我们同样通过创建一个线程,并让线程调用FreeLibrary()函数来释放DLL。


        《windows核心编程》中对这两种DLL注入方式还有对应的程序例子,会在下一篇文章<windows核心编程之DLL注入例子分析> 进行讲解。

















 
原创粉丝点击