DLL卸载

喜你入骨 提交于 2020-02-19 16:14:00

DLL卸载的工作原理

主要来说与DLL注入类似,DLL注入是驱使目标进程调用LoadLibrary() API,DLL卸载则是驱动目标进程调用FreeLibrary() API:

  • 将FreeLibrary() API的地址传递给CreatRemoteThread()的lpStartAddress参数
  • 将要卸载的DLL的句柄传递给lpParameter参数

引用指数:记录DLL被调用的次数,卸载时也要卸载相同的次数。

实现DLL卸载

  • EjectDll.exe
#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;
}

使用FreeLibrary()的方法仅适用于卸载自己强制注入的DLL文件,PE文件直接导入的DLL文件是无法在进程运行过程中卸载的。

参考文献

《逆向工程核心原理》

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!