原理

LoadLibraryW

LoadLibraryW函数用于将指定模块加载到调用者进程,函数原型如下

HMODULE
WINAPI
LoadLibraryW(
    _In_ LPCWSTR lpLibFileName
    );

CreateRemoteThread

CreateRemoteThread函数用于在指定进程创建远程线程,并且可以指定起始地址和参数等,函数原型如下:

HANDLE
WINAPI
CreateRemoteThread(
    _In_ HANDLE hProcess,
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ SIZE_T dwStackSize,
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,
    _In_opt_ LPVOID lpParameter,
    _In_ DWORD dwCreationFlags,
    _Out_opt_ LPDWORD lpThreadId
    );

注入过程分析

了解了以上两个函数以后,可以发现:用于实现DLL加载的函数LoadLibraryW需要一个DLL路径作为参数,而CreateRemoteThread又正好可以在其他进程的指定地址处创建一个线程,并且附带一个参数。那么,我们是否可以在注入器进程调用CreateRemoteThread,指定在目标进程的LoadLibraryW函数处创建线程,并且附带一个地址作为参数,该地址是我们要注入的DLL的路径,这样,目标进程就会以DLL路径为参数调用LoadLibraryW

注意,CreateRemoteThread附带的参数(DLL路径的地址)必须是目标进程空间的地址。因为我们是在目标进程创建线程,因此该线程只能访问目标进程的数据,也就是说,我们通过创建远程线程在目标进程调用LoadLibraryW的时候,传递的参数(DLL路径的地址)对于目标进程必须是一个有效的地址,该地址存储的是DLL路径。因此,我们需要提前将DLL的路径写到目标进程中。我们需要在目标内存分配一块内存并写入DLL路径,这可以通过VirtualAllocEx和WriteProcessMemory实现

完整代码

理清了思路,现在开始写代码。

为了突出核心原理,本文默认每个函数都成功执行,没有提供非必要的错误校验,读者可自行添加

注入器

#include <Windows.h>
#include <tlhelp32.h>
#include <iostream>

// 替换为你的目标进程ID
DWORD targetProcessId = 0;

// 替换为你的DLL路径
LPCWSTR dllPath = L"D:\\KrnlsYs.dll";

int main()
{
	// 获取目标进程的句柄
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetProcessId);

	// 分配内存以存储DLL路径
	PVOID targetPathBuffer = VirtualAllocEx(
		hProcess,
		0,
		(lstrlenW(dllPath)+1) * sizeof(WCHAR),
		MEM_COMMIT | MEM_RESERVE,
		PAGE_READWRITE
	);

	// 将DLL路径写入目标进程的内存
	WriteProcessMemory(hProcess,
		targetPathBuffer,
		dllPath,
		(lstrlenW(dllPath) + 1) * sizeof(WCHAR),
		nullptr
	);

	// 创建远程线程以加载DLL
	HANDLE hThread=CreateRemoteThread(hProcess,
		nullptr,
		0,
		(LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"),
		targetPathBuffer,
		0,
		nullptr
	);

	// 等待远程线程完成
	WaitForSingleObject(hThread, INFINITE);

	// 清理所有资源
	CloseHandle(hThread);
	VirtualFreeEx(hProcess, targetPathBuffer, 0, MEM_RELEASE);
	CloseHandle(hProcess);

	return 0;
}

示例DLL

BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		MessageBoxW(0, L"Hello KrnlsYs.dll", L"Msg:", 0);
		break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

测试运行

测试前请注意:

  • 修改注入器的targetProcessId和dllPath变量,以正确设置你的目标进程和DLL
  • 以管理员身份运行注入器

测试结果:成功注入

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐