DLL卸载的工作原理
使用CreateRemoteThread()API进行DLL注入的工作原理概括如下:
驱使目标进程调用LoadLibrary() API
同样,DLL卸载工作原理也非常简单:
驱使目标进程调用FreeLibrary() API
也就是说,将FreeLibrary() API的地址传递给CreateRemoteThread()的lpStartAddress参数,并把要卸载的DLL
的句柄传递给lpParameter参数。
实现DLL卸载
EjectDll.exe程序,用来从进程目标(notepad.exe)卸载指定的DLL文件,源码如下:
#include "windows.h"#include "tjhelp32.h"#include "tchar.h"#define DEF_PROC_NAME(L"notepad.exe")#define DEF_DLL_NAME (L"myhack.dll")DWORD FindProcessID(LPCTSTR szProcessName){DWORD dwPID=0xFFFFFFFF;HANDLE hSnapShot=INVALID_HANDLE_VALUE;PROCESSENTRY32 pe;//获取系统快照(Snapshot)pe.dwSize=sizeof(PROCESSENTRY32);hSnapShot=CreateToolhelp32Snapshot(TH32CS_SNAPALL,NULL);//查找进程Process32First(hSnapShot, &pe);do{if(!_tcs/Picmp(szProcessName, (LPCTSTR)pe.szExeFile)){dwPID=pe.th32ProcessID;break;}}while(Process32Next(hSnapShot, &pe));CloseHandle(hSnapShot);return dwPID;}BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege){TOKEN_PRIVILEGES tp;HANDLE hToken;LUID luid;if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)){_tprintf(L"OpenProcessToken error: %u\n", GetLastError());return FALSE;}if(!LookupPrivilegeValue(NULL, //lookup privilege on local systemlpszPrivilege, //privilege to lookup&luid)){ //receives LUID of privilege_tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError());return FALSE;}tp.PrivilegeCount=1;tp.Privileges[0].Luid=luid;if(bEnablePrivilege)tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;elsetp.Privileges[0].Attributes=0;//Enable the privilege or disable all privilegesif(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL)){_tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError());return FALSE;}if(GetLastError()==ERROR_NOT_ALL_ASSIGNED){_tprintf(L"The token does not have the specified privilege. \n");return FALSE;}return TRUE;}BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName){BOOL bMore=FALSE, bFound=FALSE;HANDLE hSnapshot,hProcess, hThread;HMODULE hModule=NULL;MODULEENTRY32 me={sizeof(me)};LPTHREAD_START_ROUTINE pThreadProc;//dwPID=notepad进程ID//使用TH32CS_SNAPMODULE参数,获取加载到notepad进程的DLL名称hSnapshot=CreateToolhelp32Snapshot(TH32CS-SNAPMODULE, dwPID);bMore=Module32First(hSnopshot, &me);for(; bMore; bMore=Module32Next(hSnapshot, &me)){ //对dll进行比较if(!_tcsicmp((LPCTSTR)me.szModule, szDllName) || !_tcsicmp((LPCTSTR)me.szExePath, szDllName)){ //szModule代表DLL的名称bFound=TRUE;break;}}if(!bFound){CloseHandle(hSnapshot);return FALSE;}//获取目标进程的句柄if(!(hProcess=OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID))){_tprintf(L"OpenProcess(%d) failed !!! [%d]\n", dwPID, GetLastError());return FALSE;}//获取FreeLibrary() API地址hModule=GetModuleHandle(L"kernel32.dll");pThreadProc=(LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");//在目标进程中运行线程hThread=CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL); //pThreadProc为FreeLibrary() API的地址,me.modBaseAddr是要卸载的DLL的加载地址WaitForSingleObject(hThread, INFINITE);CloseHandle(hThread);CloseHandle(hProcess);CloseHandle(hSnapshot);return TRUE;}int _tmain(int argc, TCHAR* argv[]){DWORD dwPID=0xFFFFFFFF;//查找processdwPID=FindProcessID(DEF_PROC_NAME);if(dwPID==0xFFFFFFFF){_tprintf(L"There is no %s process!\n", DEF_PROC_NAME);return 1;}_tprintf(L"PID of \"%s\" is %d\n", DEF_PROC_NAME, dwPID);//更改privilegeif(!SetPrivilege(SE_DEBUG_NAME, TRUE))return 1;//eject dllif(EjectDll(dwPID, DEF_DLL_NAME))_tprintf(L"EjectDll(%d, \"%s\") success!!!\n", dwPID, DEF_DLL_NAME);else_tprintf(L"EjectDll(%d, \"%s\") gailed!!!\n", dwPID, DEF_DLL_NAME);return 0;}
卸载DLL的原理是驱使目标对象自己调用FreeLibrary() API,上述代码中的EjectDll()函数就是用来卸载DLL的。
获取进程中加载的DLL信息
hSnapshot=CreateToolhelp32Snapshot(TH32CS-SNAPMODULE, dwPID);
使用CreateToolhelp32Snapshot() API可以获取加载到进程的模块(DLL)信息。将获取的hSnapshot句柄传递
给Module32First()/Module32Next()函数后,即可设置与MODULEENTRY32结构体相关模块信息。代码24-2为该结构体的定义。
typedef struct tagMODULEENTRY32{DWORD dwSize;DWORD th32ModuleID;DWORD th32ProcessID;DWORD GlblcntUsage;DWORD ProccntUsage;BYTE * modBaseAddr;DWORD modBaseSize;HMODULE hModule;char szModule[MAX_MODULE_NAME32 + 1];char szExePath[MAX_PATH];} MODULEENTRY32
