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 system
lpszPrivilege, //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;
else
tp.Privileges[0].Attributes=0;
//Enable the privilege or disable all privileges
if(!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;
//查找process
dwPID=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);
//更改privilege
if(!SetPrivilege(SE_DEBUG_NAME, TRUE))
return 1;
//eject dll
if(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