之前就玩过DLL注入,但是这次突发奇想的想把这几种常用的注入方式都结合在一起,也是方便自己对某些程序的调试更加方便,也是一次代码复用的机会。

目前DLL注入大概有以下几种注入方式:

  1. 钩子注入
  2. APC回调注入
  3. 远程线程注入
  4. 木马DLL注入
  5. IAT表注入(不知道这个算不算)
  6. 输入法注入
  7. 注册表注入

一、钩子注入

核心函数:SetWindowsHook()

有人提到过使用全局钩子,但是个人认为不怎么推荐,因为影响范围太广,有不可预料的情况发生。所以最好精确到线程。给线程设置消息钩子后,会捕捉键盘、鼠标等消息事件。一但检测到消息发生,就会回调DLL中的函数。也就是把DLL中的导出函数当作回调函数使用,DLL自然也加载了。

首先我们根据PID获取到TID,该钩子函数需求的是TID

//通过进程PID获取线程TID
DWORD GetTIDbyPID(DWORD PID)
{
	if (PID != NULL)
	{
		DWORD dwThreadID=NULL;
		THREADENTRY32 te32 = { sizeof(te32) };
		HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
		if (Thread32First(hThreadSnap, &te32))
		{
			do
			{
				if (PID == te32.th32OwnerProcessID)
				{
					dwThreadID = te32.th32ThreadID;
					break;
				}
			} while (Thread32Next(hThreadSnap, &te32));
		}
		wprintf(L"ThreadId:%d\n", dwThreadID);
		return dwThreadID;
	}
	else
		return NULL;
}

利用进程快照,进行线程遍历,判断线程所属,返回线程ID。

然后直接调用SetWindowsHook()方法了

//Hook 注入大法好
int HooksToInject(_TCHAR* DLLName, DWORD ProcessID)
{
	//wprintf(L"\n%s\n%d", DLLName, ProcessID);
	HHOOK InjectHook = NULL;
	HMODULE InjectDll = LoadLibrary(DLLName);
	if (InjectDll != NULL)
	{
		HOOKPROC InjectMethod = (HOOKPROC)GetProcAddress(InjectDll, "InjectHook");
		if (InjectMethod != NULL)
		{
			DWORD ThreadId = GetTIDbyPID(ProcessID);
			if (ThreadId!=NULL)
			{
				InjectHook = SetWindowsHookEx(WH_KEYBOARD, InjectMethod, InjectDll, ThreadId);
				if (InjectHook != NULL)
				{
					wprintf(L"%s\n", L"Hook Process Success!");
				}
				else
				{
					return GetLastError();
				}
			}
		}
		else
		{
			return GetLastError();
		}

	}
	else
	{
		return GetLastError();
	}
	wprintf(L"%s\n",L"Wait you unhook, press any key to unhook.");
	getchar();
	UnhookWindowsHookEx(InjectHook);
	return 0;

此处我监听的是WM_KEY键盘事件 当然你也可以监听 WM_MOUSE事件。首先用本程序加载动态链接库,获取到动态链接库中,我所导出函数的地址(InjectHook函数),每个链接库系统开机后都是相同的,所以本程序获取更方便一些,下面更是相同。

二、APC回调注入

所谓APC,即Asynchronous procedure call,异步程序调用。

关键函数:QueueUserAPC()

APC注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,其具体流程如下:
1)当EXE里某个线程执行到SleepEx()或者WaitForSingleObjectEx()时,系统就会产生一个软中断。
2)当线程再次被唤醒时,此线程会首先执行APC队列中的被注册的函数。
3)利用QueueUserAPC()这个API可以在软中断时向线程的APC队列插入一个函数指针,如果我们插入的是Loadlibrary()执行函数的话,就能达到注入DLL的目的。

不过这个和远程注入方法都是一样的,都需要一个调试进程的权限,才可以对其他进程进行内存空间的开辟等。所以首先提升权限:

int EnablePrivilege()
{
	//获取调试进程权限
	TOKEN_PRIVILEGES tp;
	HANDLE hToken;
	if (!LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid))
		return GetLastError();

	//Get current process's token
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
		return GetLastError();
	tp.PrivilegeCount = 1;
	tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	//Update process tonkn privilrges
	if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL))
		return GetLastError();
	CloseHandle(hToken);
	return 0;
}

获取进程权限令牌,获取进程自身LUID然后填充TOKEN_PRIVILEGES结构,修改TOKEN_PRIVILEGES的第一个数组Privileges[0].Attributes中的权限,然后更新权限。

其次调用QueueUserAPC()加入到回调队列。因为Loadlibrary当作回调函数使用,所以其参数需要写到被注入进程中,这就需要申请空间,也就有自身提权的必要性。

int APCCallbacktoInject(_TCHAR* DLLName, DWORD ProcessID)
{
	//提升进程权限
	if (int rtMsg = EnablePrivilege() != 0)
		return rtMsg;
	HANDLE hOProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID);
	if (hOProcess != NULL)
	{
		_TCHAR* pLibRemote = (_TCHAR*)VirtualAllocEx(hOProcess, NULL, 2 * wcslen(DLLName) + 1, MEM_COMMIT, PAGE_READWRITE);
		if (pLibRemote != NULL)
		{
			if (!WriteProcessMemory(hOProcess, pLibRemote, DLLName, 2 * wcslen(DLLName) + 1, NULL))
			{
				PAPCFUNC pAPCFuncAddr = (PAPCFUNC)GetProcAddress(GetModuleHandle(L"Kernel32"), "LoadLibraryW");
				DWORD dTid = GetTIDbyPID(ProcessID);
				if (dTid != NULL && pAPCFuncAddr != NULL)
				{
					HANDLE hoThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dTid);
					if (!QueueUserAPC(pAPCFuncAddr, hoThread, (ULONG_PTR)pLibRemote))
					{
						wprintf(L"%s\n", L"Inject sucessful!");
					}
				}
			}
		}
	}
	return 0;
}

三、远程线程注入

关键函数:CreateRemoteThread()

利用创建远程的线程,将LoadLibrary作为函数的回调函数,进行运行以达到加载的目的。当然因为LoadLibrary的参数需要在被注入进程中,需要分配空间用于存储,自然需要调试进程的权限,提取方法见上面的栗子(北京的栗子貌似不错)中—

int RemoteToInject(_TCHAR* DLLName, DWORD ProcessID)
{
	//DWORD ThreadId = GetTIDbyPID(ProcessID);
	//进程提取
	if (int rt = EnablePrivilege() != 0)
		return rt;
	HANDLE hOprocess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID);
	if (hOprocess != NULL)
	{
		_TCHAR* pLibFileRemote = (_TCHAR*)VirtualAllocEx(hOprocess, NULL, 2 * wcslen(DLLName) + 1, MEM_COMMIT, PAGE_READWRITE);
		if (pLibFileRemote != NULL)
		{
			if (!WriteProcessMemory(hOprocess, (void*)pLibFileRemote, DLLName, 2 * wcslen(DLLName) + 1, NULL))
				return GetLastError();

			//Get LoadLibraryW Address
			PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"Kernel32"), "LoadLibraryW");
			if (pfnStartAddr != NULL)
			{
				HANDLE hRemote = CreateRemoteThread(hOprocess, NULL, 0, pfnStartAddr, (PVOID)pLibFileRemote, 0, NULL);
				if (hRemote != NULL)
				{
					wprintf(L"%s\n", L"Inject sucessful!");
					WaitForSingleObject(hRemote, INFINITE);
					wprintf(L"%s\n", L"The inject was killed!");
					if (!VirtualFreeEx(hOprocess, pLibFileRemote, 0, MEM_RELEASE))
						return GetLastError();
				}
				else
					return GetLastError();
			}
		}
		else
			return GetLastError();
	}
	else
		return GetLastError();
	return 0;
}

其他三种,请见下回分解。目前写了一个开源项目,用于注入测试用的,代码就是文章所讲述的,可以参考完整项目:https://github.com/BackTrackCRoot/Auto-Inject-Dll/

页面下部广告

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

鲁ICP备17018668号-1