本节内容
DLL卸载是将强制插入进程的DLL弹出的一种技术,其基本原理用CreateRemoteThread API进行DLL注入的原理类似,区别是DLL注入时CreateRemoteThread 的回调函数是LoadLibrary API 而DLL卸载CreateRemoteThread 的回调函数是FreeLibrary
参考书:《逆向工程核心原理》
源码行为剖析
EjectDll -> main
1 | // EjectDll.exe |
main -> FindProcessID
main函数先是使用 API FindProcessID找到目标进程的pid,找到了对进程名称于pid进行输出,FindProcessID是在EjectDLL.exe源码中定义:
1 | DWORD FindProcessID(LPCTSTR szProcessName) |
在FindProcessID内部会使用APi CreateToolhelp32Snapshot返回系统中
1 | HANDLE CreateToolhelp32Snapshot( |
Value | Meaning |
---|---|
TH32CS_SNAPALL | Includes all processes and threads in the system, plus the heaps and modules of the process specified in th32ProcessID. Equivalent to specifying the TH32CS_SNAPHEAPLIST,TH32CS_SNAPMODULE, TH32CS_SNAPPROCESS, andTH32CS_SNAPTHREAD values combined using an OR operation (‘|’). |
CreateToolhelp32Snapshot会返回获取到的进程快照对应的句柄
接着使用Process32First & Process32Next遍历CreateToolhelp32Snapshot得到进程快照内容,依次赋给PROCESSENTRY32 pe
,接下来从pe中获取当前的进程快照块中名称是否为目标进程“notepad.exe”,PROCESSENTRY32结构体原型如下:
1 | typedef struct tagPROCESSENTRY32 { |
若配对成功说明当前进程快照块是目标进程notepad.exe,则取他的dwPID = pe.th32ProcessID
进行FindProcessID return
main -> SetPrivilege
回到main函数内部,会执行权限提升(后续操作需要),SetPrivilege(SE_DEBUG_NAME, TRUE)
,后续在补充,这里只需要先记住经过系统权限调整使得后续从目标进程弹出DLL的操作满足权限要求即可。
main -> EjectDll
随后进行关键的DLL弹出函数EjectDll(dwPID, DEF_DLL_NAME)
,参数是目标进程对应的PID,以及即将从目标进程中卸载的dll名称
1 | BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName) //待操作的进程以及将要卸载的dll |
先调用CreateToolhelp32Snapshot API,在前面的分析也调用过,该API描述如下:
1
2
3
4 HANDLE CreateToolhelp32Snapshot(
[in] DWORD dwFlags,
[in] DWORD th32ProcessID
);Takes a snapshot of the specified processes, as well as the heaps, modules, and threads used by these processes.
对指定的进程拍摄快照,保存进程相关信息的使用情况:诸如堆使用情况、模块载入情况、线程等
CreateToolhelp32Snapshot 第一个参数dwFlags,指明那些信息应该被包括进快照
Value | Meaning |
---|---|
TH32CS_SNAPMODULE0x00000008 | Includes all modules of the process specified in th32ProcessIDin the snapshot. To enumerate the modules, see Module32First. If the function fails with ERROR_BAD_LENGTH, retry the function until it succeeds.64-bit Windows: Using this flag in a 32-bit process includes the 32-bit modules of the process specified in th32ProcessID, while using it in a 64-bit process includes the 64-bit modules. To include the 32-bit modules of the process specified in th32ProcessID from a 64-bit process, use the TH32CS_SNAPMODULE32 flag. |
因此语句:hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
的含义是返回目标进程dwPID载入的所有模块信息的句柄存放到hSnapshot中。
接着EjectDll函数执行下面的代码
1 | bMore = Module32First(hSnapshot, &me); |
遍历hSnapshot 句柄中的所有模块,依次放入bMore,进行判断(路径 & dll名称都要准确),看是否是我们将要从目标进程中卸载的dll,贴一下(me变量)MODULEENTRY32结构体,这一步结束me中存放的是待卸载dll的信息
1 | typedef struct tagMODULEENTRY32 { |
接下来hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID
尝试以PROCESS_ALL_ACCESS权限打开目标进程
获取系统的kernel32.dll中的FreeLibrary API地址,调用CreateRemoteThread,设置回调函数以及传递参数,在线程中调用FreeLibrary 完成对目标进程中目标dll的卸载工作。
回顾
API– CreateToolhelp32Snapshot,对进程在系统中的信息执行快照
1 | HANDLE CreateToolhelp32Snapshot( |
文中两次调用中第一个参数dwFlags有所区别:第一次 = TH32CS_SNAPALL,第二次 = TH32CS_SNAPMODULE,获取到的内容也不同。详情查看微软文档API如预期的执行会返回相应的句柄,可以对进行遍历。
第一次调用是为了找到目标进程PID,因此获取到的句柄时系统中所有进程的快照,使用类似如下代码遍历:
1 | //hSnapShot是CreateToolhelp32Snapshot返回的句柄,其中存放进程快照信息 |
第二次调用是在第一次执行获取了目标进程的PID后需要从其载入的DLL中找到待卸载DLL,使用类似如下代码遍历:
1 | //hSnapshot存放CreateToolhelp32Snapshot返回的句柄,这次是目标进程中载入的所有模块快照信息 |
本节内容主要的内容在于查找目标进程以及目标进程中待卸载的DLL,真正完成卸载的部分并不算有难度。