高级进程注入之利用线程名和APC实现进程注入(上)
字数 2602 2025-09-23 19:27:38
利用线程名和APC实现高级进程注入技术(上)
一、前言
进程注入是攻击方武器库中的核心技术之一,主要用于:
- AV/EDR躲避:将恶意代码隐藏在合法进程中
- 操作现有进程:如dump lsass进程凭据
- 权限提升
传统注入技术面临严格监控,因此需要研究新型注入方法。本文介绍基于线程描述(Thread Description)和APC(Asynchronous Procedure Call)的高级注入技术,可有效绕过大多数AV/EDR的检测。
二、线程名在攻击中的应用场景
2.1 IPC(进程间通信)
- 机制:通过
SetThreadDescription设置线程描述,GetThreadDescription获取描述 - 优势:提供隐蔽的进程间通信渠道
- 参考实现:https://github.com/LloydLabs/dearg-thread-ipc-stealth
2.2 躲避内存扫描
- 原理:将shellcode存储在线程名中(内核模式结构),规避用户模式内存扫描
- 参考实现:https://gitlab.com/ORCA000/t.d.p
2.3 辅助内核模式利用
- 用途:在用户层为内核层分配内存空间
- 参考实现:https://web.archive.org/web/20221009194014/https://blahcat.github.io/posts/2019/03/17/small-dumps-in-the-big-pool.html
2.4 进程注入变种
2.4.1 双管枪技术(Double Barrel)
- 技术特点:通过线程劫持+ROP链实现代码注入
- 缺点:Windows版本依赖性强,可能导致目标应用不稳定
- 代码实现:https://www.lodsb.com/shellcode-injection-using-threadnameinformation
2.4.2 线程名调用技术(本文重点)
- 技术特点:
- 通过线程描述传递shellcode
- 使用APC机制执行代码
- 对shellcode无特殊限制
- 不干扰原始线程执行
- 实现流程:
- 通过线程描述发送shellcode到目标进程
- 通过APC调用
GetThreadDescription获取描述 - 修改内存权限为可执行
- 通过另一个APC执行shellcode
2.4.3 DLL注入变种
- 特点:DLL路径通过线程名传递,替代传统的
VirtualAllocEx和WriteProcessMemory
三、关键API详解
3.1 GetThreadDescription/SetThreadDescription
3.1.1 函数原型
HRESULT GetThreadDescription(
[in] HANDLE hThread,
[out] PWSTR *ppszThreadDescription
);
HRESULT SetThreadDescription(
[in] HANDLE hThread,
[in] PCWSTR lpThreadDescription
);
3.1.2 技术要求
- 系统版本:Windows 10 1607+
- 权限要求:
THREAD_SET_LIMITED_INFORMATION - 缓冲区限制:最大65536字节(16页),实际可用65534字节
- 格式要求:Unicode字符串(以L'\0'结尾)
3.1.3 底层实现
// SetThreadDescription内部实现
#define ThreadNameInformation 0x26
HRESULT __stdcall SetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription)
{
NTSTATUS status;
_UNICODE_STRING DestinationString;
status = RtlInitUnicodeStringEx(&DestinationString, lpThreadDescription);
if (status >= 0)
status = NtSetInformationThread(hThread, ThreadNameInformation,
&DestinationString, 0x10u);
return status | 0x10000000;
}
3.1.4 内核存储位置
- 存储位置:
ETHREAD->ThreadName - 内存类型:NonPagedPoolNx(不可执行非分页池)
- 监控能力:ETW已注册相关事件,可检测线程名设置操作
3.2 空字节问题解决方案
3.2.1 问题描述
官方API要求线程名以空WCHAR(2字节)结尾,连续NULL字节会导致截断
3.2.2 解决方案
- 传统方法:使用shellcode编码器消除空字节
- 高级方法:直接操作
UNICODE_STRING结构,绕过空字节限制 - 关键技术:使用
RtlInitUnicodeStringEx初始化指定长度的缓冲区
3.3 NtQueueApcThreadEx2
3.3.1 APC API演进历程
| API版本 | 引入系统 | 特点 |
|---|---|---|
| NtQueueApcThread | Windows NT 4.0 | 传统APC实现 |
| NtQueueApcThreadEx | Windows Vista | 扩展功能 |
| QueueUserAPC2 | Windows 10 1703 | 用户层新API |
| NtQueueApcThreadEx2 | Windows 10 1903 | 最新内核API |
3.3.2 函数优势
- 绕过alertable限制:使用
QUEUE_USER_APC_SPECIAL_USER_APC标志 - 参数传递:支持3个参数(优于线程创建的1参数限制)
- 隐蔽性:避免触发线程创建回调(
PsSetCreateThreadNotifyRoutine)
3.3.3 函数原型
NTSTATUS NtQueueApcThreadEx2(
[in] HANDLE ThreadHandle,
[in] HANDLE UserApcReserveHandle,
[in] PPS_APC_ROUTINE ApcRoutine,
[in] PVOID SystemArgument1,
[in] PVOID SystemArgument2,
[in] PVOID SystemArgument3
);
3.4 RtlDispatchAPC
3.4.1 技术特点
- 合法代理函数:避免直接执行私有内存的检测
- 参数兼容性:支持3个参数,完美匹配APC机制
- 调用方式:通过序号调用(未导出函数名)
3.4.2 使用要求
// 调用参数要求
RtlDispatchAPC(
shellcode_address, // 参数1: shellcode地址
shellcode_param1, // 参数2: 自定义参数1
shellcode_param2 // 参数3: 自定义参数2
);
3.4.3 替代方案参考
其他可用于代理执行的回调函数(详见Hexacorn博客):
- https://www.hexacorn.com/blog/2016/12/17/shellcode-ill-call-you-back/
四、检测与防御
4.1 检测方法
- ETW监控:检测线程名设置事件
- API监控:监控
NtSetInformationThread(ThreadNameInformation)调用 - 行为分析:检测异常APC队列操作
4.2 防御建议
- 权限限制:严格控制
THREAD_SET_LIMITED_INFORMATION权限 - 进程监控:加强对敏感进程的线程操作监控
- 内存保护:监控非标准内存执行行为
五、参考资源
- CheckPoint研究原文:https://research.checkpoint.com/2024/thread-name-calling-using-thread-name-for-offense/
- Windows进程注入技术大全:BlackHat UAS 2019演讲
- 各种注入技术实现代码库
下篇预告:将详细讲解具体实现代码、实战案例和高级规避技术。