0xd00 随笔小记

Back

详解 Windows DLL 远程注入的实现原理Blur image

简单介绍DLL#

DLL (Dynamic-Link Library),即“动态链接库”,是一个编译后的代码和资源库,它允许多个可执行文件.exe在运行时动态加载并共享其功能,类似于Linux的共享对象.so。它的核心优势是运行时链接(Runtime linking),而非编译时将所有代码都静态链接到主程序中。

DLL文件和EXE文件一样,都使用PE文件格式。他们的主要区别在于PE头中的一个标志位,以及DLL文件一般不能独立运行,需要通过其他程序加载并运行。

PE头中的标志位

导出表#

正常情况下DLL需要通过导出表来展示一个可供调用的函数列表,当一个程序想要调用DLL中的函数时,它就会通过这个导出表来找到函数的实际内存地址。

本例中方便起见不导出函数,直接通过DLL_PROCESS_ATTACH来展示DLL被载入后的运行效果。

DLL本地注入#

这个 DLL 在被加载时,会利用其 DllMain 入口点弹出一个消息框。

加载器程序调用 LoadLibraryA 来加载 Dll.dll。这个 API 会触发 Windows 加载器执行完整的 DLL 加载流程:解析 PE 头、映射段、处理导入、最后调用 DllMain。

成功运行loader并弹出messagebox

DLL远程注入#

远程注入的核心思想是:在目标进程的上下文中,强制其调用 LoadLibraryA 来加载我们指定的 DLL。由于进程地址空间的隔离性,这需要一系列精确的跨进程内存操作。

定位目标进程#

通过 CreateToolhelp32Snapshot API 遍历系统进程,根据进程名找到其 PID。

获取目标句柄#

调用 OpenProcess获取一个拥有足够权限(PROCESS_ALL_ACCESS)的句柄。

    HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD, FALSE, pid);
    if (hProcess == NULL) {
        wprintf(L"Failed to open target process (Error: %d). Try running as Administrator.\n", GetLastError());
        return 1;
    }
c

在目标进程中分配内存#

使用VirtualAllocEx在目标进程的地址空间中申请一块足以保存DLL的内存空间。

LPVOID pRemoteMem = VirtualAllocEx(hProcess, NULL, dllPathSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pRemoteMem == NULL) {
    wprintf(L"VirtualAllocEx failed. Error: %d\n", GetLastError());
    CloseHandle(hProcess);
    return 1;
}
plaintext

写入DLL路径#

使用 WriteProcessMemory 将我们的 DLL 路径字符串写入到刚刚在远程进程中分配的内存里。

    // 写入内存时,也要提供正确的字节大小
    if (!WriteProcessMemory(hProcess, pRemoteMem, dllPath, dllPathSize, NULL)) {
        wprintf(L"WriteProcessMemory failed. Error: %d\n", GetLastError());
        VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE);
        CloseHandle(hProcess);
        return 1;
    }
c

查找 LoadLibraryW 地址#

LoadLibraryW 用于加载一个调用它的进程内部的 DLL。 由于目标是加载远程进程内部而非本地进程内部的 DLL,所以不能直接调用它。 相反,必须检索 LoadLibraryW 的地址并将其作为参数传递给进程中远程创建的线程,同时将 DLL 名称作为其参数。 这起作用是因为 LoadLibraryW WinAPI 的地址在远程进程中和在本地进程中相同。 为了确定 WinAPI 的地址,需要使用 GetProcAddressGetModuleHandle

    // 必须使用 LoadLibraryW,而不是 LoadLibraryA
    LPVOID pLoadLibraryW = (LPVOID)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW");
    if (pLoadLibraryW == NULL) {
        wprintf(L"GetProcAddress for LoadLibraryW failed. Error: %d\n", GetLastError());
        CloseHandle(hProcess);
        return 1;
    }
c

创建远程线程#

调用 CreateRemoteThread,让它在目标进程中创建一个新线程。这个新线程的起始执行地址被设置为 LoadLibraryA 的地址,其接收的参数则被设置为我们刚刚写入的 DLL 路径的地址。

HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibraryW, pRemoteMem, 0, NULL);
if (hRemoteThread == NULL) {
    wprintf(L"CreateRemoteThread failed. Error: %d\n", GetLastError());
    VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return 1;
}
plaintext

完整代码#

运行效果#

image-20250723154543840

详解 Windows DLL 远程注入的实现原理
https://blog.0xd00.com/blog/createremotethread-dll-injection
Author 0xd00
Published at 2025年7月23日
Comment seems to stuck. Try to refresh?✨