微软专家 发表于 2026-7-23 14:00:00

Windows DLL注入三种实战方法:全局钩子、远程线程与Sessio

Windows平台下的DLL注入是系统安全研究和底层开发中的常见技术,涉及在目标进程中加载自定义DLL以执行特定代码。本文基于实际注入实践,介绍三种经典注入方法:全局钩子注入、远程线程注入,以及突破Session 0隔离的远线程注入。每种方法均给出Win32 API原理、关键步骤和完整代码示例。

一、全局钩子注入
Windows消息机制中,钩子(Hook)可以截获系统或线程的消息。全局钩子注入利用SetWindowsHookEx将DLL中的钩子函数安装到钩子链中,当系统产生消息时自动加载DLL。

1.1 生成DLL
新建DLL项目,在预编译头中声明导出函数:

// pch.h
extern "C" _declspec(dllexport) int SetHook();
extern "C" _declspec(dllexport) LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam);
extern "C" _declspec(dllexport) BOOL UnsetHook();


在pch.cpp中实现:

#include "pch.h"
#include <Windows.h>
#include <stdio.h>

extern HMODULE g_hDllModule;

#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
#pragma comment(linker, "/SECTION:mydata,RWS")

LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {
    return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}

BOOL SetHook() {
    g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);
    if (g_hHook == NULL) return FALSE;
    return TRUE;
}

BOOL UnsetHook() {
    if (g_hHook) ::UnhookWindowsHookEx(g_hHook);
    return TRUE;
}


DLL入口文件dllmain.cpp保存模块句柄:

#include "pch.h"
HMODULE g_hDllModule = NULL;
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
      g_hDllModule = hModule;
      break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
      break;
    }
    return TRUE;
}


以Release X86配置生成得到DLL文件。

1.2 测试注入
创建控制台项目,加载DLL并调用SetHook:

#include <Windows.h>
#include <stdio.h>

int main() {
    typedef BOOL(*typedef_SetGlobalHook)();
    typedef BOOL(*typedef_UnsetGlobalHook)();

    HMODULE hDll = NULL;
    typedef_SetGlobalHook SetGlobalHook = NULL;
    typedef_UnsetGlobalHook UnsetGlobalHook = NULL;
    BOOL bRet = FALSE;

    do {
      hDll = ::LoadLibraryW(TEXT("C:\\path\\to\\DLLInjection.dll"));
      if (hDll == NULL) { printf("LoadLibrary Error[%d]\n", ::GetLastError()); break; }

      SetGlobalHook = (typedef_SetGlobalHook)::GetProcAddress(hDll, "SetHook");
      if (SetGlobalHook == NULL) { printf("GetProcAddress Error[%d]\n", ::GetLastError()); break; }

      bRet = SetGlobalHook();
      if (bRet) printf("SetGlobalHook OK.\n");
      else printf("SetGlobalHook Error.\n");

      system("pause");

      UnsetGlobalHook = (typedef_UnsetGlobalHook)::GetProcAddress(hDll, "UnsetHook");
      if (UnsetGlobalHook == NULL) { printf("GetProcAddress Error[%d]\n", ::GetLastError()); break; }
      UnsetGlobalHook();
      printf("UnsetGlobalHook OK.\n");
    } while (FALSE);

    system("pause");
    return 0;
}


全局钩子注入的优点是无需目标进程配合,缺点是会影响系统性能,且容易被安全软件检测。

二、远程线程注入
远程线程注入的核心思路是在目标进程中创建线程,线程入口为LoadLibrary,参数为DLL路径,从而将DLL加载到目标进程空间。

2.1 生成DLL
仅需在DllMain中弹出消息框验证:

#include "pch.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
      MessageBox(NULL, L"success!", L"RemoteThreadInjection", MB_OK);
      break;
    case DLL_THREAD_ATTACH:
      MessageBox(NULL, L"success!", L"RemoteThreadInjection", MB_OK);
      break;
    }
    return TRUE;
}


2.2 实现注入函数
通过OpenProcess打开目标进程,VirtualAllocEx分配内存,WriteProcessMemory写入DLL路径,然后获取kernel32.dll中LoadLibraryW的地址,最后用CreateRemoteThread创建线程:

#include <Windows.h>
#include <tchar.h>
#include <stdio.h>

DWORD _RemoteThreadInjection(DWORD _Pid, LPCWSTR DllName) {
    HANDLE hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid);
    if (hprocess == NULL) { printf("OpenProcess Error\n"); return FALSE; }

    SIZE_T _Size = (_tcslen(DllName) + 1) * sizeof(TCHAR);
    LPVOID pDllAddr = ::VirtualAllocEx(hprocess, NULL, _Size, MEM_COMMIT, PAGE_READWRITE);
    if (pDllAddr == NULL) { printf("Memory Allocation Error\n"); return FALSE; }

    BOOL isWriteSuccess = ::WriteProcessMemory(hprocess, pDllAddr, DllName, _Size, NULL);
    if (!isWriteSuccess) { printf("Write Process Memory Error\n"); return FALSE; }

    FARPROC pThread = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
    LPTHREAD_START_ROUTINE addr = (LPTHREAD_START_ROUTINE)pThread;

    HANDLE hThread = ::CreateRemoteThread(hprocess, NULL, 0, addr, pDllAddr, 0, NULL);
    if (hThread == NULL) { printf("CreateRemoteThread Error\n"); return FALSE; }

    WaitForSingleObject(hThread, -1);
    ::VirtualFreeEx(hprocess, pDllAddr, _Size, MEM_DECOMMIT);
    ::CloseHandle(hprocess);
    return TRUE;
}

int main() {
    DWORD _PID = 1220; // 替换为目标进程PID
    _RemoteThreadInjection(_PID, L"C:\\path\\to\\DLLInjection.dll");
    return 0;
}


注意:Windows ASLR虽使LoadLibrary地址每次启动不同,但同一时刻kernel32.dll在各进程中的基址相同,因此可安全获取。

三、突破Session 0隔离的远线程注入
Session 0隔离机制从Windows Vista开始生效,系统服务运行在Session 0,用户应用运行在其他Session。若目标进程是系统服务,普通CreateRemoteThread会被拒绝。解决办法是使用更底层的ZwCreateThreadEx函数绕过会话检查。

3.1 原理
ZwCreateThreadEx是ntdll.dll中的未文档化函数,相比CreateRemoteThread多了CreateThreadFlags等参数,可指定线程创建时不检查会话。同时,还需启用SeDebugPrivilege权限以访问系统进程。

3.2 实现代码

#include <Windows.h>
#include <stdio.h>

// 定义ZwCreateThreadEx函数指针
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
    PHANDLE ThreadHandle,
    ACCESS_MASK DesiredAccess,
    LPVOID ObjectAttributes,
    HANDLE ProcessHandle,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    ULONG CreateThreadFlags,
    SIZE_T ZeroBits,
    SIZE_T StackSize,
    SIZE_T MaximumStackSize,
    LPVOID pUnknown);

BOOL EnableDebugPrivilege() {
    HANDLE hToken;
    if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
      TOKEN_PRIVILEGES tp;
      tp.PrivilegeCount = 1;
      LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges.Luid);
      tp.Privileges.Attributes = SE_PRIVILEGE_ENABLED;
      AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
      BOOL flag = (GetLastError() == ERROR_SUCCESS);
      CloseHandle(hToken);
      return flag;
    }
    return FALSE;
}

BOOL BypassSession0Injection(DWORD _PID, const char* DLLPath) {
    HANDLE hProcess = NULL;
    SIZE_T dwSize = 0;
    LPVOID pDllAddr = NULL;
    FARPROC LoadLibraryA_Addr = NULL;
    HANDLE hRemoteThread = NULL;
    DWORD dwStatus = 0;

    // 提权
    if (!EnableDebugPrivilege()) {
      printf("SeDebugPrivilege enable failed.\n");
      return FALSE;
    }

    hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _PID);
    if (hProcess == NULL) { printf("OpenProcess Error\n"); return FALSE; }

    dwSize = ::strlen(DLLPath) + 1;
    pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
    if (pDllAddr == NULL) { printf("VirtualAllocEx Error\n"); return FALSE; }

    if (!::WriteProcessMemory(hProcess, pDllAddr, DLLPath, dwSize, NULL)) {
      printf("WriteProcessMemory Error\n");
      return FALSE;
    }

    HMODULE hNtdDll = ::LoadLibrary(L"ntdll.dll");
    if (hNtdDll == NULL) { printf("Load ntdll.dll Error\n"); return FALSE; }

    LoadLibraryA_Addr = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
    if (LoadLibraryA_Addr == NULL) { printf("GetProcAddress Error\n"); return FALSE; }

    typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdDll, "ZwCreateThreadEx");
    if (ZwCreateThreadEx == NULL) { printf("ZwCreateThreadEx not found\n"); return FALSE; }

    dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS,
      NULL, hProcess, (LPTHREAD_START_ROUTINE)LoadLibraryA_Addr, pDllAddr,
      0, 0, 0, 0, NULL);
    if (hRemoteThread == NULL) { printf("ZwCreateThreadEx failed\n"); return FALSE; }

    ::CloseHandle(hProcess);
    ::FreeLibrary(hNtdDll);
    return TRUE;
}

int main() {
    // 以系统服务PID为例,需管理员权限
    BypassSession0Injection(3788, "C:\\path\\to\\artifact_x86.dll");
    return 0;
}


关键点:ZwCreateThreadEx的第7个参数CreateThreadFlags置0可绕过Session检查;必须先提升权限。

四、总结
三种注入方法各有利弊。全局钩子注入简单但影响系统范围;远程线程注入灵活但无法穿透Session 0;突破Session 0的注入利用底层API,适合对系统服务进行DLL加载。实际应用中需根据目标进程性质和权限环境选择合适技术,并注意DLL的32/64位匹配。

热心网友4 发表于 2026-7-23 14:05:00

Re: Windows DLL注入三种实战方法:全局钩子、远程线程与Sessio

感谢楼主的详细分享!全局钩子注入的代码示例很清晰,从DLL导出函数到测试控制台都讲得很扎实,特别是#pragma data_seg()的使用体现了跨进程共享数据的细节。期待你继续写完远程线程和Session 0隔离的部分,这两种方法在实际开发中也很常见,尤其是突破Session 0隔离对服务程序的注入很有参考价值。辛苦楼主!

热心网友4 发表于 2026-7-23 14:05:00

Re: Windows DLL注入三种实战方法:全局钩子、远程线程与Sessio

感谢楼主的详细分享,全局钩子注入的代码示例非常清晰,从DLL编写到测试注入的流程都很完整。特别是对“影响系统性能”和“易被安全软件检测”这两点缺点的说明很实在,能让读者更全面地理解这种方法的适用场景。 期待后续远程线程注入和Session 0隔离的内容——这两种在实际对抗或渗透测试中也很常见,想知道楼主在突破Session 0隔离时具体采用了哪些API技巧。

热心网友4 发表于 2026-7-23 14:05:00

Re: Windows DLL注入三种实战方法:全局钩子、远程线程与Sessio

感谢分享,全局钩子注入的讲解很详细,代码示例也很完整,对理解Windows消息钩子和DLL注入的实战很有帮助。期待楼主继续更新远程线程和Session 0隔离的部分。
页: [1]
查看完整版本: Windows DLL注入三种实战方法:全局钩子、远程线程与Sessio