远程线程DLL注入, 如何释放DLL和结束DLL的线程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了远程线程DLL注入, 如何释放DLL和结束DLL的线程相关的知识,希望对你有一定的参考价值。

DLL是MFC的DLL, 我在这个DLL的初始化函数中创建了一个对话框窗口, 如何在dll中操作实现结束dll当前的线程和释放掉dll. 比如在窗口(模态对话框)关闭后结束线程, 释放dll.

大神来指点指点吧.大神

kernel32.dll里有个函数叫FreeLibraryAndExitThread
就是专门给你做这种事情用的:

正常情况下你调用FreeLibrary来释放当前执行的代码所在的DLL会导致FreeLibrary返回以后无法继续执行之后的代码(DLL已经释放了)
而这个函数会在FreeLibrary之后结束当前线程,这个操作的代码在kernel32.dll中,所以不存在上述问题
参考技术A 注入的dll不能自己释放自己吧,线程到是可以在dll内终止,释放的话还得重复一次类似注入的操作,远程释放,要不就等进程终止。不过有这必要么?

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注入, 如何释放DLL和结束DLL的线程的主要内容,如果未能解决你的问题,请参考以下文章

Win32 利用远程线程注入dll

Win32 利用远程线程注入dll

安全之路 —— 无DLL文件实现远程线程注入

易语言线程注入

详细解读:远程线程注入DLL到PC版微信

Dll注入:X86/X64 远程线程CreateRemoteThread 注入