DLL注入和API拦截
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DLL注入和API拦截相关的知识,希望对你有一定的参考价值。
22.4 使用远程线程来注入DLL
22.4.1 概述
(1)远程线程注入是指一个进程在另一个进程中创建线程,然后载入我们编写的DLL,并执行该DLL代码的技术。其基本思路是通过CreateRemoteThread创建一个远程线程,并将LoadLibrary函数作为该线程函数来启动线程,同时将Dll文件名作为线程函数的参数传入。大致执过程如下:CreateRemoteThread()→LoadLibrary()→DllMain()。
(2)核心函数:CreateRemoteThread
参数 |
说明 |
HANDLE hProcess |
要创建远程线程的进程句柄。除了这参数外,CreateRemoteThread与CreateThread函数参数含义完全相同! |
PSECURITY_ATTRIBUTES psa |
用于定义新线程的安全属性,这里设为NULL采用默认值即可 |
DWORD dwStackSize |
初始化线程堆栈大小,NULL为默认大小 |
PTHREAD_START_ROUTINE pfnStartAddr |
线程函数的地址,这里传入LoadLibrary函数的地址,但它须在远程进程的地址空间中,因为它是让远程线程调用的。如何获得远程进程中这函数地址,可参考后面内容。 |
PVOID pvParam |
线程函数参数,这里一般是DLL文件名,但这个名称须保存在远程进程的地址空间中,这也是个比较棘手的问题。 |
DWORD fdwCreate |
函数表示创建线程后线程的运行状态 |
PDWORD pdwThreadId |
返回线程ID,不关心可以设为NULL不返回 |
备注:使用这个函数关键要解决三个参数问题:①获得远程线程的进程句柄,而且要确保相应权限(如Debug权限);②获取远程进程中线程函数的开始地址,而非本地地址;③向远程线程成功传入DLL路径字符串。 |
(3)其他函数
①在远程进程中分配/释放内存:VirtualAllocEx/VirtualFreeEx
②对远程进程地址空间进行读写:ReadProcessMemory/WriteProcessMemory
22.4.2 获取LoadLibrary函数的远程地址
(1)LoadLibrary要作为远程线程函数来使用,必须满足两个条件:
①该函数符合线程函数的原型。(查看MSDN,他们有相同的调用约定,都有一个参数和一个返回值。至于类型不同,可以通过强转类型得到,所以该条件满足)
②该函数存在于远程线程地址空间内。这一点也可以保证的,因为LoadLibrary函数位于Kernel32.dll中,对于Windows系统而言,本地进程和远程进程中的Kernel32.dll被映射到地址空间的同一内存地址,因而只要通过GetProcAddress获取本地进程中LoadLibrary的地址,在远程进程中也同样是这个地址,可以直接传给CreateRemoteThread。
(2)LoadLibrary是被定义为一个宏,而不是函数。有两个版本LoadLibraryA和LoadLibraryW。
(3)为什么CreateRemoteThread的pfnStartAddr参数不能直接写成LoadLibrary W(或A),而要使用GetProcAddress获得的LoadLibary函数的地址。
①LoadLibrary W(或A)是Kernel32.dll中的一个导出函数,但我们的Dll中直接引用该函数时,会在Dll的导入表记录下来。我们都知道导入函数的真实地址是在DLL加载的时候才能确定的,加载程序会从导入表中取得导入函数名,在被加载到进程地址空间后,会计算出该函数地真实地址,然后填入导入表(IAT)相应的位置。这种函数在编译期无法知道切确的地址,所以被编译成CALL DWORD PTR[XXXXXXXX]之类的代码,中括号中的数值虽然是一个确定的数值,但并不是导入函数的真实地址(形如CALL XXXXXXXX),而是一个子程序的地址,该程序被称为转换函数(Thunk)。【顺便说一下,这也是为什么在声明一个导入函数时要加上__declspec(dllimport)前缀的原因,因为编译器无法区分应用程序是对一般函数调用还是对导入函数调用。当加上这个前缀时,编译器会认为此函数来自导入函数,就会产生CALL DWORD PTR[XXXXXXXX]的指令,而不是CALL XXXXXXXX。】
②当程序调用导入函数时,编译器会处理成先调用转换函数,然后转换函数从IAT表中获得导入函数的真实地址,再调用相应的地址。所以如果将CreateRemoteThread的pfnStartAddr参数写成LoadLibraryW(或A),这里地址将被编译成转换函数的地址,而不是LoadLibrary的真实地址。
22.4.3 将DLL的路径字符串存放到远程的地址空间中
(1)如果直接向CreateRemoteThread()传入DLL路径,如”C:\\MyDLL.dll”那么实际向远程线程传递的是一个本地的指针值,这个值在远程进程的地址空间中是没有意义的。
(2)可以使用VirtualAllocEx()函数在远程进程中先分配一段空间,然后再使用WriteProcessMemory将DLL路径字符串复制到远程进程的地址空间中去,最后将该远程内存的指针传给CreateRemoteThead相应的参数。
24.4.4 总结使用远程线程注入DLL的步骤
①用VirtualAllocEx函数在远程进程的地址空间中分配一块内存。
②用WriteProcessMemory函数反映Dll的路径名复制到第1步分配的内存中
③用GetProcAddress函数来得到LoadLibrary W(或A)函数在Kernel32.dll的真实地址。
④用CreateRemoteThread函数在远程进程中创建一个线程,让新线程调用正确的LoadLibrary函数并在参数中传入第1步分配的内存地址。这时,DLL己经被注入到远程进程的地址空间中,DLL的DllMain函数会收到DLL_PROCESS_ATTACH通知并县城可以执行我们想要执行的代码。当DllMain返回时,远程线程会从线程函数(LoadLibraryW/A)调用返回到线程启动函数RtlUserThreadStart(该函数的实现可参考第6章),最后调用ExitThread使远程线程终止。
⑤此时远程进程中那块在第1步分配的内存还在,DLL也还在远程进程的地址空间中。这里只需调用VirtualFreeEx就可以释放远程进程的内存。
⑥但DLL的释放,要先通过GetProcAddress获得FreeLibrary的地址,然后再通过CreateRemoteThread在远程进程中创建一个线程,让该线程调用FreeLibrary,pvParam参数传入远程DLL中句柄。
【InjectLibrary示例程序】
以上是关于DLL注入和API拦截的主要内容,如果未能解决你的问题,请参考以下文章