DLL注入——远程线程注入

xuanbin 224 次阅读 发布于 2025-06-22


最近看书看到了DLL注入,于是打算实现一下各种注入方式。

远程线程注入的思路大致是:定位注入目标进程——在目标进程申请一块内存——将要注入的dll的路径写入目标进程内存——获取LoadLibraryW API的地址————在目标进程中运行线程。

定位注入目标进程

	if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwPID)))
	{
		printf("error2");
		return false;
	}

这个API的作用是打开一个进程并返回这个进程的句柄,通过PID来定位某个进程进而返回它的句柄。

三个参数分别为:该进程对打开进程的权限、是否继承进程句柄、目标进程的PID。

申请内存空间

pRemoteBuf = VirtualAllocEx(hProcess, NULL, BufSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pRemoteBuf == 0)
{
    printf("error3");
    return false;
}

这个API的作用是在目标进程申请一段内存空间并返回所申请的内存空间的地址。

五个参数分别为:目标进程的句柄、指定要分配的页面区域所需的起始地址的指针、所需分配内存空间的大小、内存分配类型、内存保护权限。

写入dll路径

	if (!WriteProcessMemory(hProcess, pRemoteBuf, szDllPath, BufSize, NULL))
	{
		printf("error4");
		return false;
	}

这个API的作用是在目标进程的一段内存空间中写入,并返回是否写入成功。

五个参数分别为:目标进程的句柄、写入的内存地址、写入的数据、写入数据的长度、接收传输到目标进程的字节数的指针(可为NULL)。

获取加载函数地址

lpLoadLibrary = GetProcAddress(GetModuleHandleW(L"kernel32.dll"),"LoadLibraryW");

有两个API,均较为简单。GetModuleHandleW输入文件名返回文件句柄,GetProcAddress输入句柄与查找函数名返回查找函数名的地址。在这里GetModuleHandleW返回了kernel32.dll的句柄,GetProcAddress返回了kernel32.dll中LoadLibraryW API的地址。

在目标进程中运行线程

	hThread = CreateRemoteThread(
		hProcess,
		NULL,
		0,
		(LPTHREAD_START_ROUTINE)lpLoadLibrary,
		pRemoteBuf,
		0,
		NULL
	);

CreateRemoteThread API,相比于CreateThread区别在于为目标进程创建线程。

七个参数分别为:目标进程句柄、子进程是否继承句柄、堆栈初始大小(为0时默认大小)、线程函数地址、线程函数参数指针、控制线程创建的标志。

完整代码:

#include<Windows.h>
#include<stdio.h>
#include <string>
BOOL InjectDLL(int dwPID, const wchar_t* szDllPath)
{
	HANDLE hProcess = NULL, hThread = NULL;
	LPVOID pRemoteBuf = NULL;
	int BufSize = (wcslen(szDllPath) + 1) * sizeof(wchar_t);
	LPVOID lpLoadLibrary = NULL;

	//打开目标进程
	if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwPID)))
	{
		printf("error2");
		return false;
	}

	//在目标进程中分配一块内存
	pRemoteBuf = VirtualAllocEx(hProcess, NULL, BufSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (pRemoteBuf == 0)
	{
		printf("error3");
		return false;
	}
	//获取LoadLibraryW的地址
	lpLoadLibrary = GetProcAddress(GetModuleHandleW(L"kernel32.dll"),"LoadLibraryW");

	//写入dll路径到目标进程内存
	if (!WriteProcessMemory(hProcess, pRemoteBuf, szDllPath, BufSize, NULL))
	{
		printf("error4");
		return false;
	}

	//在目标进程运行线程
	hThread = CreateRemoteThread(
		hProcess,
		NULL,
		0,
		(LPTHREAD_START_ROUTINE)lpLoadLibrary,
		pRemoteBuf,
		0,
		NULL
	);

	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);
	CloseHandle(hProcess);
	return true;
}
int wmain(int argc, wchar_t* argv[])
{
	if (argc != 3)
	{
		printf("error1");
		return 1;
	}
	if (InjectDLL(std::stol(argv[1]), argv[2]))
	{
		printf("success!");
	}
	else
	{
		printf("failed!");
	}
	return 0;
}

测试DLL

#include "pch.h"

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {

    case DLL_PROCESS_ATTACH: {
        HWND hwnd = GetActiveWindow();
        MessageBox(hwnd, L"DLL已进入目标进程。", L"信息", MB_ICONINFORMATION);
        OutputDebugStringW(L"dllinject");

    }
    }
    return TRUE;
}
通过弹窗判断是否注入成功。

测试

cmd输入目标进程的PID与要注入的DLL。

值得注意的是,目标进程是64位或32位程序,注入器与DLL需与目标进程保持一致。