初探dll注入
字数 1382 2025-08-09 13:33:38

DLL注入技术详解

1. 全局钩子注入

原理

Windows系统中大部分应用基于消息机制,通过钩子机制可以截获和监视系统消息。全局钩子通过DLL文件实现钩子函数,当对应事件发生时,系统会将DLL加载到发生事件的进程地址空间中。

核心API

  • SetWindowsHookEx: 安装钩子到挂钩链
  • CallNextHookEx: 将当前钩子传递给下一个钩子
  • UnhookWindowsHookEx: 卸载钩子

实现步骤

  1. 创建DLL项目,导出以下函数:

    extern "C" _declspec(dllexport) int SetHook();
    extern "C" _declspec(dllexport) LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam);
    extern "C" _declspec(dllexport) BOOL UnsetHook();
    
  2. 实现钩子回调函数:

    LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {
        return ::CallNextHookEx(g_hHook, code, wParam, lParam);
    }
    
  3. 设置全局钩子:

    BOOL SetHook() {
        g_Hook = ::SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllMoudle, 0);
        return g_Hook != NULL;
    }
    
  4. 使用共享内存实现进程通信:

    #pragma data_seg("mydata")
    HHOOK g_hHook = NULL;
    #pragma data_seg()
    #pragma comment(linker, "/SECTION:mydata,RWS")
    
  5. 创建注入程序加载DLL并调用SetHook函数

2. 远程线程注入

原理

通过CreateRemoteThread在一个进程中创建线程,将DLL加载到目标进程地址空间。

核心API

  • CreateRemoteThread: 在远程进程中创建线程
  • VirtualAllocEx: 在远程进程中申请内存
  • WriteProcessMemory: 向远程进程写入数据
  • GetProcAddress: 获取LoadLibrary地址

实现步骤

  1. 获取目标进程PID:

    DWORD _GetProcessPID(LPCTSTR lpProcessName) {
        // 使用CreateToolhelp32Snapshot拍摄进程快照
        // 遍历进程列表匹配进程名
    }
    
  2. 打开目标进程:

    HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid);
    
  3. 在目标进程中分配内存:

    LPVOID pAllocMemory = ::VirtualAllocEx(hProcess, NULL, _Size, MEM_COMMIT, PAGE_READWRITE);
    
  4. 写入DLL路径:

    BOOL Write = ::WriteProcessMemory(hProcess, pAllocMemory, DllName, _Size, NULL);
    
  5. 获取LoadLibrary地址:

    FARPROC pThread = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
    
  6. 创建远程线程:

    HANDLE hThread = ::CreateRemoteThread(hProcess, NULL, 0, addr, pAllocMemory, 0, NULL);
    

3. 突破Session 0的远程线程注入

Session 0隔离机制

从Windows Vista开始,服务和用户应用程序被隔离到不同会话中,只有服务运行在Session 0中。

核心API

  • ZwCreateThreadEx: 比CreateRemoteThread更底层的线程创建函数
  • OpenProcessToken: 打开进程访问令牌
  • LookupPrivilegeValue: 查看系统特权
  • AdjustTokenPrivileges: 调整令牌权限

实现步骤

  1. 提权函数实现:

    BOOL EnableDebugPrivilege() {
        // 获取当前进程令牌
        // 查找SE_DEBUG_NAME特权
        // 调整令牌权限
    }
    
  2. 32位和64位下ZwCreateThreadEx定义:

    #ifdef _WIN64
    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 pUnkown);
    #else
    // 32位定义...
    #endif
    
  3. 注入流程:

    // 1. 提权
    // 2. 打开目标进程
    // 3. 在目标进程分配内存
    // 4. 写入DLL路径
    // 5. 获取LoadLibrary地址
    // 6. 获取ZwCreateThreadEx地址
    // 7. 调用ZwCreateThreadEx创建远程线程
    

4. APC注入

原理

利用异步过程调用(APC)机制,将APC函数插入到线程APC队列中,当线程处于可警告状态时执行。

核心API

  • QueueUserAPC: 向线程APC队列添加APC函数
  • SleepEx/WaitForSingleObjectEx: 使线程进入可警告状态

实现条件

  1. 多线程环境
  2. 目标程序会调用同步对象(SleepEx等)

实现步骤

  1. 获取目标进程所有线程:

    BOOL GetProcessThreadList(DWORD th32ProcessID, DWORD** ppThreadIdList, LPDWORD pThreadIdListLength) {
        // 使用CreateToolhelp32Snapshot拍摄线程快照
        // 遍历线程列表
    }
    
  2. APC注入主函数:

    BOOL APCInject(HANDLE hProcess, CHAR* wzDllFullPath, LPDWORD pThreadIdList, DWORD dwThreadIdListLength) {
        // 1. 在目标进程分配内存
        // 2. 写入DLL路径
        // 3. 获取LoadLibrary地址
        // 4. 遍历线程插入APC
    }
    
  3. 遍历线程插入APC:

    for(int i = dwThreadIdListLength-1; i >= 0; i--) {
        HANDLE hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadIdList[i]);
        ::QueueUserAPC((PAPCFUNC)loadLibraryAddress, hThread, (ULONG_PTR)lpAddr);
    }
    

技术对比

注入方式 优点 缺点 适用场景
全局钩子 实现简单 需要DLL支持 GUI程序
远程线程 通用性强 受Session 0限制 普通进程注入
ZwCreateThreadEx 可突破Session 0 实现复杂 系统服务注入
APC注入 隐蔽性高 需要多线程环境 多线程程序注入

注意事项

  1. 32位和64位程序注入时需要注意位数匹配
  2. 注入系统进程需要提权(SE_DEBUG_NAME)
  3. 注入DLL中避免使用MessageBox等UI操作
  4. 注入后需要清理内存和句柄
  5. 考虑目标进程的UAC和完整性级别
DLL注入技术详解 1. 全局钩子注入 原理 Windows系统中大部分应用基于消息机制,通过钩子机制可以截获和监视系统消息。全局钩子通过DLL文件实现钩子函数,当对应事件发生时,系统会将DLL加载到发生事件的进程地址空间中。 核心API SetWindowsHookEx : 安装钩子到挂钩链 CallNextHookEx : 将当前钩子传递给下一个钩子 UnhookWindowsHookEx : 卸载钩子 实现步骤 创建DLL项目,导出以下函数: 实现钩子回调函数: 设置全局钩子: 使用共享内存实现进程通信: 创建注入程序加载DLL并调用SetHook函数 2. 远程线程注入 原理 通过 CreateRemoteThread 在一个进程中创建线程,将DLL加载到目标进程地址空间。 核心API CreateRemoteThread : 在远程进程中创建线程 VirtualAllocEx : 在远程进程中申请内存 WriteProcessMemory : 向远程进程写入数据 GetProcAddress : 获取LoadLibrary地址 实现步骤 获取目标进程PID: 打开目标进程: 在目标进程中分配内存: 写入DLL路径: 获取LoadLibrary地址: 创建远程线程: 3. 突破Session 0的远程线程注入 Session 0隔离机制 从Windows Vista开始,服务和用户应用程序被隔离到不同会话中,只有服务运行在Session 0中。 核心API ZwCreateThreadEx : 比CreateRemoteThread更底层的线程创建函数 OpenProcessToken : 打开进程访问令牌 LookupPrivilegeValue : 查看系统特权 AdjustTokenPrivileges : 调整令牌权限 实现步骤 提权函数实现: 32位和64位下ZwCreateThreadEx定义: 注入流程: 4. APC注入 原理 利用异步过程调用(APC)机制,将APC函数插入到线程APC队列中,当线程处于可警告状态时执行。 核心API QueueUserAPC : 向线程APC队列添加APC函数 SleepEx / WaitForSingleObjectEx : 使线程进入可警告状态 实现条件 多线程环境 目标程序会调用同步对象(SleepEx等) 实现步骤 获取目标进程所有线程: APC注入主函数: 遍历线程插入APC: 技术对比 | 注入方式 | 优点 | 缺点 | 适用场景 | |---------|------|------|---------| | 全局钩子 | 实现简单 | 需要DLL支持 | GUI程序 | | 远程线程 | 通用性强 | 受Session 0限制 | 普通进程注入 | | ZwCreateThreadEx | 可突破Session 0 | 实现复杂 | 系统服务注入 | | APC注入 | 隐蔽性高 | 需要多线程环境 | 多线程程序注入 | 注意事项 32位和64位程序注入时需要注意位数匹配 注入系统进程需要提权(SE_ DEBUG_ NAME) 注入DLL中避免使用MessageBox等UI操作 注入后需要清理内存和句柄 考虑目标进程的UAC和完整性级别