高级进程注入之利用线程名和APC实现进程注入(下)
字数 3803 2025-09-23 19:27:38

高级进程注入技术:利用线程名与APC实现进程注入(下)

1. 前言

本文是《高级进程注入之利用线程名和APC实现进程注入》的下半部分,将深入探讨利用线程名(Thread Description)进行进程注入的实现细节、技术挑战及规避方案。与传统的CreateRemoteThread注入技术不同,此技术核心在于无需创建远程线程即可将Shellcode写入并执行于目标进程,有效降低了被AV/EDR检测的风险。

上篇回顾:主要介绍了实现此技术所需的关键Windows API,如SetThreadDescription, GetThreadDescription, QueueUserAPC2等。

2. 技术原理与优势

2.1 核心思想

通常的进程注入需要获取目标进程的写权限(PROCESS_VM_WRITE)以写入Shellcode,此行为易被安全软件标记为可疑。本技术通过利用系统API GetThreadDescription 的合法功能,诱使目标进程自己在内存中分配并写入Shellcode(即线程名),从而规避了对进程直接写入的敏感操作。

2.2 所需权限

为最小化可疑指标,应尽可能申请最少的权限。所需基础权限如下:

HANDLE open_process(DWORD processId, bool isCreateThread) {
    DWORD access = PROCESS_QUERY_LIMITED_INFORMATION // 用于读取远程进程PEB
                  | PROCESS_VM_READ                  // 用于修改PEB中的字段
                  | PROCESS_VM_OPERATION;            // 用于操作内存权限(如VirtualProtectEx)
    if (isCreateThread) {
        access |= PROCESS_CREATE_THREAD; // 备选方案:当需要创建新线程时才申请
    }
    return OpenProcess(access, FALSE, processId);
}
  • PROCESS_QUERY_LIMITED_INFORMATION: 用于获取远程进程的PEB(Process Environment Block)地址。
  • PROCESS_VM_READ: 用于读取/修改PEB中的特定字段。
  • PROCESS_VM_OPERATION: 用于后续调用VirtualProtectExVirtualAllocEx来更改内存属性或分配内存。
  • PROCESS_CREATE_THREAD: 可选权限。仅在旧版Windows无法使用新型APC且找不到可警告线程时,作为备选方案(创建新线程)使用。应尽量避免使用以降低检测概率。

操作目标进程的线程所需的最小权限:

DWORD thAccess = SYNCHRONIZE;
thAccess |= THREAD_SET_CONTEXT;             // 用于向线程APC队列添加项
thAccess |= THREAD_SET_LIMITED_INFORMATION; // 用于设置线程描述(即Shellcode)

3. 注入流程详解

任何Shellcode注入都包含三个核心步骤:

  1. 写入:将Shellcode写入远程进程内存。
  2. 权限:确保存放Shellcode的内存区域具有可执行(X)权限。
  3. 执行:控制执行流,跳转到Shellcode所在地址。

本技术通过以下方式实现这些步骤:

3.1 第一步:远程写入(无需PROCESS_VM_WRITE)

  1. Shellcode准备:确保Shellcode无空字节(NULL-byte free)且不会阻塞线程。
  2. 选择目标线程
    • 理想情况(新API):使用QueueUserAPC2并设置QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC标志,可选用任意线程。
    • 备选情况(旧API):必须找到一个处于可警告(Alertable)状态的线程,或自行创建一个(此时需要PROCESS_CREATE_THREAD权限)。
  3. “写入”机制
    • 在注入器进程中,使用SetThreadDescription将一个伪句柄NtCurrentThread())的线程名设置为我们的Shellcode(Unicode字符串形式)。使用伪句柄避免了需要PROCESS_DUP_HANDLE权限来复制远程线程真句柄的问题。
    • 通过APC,在目标线程的上下文中排队一个任务,调用GetThreadDescription
    • GetThreadDescription函数被执行时,会自动在目标进程的堆上分配内存,并将线程名(即我们的Shellcode)写入该内存。该函数会将指向这块新内存的指针存入其输出参数ppszThreadDescription中。
    • 至此,Shellcode已被成功写入远程进程内存,且权限为RW(可读可写)。

3.2 第二步:存储指针与定位PEB

GetThreadDescription返回的指针ppszThreadDescription需要存储在目标进程的一个已知地址,以便后续检索和使用。

首选方案:利用PEB中的未使用字段

  • PEB结构体中包含一些保留或未使用的字段,它们是存储此指针的理想位置,因为PEB本身位于可读写(RW)的内存区域。
  • 通过NtQueryInformationProcess(查询ProcessBasicInformation)来获取目标进程的PEB基地址。
  • 在64位Windows 10/11系统中,偏移量+0x340处的SpareUlongs数组是一个合适的候选字段。
    ULONG_PTR get_peb_unused(HANDLE hProcess) {
        ULONG_PTR peb_addr = remote_peb_addr(hProcess); // 获取PEB地址
        if (!peb_addr) return NULL;
        const ULONG_PTR UNUSED_OFFSET = 0x340; // 示例偏移,需根据系统版本调整
        const ULONG_PTR remotePtr = peb_addr + UNUSED_OFFSET;
        return remotePtr;
    }
    

    注意:PEB结构可能随Windows版本更新而变化,此偏移量需要根据目标系统进行验证和调整(例如使用WinDbg的dt nt!_PEB命令查看实际结构)。使用SpareUlongs相对安全,但未来可能被系统占用。

备选方案:在远程进程内存空间中搜索其他可写的已知地址。

3.3 第三步:分配可执行内存与权限变更

写入的Shellcode位于堆上,权限为RW,不可执行。需要将其转移至可执行的内存区域。

方案分析

方案 实现方式 优点 缺点
1. VirtualProtectEx 直接调用VirtualProtectEx,将堆上的Shellcode内存页权限改为RWX。 步骤简单,无需额外拷贝。 高度可疑。直接创建RWX内存页是恶意软件的经典行为,易被内存扫描器检测。
2. VirtualAllocEx 调用VirtualAllocEx申请新的RWX内存页,然后将Shellcode拷贝过去。 新内存页,相对干净。 可疑。申请RWX内存是可检测指标。无法通过APC直接调用(参数超过3个)。
3. ROP链 构造Return-Oriented Programming链来调用VirtualProtectVirtualAlloc 避免直接调用敏感API,更隐蔽。 实现复杂,需要操作线程上下文(Get/SetThreadContext),会产生更多可疑API调用,易触发告警。

结论与选择
尽管VirtualProtectExVirtualAllocEx本身是可疑指标,但测试表明,在缺乏其他传统注入指标(如创建线程)的配合下,许多AV/EDR并不会仅因此而被触发。因此,文档中选择直接使用VirtualProtectEx作为实现方案,因其步骤最少。代码中也应提供VirtualAllocEx的备选路径。

重要提示:如果目标进程启用了DCP(Device Guard Code Integrity),任何尝试分配可执行内存的操作都可能会失败。

3.4 第四步:执行Shellcode

  1. 通过另一个APC,将执行流程跳转到已具备可执行权限的Shellcode地址。
  2. 可以使用RtlDispatchAPC作为代理函数来触发APC的执行。

4. 技术总结与规避要点

  1. 权限最小化:始终遵循最小权限原则,避免申请不必要的权限(如PROCESS_CREATE_THREAD, PROCESS_DUP_HANDLE)。
  2. API选择:优先使用新型APC API(QueueUserAPC2 + SPECIAL_USER_APC),以获得最大的线程兼容性和更低的权限要求。
  3. 线程选择:尽力寻找可警告线程,避免创建新线程。
  4. 内存操作
    • VirtualProtectEx/VirtualAllocEx + RWX内存是当前方案的已知弱点,但在此技术上下文中检测率较低。
    • 理想的终极目标是找到进程中现成的RX内存空洞并复制Shellcode进去,但这在实践中非常困难。
  5. 检测规避:此技术的优势在于拆解并伪装了注入步骤:
    • 写入操作由系统APIGetThreadDescription合法完成。
    • 避免了WriteProcessMemoryCreateRemoteThread这两个最敏感的API。
    • 即使使用了敏感的权限操作API,也因为其上下文(与线程名操作相关)而变得不那么突兀。

5. 结语

利用线程名和APC进行进程注入是一种先进的规避技术(Advanced Evasion Technique),它巧妙地利用了Windows系统的新增功能。其有效性源于将恶意操作分解并隐藏在合法的API调用序列中。

防守方视角
对于防御者而言,检测此类技术需要更深入的行为分析,而非简单的API钩取或静态规则。应重点关注:

  • 异常序列SetThreadDescription后紧跟针对其他进程的APC操作。
  • PEB修改:对进程PEB中特定字段的写操作。
  • 内存权限变更:任何将非映像内存(如堆)设置为可执行的操作,尤其是通过VirtualProtectEx完成的。
  • ETW(Event Tracing for Windows):利用微软提供的ETW日志进行实时监控,可以捕获到这些API的调用及其上下文,是检测此类高级威胁的关键。
高级进程注入技术:利用线程名与APC实现进程注入(下) 1. 前言 本文是《高级进程注入之利用线程名和APC实现进程注入》的下半部分,将深入探讨利用线程名(Thread Description)进行进程注入的实现细节、技术挑战及规避方案。与传统的 CreateRemoteThread 注入技术不同,此技术核心在于 无需创建远程线程 即可将Shellcode写入并执行于目标进程,有效降低了被AV/EDR检测的风险。 上篇回顾 :主要介绍了实现此技术所需的关键Windows API,如 SetThreadDescription , GetThreadDescription , QueueUserAPC2 等。 2. 技术原理与优势 2.1 核心思想 通常的进程注入需要获取目标进程的写权限( PROCESS_VM_WRITE )以写入Shellcode,此行为易被安全软件标记为可疑。本技术通过利用系统API GetThreadDescription 的合法功能,诱使目标进程 自己 在内存中分配并写入Shellcode(即线程名),从而规避了对进程直接写入的敏感操作。 2.2 所需权限 为最小化可疑指标,应尽可能申请最少的权限。所需基础权限如下: PROCESS_QUERY_LIMITED_INFORMATION : 用于获取远程进程的PEB(Process Environment Block)地址。 PROCESS_VM_READ : 用于读取/修改PEB中的特定字段。 PROCESS_VM_OPERATION : 用于后续调用 VirtualProtectEx 或 VirtualAllocEx 来更改内存属性或分配内存。 PROCESS_CREATE_THREAD : 可选权限 。仅在旧版Windows无法使用新型APC且找不到可警告线程时,作为备选方案(创建新线程)使用。应尽量避免使用以降低检测概率。 操作目标进程的线程所需的最小权限: 3. 注入流程详解 任何Shellcode注入都包含三个核心步骤: 写入 :将Shellcode写入远程进程内存。 权限 :确保存放Shellcode的内存区域具有可执行(X)权限。 执行 :控制执行流,跳转到Shellcode所在地址。 本技术通过以下方式实现这些步骤: 3.1 第一步:远程写入(无需PROCESS_ VM_ WRITE) Shellcode准备 :确保Shellcode无空字节(NULL-byte free)且不会阻塞线程。 选择目标线程 : 理想情况(新API) :使用 QueueUserAPC2 并设置 QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC 标志,可选用任意线程。 备选情况(旧API) :必须找到一个处于 可警告(Alertable) 状态的线程,或自行创建一个(此时需要 PROCESS_CREATE_THREAD 权限)。 “写入”机制 : 在注入器进程中,使用 SetThreadDescription 将一个 伪句柄 ( NtCurrentThread() )的线程名设置为我们的Shellcode(Unicode字符串形式)。使用伪句柄避免了需要 PROCESS_DUP_HANDLE 权限来复制远程线程真句柄的问题。 通过APC,在目标线程的上下文中排队一个任务,调用 GetThreadDescription 。 GetThreadDescription 函数被执行时,会 自动在目标进程的堆上分配内存 ,并将线程名(即我们的Shellcode)写入该内存。该函数会将指向这块新内存的指针存入其输出参数 ppszThreadDescription 中。 至此, Shellcode已被成功写入远程进程内存 ,且权限为RW(可读可写)。 3.2 第二步:存储指针与定位PEB GetThreadDescription 返回的指针 ppszThreadDescription 需要存储在目标进程的一个已知地址,以便后续检索和使用。 首选方案:利用PEB中的未使用字段 PEB结构体中包含一些保留或未使用的字段,它们是存储此指针的理想位置,因为PEB本身位于可读写(RW)的内存区域。 通过 NtQueryInformationProcess (查询 ProcessBasicInformation )来获取目标进程的PEB基地址。 在64位Windows 10/11系统中,偏移量 +0x340 处的 SpareUlongs 数组是一个合适的候选字段。 注意 :PEB结构可能随Windows版本更新而变化,此偏移量需要根据目标系统进行验证和调整(例如使用WinDbg的 dt nt!_PEB 命令查看实际结构)。使用 SpareUlongs 相对安全,但未来可能被系统占用。 备选方案 :在远程进程内存空间中搜索其他可写的已知地址。 3.3 第三步:分配可执行内存与权限变更 写入的Shellcode位于堆上,权限为RW,不可执行。需要将其转移至可执行的内存区域。 方案分析 : | 方案 | 实现方式 | 优点 | 缺点 | | :--- | :--- | :--- | :--- | | 1. VirtualProtectEx | 直接调用 VirtualProtectEx ,将堆上的Shellcode内存页权限改为RWX。 | 步骤简单,无需额外拷贝。 | 高度可疑 。直接创建RWX内存页是恶意软件的经典行为,易被内存扫描器检测。 | | 2. VirtualAllocEx | 调用 VirtualAllocEx 申请新的RWX内存页,然后将Shellcode拷贝过去。 | 新内存页,相对干净。 | 可疑 。申请RWX内存是可检测指标。无法通过APC直接调用(参数超过3个)。 | | 3. ROP链 | 构造Return-Oriented Programming链来调用 VirtualProtect 或 VirtualAlloc 。 | 避免直接调用敏感API,更隐蔽。 | 实现复杂,需要操作线程上下文( Get/SetThreadContext ),会产生更多可疑API调用,易触发告警。 | 结论与选择 : 尽管 VirtualProtectEx 和 VirtualAllocEx 本身是可疑指标,但测试表明,在缺乏其他传统注入指标(如创建线程)的配合下,许多AV/EDR并不会仅因此而被触发。因此,文档中选择 直接使用 VirtualProtectEx 作为实现方案,因其步骤最少。代码中也应提供 VirtualAllocEx 的备选路径。 重要提示 :如果目标进程启用了 DCP(Device Guard Code Integrity) ,任何尝试分配可执行内存的操作都可能会失败。 3.4 第四步:执行Shellcode 通过另一个APC,将执行流程跳转到已具备可执行权限的Shellcode地址。 可以使用 RtlDispatchAPC 作为代理函数来触发APC的执行。 4. 技术总结与规避要点 权限最小化 :始终遵循最小权限原则,避免申请不必要的权限(如 PROCESS_CREATE_THREAD , PROCESS_DUP_HANDLE )。 API选择 :优先使用新型APC API( QueueUserAPC2 + SPECIAL_USER_APC ),以获得最大的线程兼容性和更低的权限要求。 线程选择 :尽力寻找可警告线程,避免创建新线程。 内存操作 : VirtualProtectEx / VirtualAllocEx + RWX内存是当前方案的已知弱点,但在此技术上下文中检测率较低。 理想的终极目标是找到进程中现成的RX内存空洞并复制Shellcode进去,但这在实践中非常困难。 检测规避 :此技术的优势在于 拆解并伪装 了注入步骤: 写入操作由系统API GetThreadDescription 合法完成。 避免了 WriteProcessMemory 和 CreateRemoteThread 这两个最敏感的API。 即使使用了敏感的权限操作API,也因为其上下文(与线程名操作相关)而变得不那么突兀。 5. 结语 利用线程名和APC进行进程注入是一种先进的规避技术(Advanced Evasion Technique),它巧妙地利用了Windows系统的新增功能。其有效性源于将恶意操作分解并隐藏在合法的API调用序列中。 防守方视角 : 对于防御者而言,检测此类技术需要更深入的行为分析,而非简单的API钩取或静态规则。应重点关注: 异常序列 : SetThreadDescription 后紧跟针对其他进程的APC操作。 PEB修改 :对进程PEB中特定字段的写操作。 内存权限变更 :任何将非映像内存(如堆)设置为可执行的操作,尤其是通过 VirtualProtectEx 完成的。 ETW(Event Tracing for Windows) :利用微软提供的ETW日志进行实时监控,可以捕获到这些API的调用及其上下文,是检测此类高级威胁的关键。