近日,瞬犀安全的安全团队捕捉到一个银狐样本,该样本是一个具备极高隐蔽性、采用多级架构且完全无文件落地的复杂恶意软件。该威胁专注于深度防御规避与安全产品致盲,打破传统安全产品的遥测行为链。
样本利用了前沿的进程注入技术(如 IOCP 线程池注入 PoolParty 变种 7 结合句柄劫持),并通过替换 NTDLL 代码段和加载漏洞驱动( BYOVD )来从用户态和内核态双向瘫痪安全监控。其载荷投递采用多级反射式注入,全程在内存中解密与执行(双重压缩与加密的 Shellcode ),并利用白进程 DLL 侧加载和 COM 劫持实现高隐蔽持久化。此外,该恶意软件集成 libwebsockets 网络通信库,支持多协议( HTTP/TCP/UDP )加密通信,在与 C2 服务器建立连接后,直接在内存中创建线程运行下发的 Shellcode ,全程无文件落地,传统的基于文件特征的杀毒软件对这种攻击几乎完全失效,这也是 APT 最常用的免杀手段。
该恶意软件对企业网络构成了严重的系统性风险,具体危害体现在以下三个核心层面:
1. 全面瘫痪端点防御( EDR 致盲)
o 用户态防御失效: 通过动态 API 解析和 NTDLL 代码段替换( Unhooking ),能够绕过绝大多数基于用户态 API 挂钩的 EDR 行为监控。
o 内核态安全受损: 利用 “自带漏洞驱动”( BYOVD )技术,恶意软件可以直接干预内核,关闭或削弱安全软件的驱动程序,让目标主机处于完全不设防的“致盲”状态。
2. 极高的隐蔽性与长效持久化
o 阻断溯源分析: 强烈的 “单进程行为链聚合规避”意识和 PoolParty 注入技术,使得恶意行为被打散到正常的系统线程池中,让传统的基于行为链关联的威胁检测( XDR/EDR )失效。
o 白文件驻留: 结合 COM 劫持、白名单进程和 DLL 侧加载,恶意软件与拥有微软签名的进程深度绑定。每次系统启动时,恶意载荷均以合法身份在内存中动态组装,硬盘上无任何直接的恶意可执行文件落地。
3. 无文件落地的下发载荷执行
o 高性能 C2 通道: 支持 HTTP、 TCP 、 UDP 多协议且配置与载荷双重加密,集成 libwebsockets 高性能网络通信库。
o 载荷内存执行: C2 下发的载荷直接在内存中执行,全程无文件落地。
该恶意程序的所有字符串都不会以明文的方式暴露在文件中,防止被 strings, floss 这类提取工具扫描到。该恶意软件采用了基于 SIMD ( SSE2 )指令集的混淆字符串方式,它不再使用简单的单字节运算,而是利用了处理器的 128 位寄存器 (XMM) ,通过并行、位移、异或和重排 (Shuffle) 操作还原成字符串(例如 API 函数名)。
恶意软件作者通过引入一个全局变量 dword_14001A004 ,打破了纯函数的特性。由于这是一个内存地址,静态分析工具 无法确定这个值在运行时是否会被其他线程或前期初始化的代码修改 。因此,反编译器无法通过编译优化算法去化简这部分代码或自动算出明文内容。
在编译器优化理论中,如果一个函数只依赖于输入参数,且没有副作用,它被称为 纯函数 (Pure Function) 。一般反编译器都具有常量折叠和数据流分析功能。如果解密函数是纯函数,且输入参数是固定的(比如本例无参数),反编译器能在分析时直接计算出结果。
使用 unicorn 来模拟执行这些函数,核心代码如下:
使用函数引用的地址 0x14001A004 作为种子,扫描交叉引用,对这些函数进行 unicorn 模拟。最终得到的字符串列表如下:
该恶意软件通过动态库加载与 API 调用,确保恶意进程在具备管理员权限的环境下运行,以解除后续操作系统级操作的权限限制。校验当前进程令牌的完整性级别。若校验结果返回为 0 (即非管理员权限),控制流将强制跳转至 restart_as_admin 函数,并立即调用 ExitProcess(0) 终止非特权进程,将控制权移交给后续启动的新实例。
restart_as_admin 函数实现了基于 UAC 机制的特权升级,调用 decrypt_str_runas 解密 “ runas ”,并将其赋值给 lpVerb 成员。这是调用 Windows 提权机制的关键参数。该流程将恶意代码自身的绝对路径(通过 GetModuleFileNameW 获取)传入 ShellExecuteExW 函数。随着新进程以管理员权限启动,原始非特权进程随即退出,完成特权环境的切换。
该恶意软件在执行核心的线程池劫持( PoolParty )注入之前,通过探测宿主机上运行的特定终端防御产品( EDR/AV ),动态决定注入的系统级服务宿主。
程序遍历当前系统的进程列表,进行硬编码特征匹配。恶意软件未采用高危的进程句柄请求,仅依赖于只读的进程快照读取,从而降低了在环境探测阶段被杀毒软件主动防御拦截的概率。
360tray.exe 探测
bdservicehost.exe ( BitDefender 核心服务)探测
当检测到上述任意一款安全软件处于运行状态时,恶意软件将注入目标锁定为 Volume Shadow Copy (VSS) 服务。若该服务未运行,恶意软件会调用内部函数 start_vss_service 强制启动,并进入一个带有 Sleep 延迟的轮询循环,直到成功获取 VSS 服务的 PID。
若 PID 检索返回为 0 ,意味着目标 VSS 服务当前处于停止状态。恶意软件将立刻调用 start_vss_service 执行强制提态与激活。函数通过 OpenSCManagerW(0, 0, 2) 获取服务控制管理器的句柄,随后调用 OpenServiceW 显式请求对 VSS 服务的控制权(请求权限掩码为 SERVICE_ALL_ACCESS )。成功获取服务句柄后,调用 StartServiceW 强行更改服务状态机,迫使系统派生合法的 vssvc.exe 进程。
在触发 StartServiceW 之后,当前进程为了规避单进程行为链聚合,再次实施了进程切断。在成功启动了一个全新的自身实例之后,立即结束自己。
这种设计的核心对抗目标并非人工溯源审计(父子进程树在 Sysmon 事件 ID 1 中依然可见),而是旨在精准扰乱现代 EDR (尤其是依赖 ETW-TI 遥测数据的受保护进程 PPL )的自动化上下文聚合与风险评分机制。它将“触发高权限系统服务启动( SCM 交互) ”与“执行后续跨进程注入(句柄窃取与内存操作)”这两个连续的高危行为,在系统底层强行拆分至两个完全独立的进程上下文( EPROCESS )中执行。
· 原实例( PID A ): 仅执行了常规的服务拉起与子进程派生,其单一进程的行为风险评分远低于 EDR 的启发式拦截阈值。
· 新实例( PID B ): 在第二次运行时,由于 VSS 服务已被前置激活,它将直接走通 PID 检索流程,直接获取合法 vssvc.exe 的句柄并执行 PoolParty 劫持。
虽然高级 EDR 具备追踪进程树的能力,但在实际内核级监控中,跨进程生命周期的细粒度行为意图聚合(即将 PID A 的服务状态操控与 PID B 的底层句柄窃取进行实时强关联分析)具有极高的计算开销,且极易引发正常业务系统的误报。攻击者正是利用了 EDR 引擎在 “状态机重置”与“性能开销平衡”上的妥协,通过主动切断单一进程的行为时间线,迫使 EDR 遥测数据碎片化,从而规避了单进程行为链聚合所触发的实时拦截。
若探测确认当前环境不存在上述指定的安全软件,恶意软件将执行降级策略,将目标切换至 Task Scheduler (Schedule) 服务。
该恶意软件将加密后的 Shellcode 嵌入在程序自身, Shellcode 采用了双重压缩与加密的方式。该区域的结构如下:
偏移
大小
意义
0x00
0x01
垃圾数据的长度,该值为 0xA8
0x01
0xA8
垃圾数据
0xA9
0x04
压缩数据的长度,该值为 0xB13
0xAD
0xB13
压缩数据
恶意软件使用标准的 LZNT1 压缩算法,并自己实现了 LZNT1 解压算法。解压后的数据结构如下:
偏移
大小
意义
0x00
0x01
密钥的长度,该值为 0x56
0x01
0x56
密钥内容
0x57
0x04
压缩数据的长度,该值为 0x97B
0x5B
0x97B
压缩数据
恶意软件使用密钥解密之后,得到的数据仍然是 LZNT1 压缩数据,随后恶意软件再次调用自己实现的 LZNT1 解压算法得到 Shellcode 。
恶意软件在进行 NTDLL 脱钩之前会将自己提升至调试权限,以便后续对高权限进程进行内存扫描并执行无线程注入。
恶意软件通过从磁盘加载一份未受污染的 ntdll.dll 干净映像,并强行覆盖当前进程内存中已被 EDR 挂钩的代码段( .text ),从而彻底清除 EDR 在用户态部署的所有监控点。
程序调用 GetSystemDirectoryW 获取系统目录(通常为 C:\Windows\System32 ),随后解密出字符串 \ntdll.dll 并进行拼接,确保精确定位到合法的系统核心动态链接库。
由于 MapViewOfFile 配合 SEC_IMAGE 已经将文件自动展开为内存对齐的 PE 结构,恶意软件开始手动解析当前进程中已加载的 ntdll.dll (由 ProcessHandle 指向其基地址)的 PE 头,以精确定位代码段。程序解密出字符串 .text ,并以双 DWORD ( 8 字节)的宽度逐个比对 Section 名称。一旦匹配成功,说明已成功定位到原始机器码的代码段。
在定位到 .text 段后,恶意软件执行强制覆盖,借助 memcpy 程序将刚才从磁盘映射出来的、未经 EDR 注入挂钩的干净 .text 代码段数据(由 v118 指向),强行覆盖到当前进程正在使用的 ntdll.dll 内存区间。覆写完成后,恶意软件再次调用 VirtualProtect 将该区间恢复为原始保护属性。最后,调用 UnmapViewOfFile 清理磁盘映射缓冲区。
该恶意软件内部实现的 find_pattern_in_memory 函数是一个高度优化、具备硬件加速能力的远程进程内存特征码扫描引擎。在整体攻击链中,该模块负责在目标进程的虚拟地址空间内,检索前置投递的 Shellcode 和 _TP_DIRECT ,这样即可让子进程定位父进程在目标进程中写入的 Shellcode 或其他数据。
恶意软件根据特征码长度( PatternSize ),设计了三级启发式硬件加速匹配
· 当检索特征长度大于 16 字节时,启用 x64 架构的 SSE2 向量化指令集。一次性从内存中并发加载 128 位( 16 字节)数据进行比对。
· 特征码长度处于中等区间( 4 PatternSize 17 字节),采用双字( 4 字节)为对齐基准对比
· 逐字节校验( PatternSize 4 字节)
控制流劫持结构初始化
恶意软件在目标进程中部署 Shellcode 以及伪造线程池任务控制块之前,会先通过内存扫描来检测是否存在上一个进程已经注入的 Shellcode 和 _TP_DIRECT ,这正是开发者为了规避单进程行为链聚合。
如果未搜寻到内存特征就手动部署 Shellcode 和伪造线程池控制块。并且在部署之前,恶意软件已经进行了 NTDLL 脱钩,该过程通过调用已经脱钩的 Native API 以规避用户层挂钩。
为了实现无线程注入,恶意软件通过构造 _TP_DIRECT 来接管控制流。将 Shellcode 的远程地址写入了 _TP_DIRECT 的 Callback 字段中。恶意软件会直接调用 NtSetIoCompletion ,将这个结构体的指针作为 “完成键( Completion Key )”或“重叠结构( Overlapped )”强行推入目标进程的 IOCP 中。目标进程中处于睡眠状态的 TppWorkerThread 被唤醒。工作线程从 IOCP 中取出一个数据包,发现它是一个直接调用( Direct Task )。操作系统此时 不进行任何额外的安全或上下文校验 ,直接将指针强转为 PTP_DIRECT ,读取偏移 0x38 处的 Callback 地址,并立即执行。
typedef struct _TP_TASK { struct _TP_TASK_CALLBACKS * Callbacks ; UINT32 NumaNode ; UINT8 IdealProcessor ; char Padding_242 [ 3 ]; struct _LIST_ENTRY ListEntry ; } TP_TASK , * PTP_TASK ; typedef struct _TP_DIRECT { struct _TP_TASK Task ; UINT64 Lock ; struct _LIST_ENTRY IoCompletionInformationList ; void * Callback ; UINT32 NumaNode ; UINT8 IdealProcessor ; char __PADDING__ [ 3 ]; } TP_DIRECT , * PTP_DIRECT ;
这 0x48 字节的数据内容如下:
旁注: Windows 线程池 TP_DIRECT 机制与安全盲区
机制概述 在 Windows 操作系统的线程池( Thread Pool )架构中,系统通过不同类型的结构体来管理异步任务。常规任务(如 TP_WORK )会被投递至线程池内部的任务队列中,由调度器进行复杂的环境与状态校验后方可执行。而 _TP_DIRECT 是一种高度优化的 “直接执行”结构。它不经过常规的线程池队列排队,而是被直接推入底层的 I/O 完成端口( IOCP )。
信任快速通道与滥用原理 当底层工作线程( Worker Thread )从 IOCP 唤醒并取出 _TP_DIRECT 数据包时,为了追求极致的异步 I/O 处理性能,系统调度器会跳过针对常规工作项的严格上下文验证,直接提取结构体偏移 0x38 处的 Callback (回调函数)指针并跳转执行。
攻击者(采用 PoolParty Variant 7 技术的恶意软件)正是利用了操作系统的这一 “信任快速通道”。攻击者仅需在目标系统服务中伪造一个 72 字节( 0x48 )的 _TP_DIRECT 结构体,将恶意载荷地址覆盖至回调函数指针处。随后通过合法的 IOCP 通知触发执行,即可在无需调用任何高危 “线程创建” API 的前提下,实现高权限的静默代码执行。这种利用底层调度机制合法性的攻击,使得传统的行为监控难以进行有效拦截。
在执行完 PoolParty 注入后,代码跳到了 LABEL_164 。这里的目的是 启动自身的一个新实例 。
IOCP 劫持
如果前一个进程(比如父进程)已经在目标进程的中部署了 Shellcode 和 _TP_DIRECT 结构体,恶意软件会搜寻 Shellcode 的内存地址,并重新构造完全相同的 _TP_DIRECT 结构体作为内存特征,调用内存扫描函数定位该结构体的基地址。
恶意软件首先进行一次系统级的句柄快照,调用 DuplicateHandle 将目标进程的句柄复制到自身。
对于每一个复制到自身的句柄,恶意软件会进行一次对象查询,直到找出 IoCompletion 对象。
在完成载荷投递、 _TP_DIRECT 伪造和句柄窃取之后,恶意软件进入执行链的最终环节。
恶意软件通过调用 PostQueuedCompletionStatus (或在 API 解析失败时,降级调用未文档化的 Native API ZwSetIoCompletion )向目标进程的 IOCP 队列中手动插入一个完成包。 在调用该 API 时,恶意软件将预先写入目标进程内存的 _TP_DIRECT 作为 dwCompletionKey 或 lpOverlapped 参数传入。操作系统内核接收到该请求后,将其视为一次合法的异步 I/O 完成事件,并将包含该结构体地址的数据包压入目标进程 IoCompletion 的先进先出( FIFO )队列中。
Windows 线程池引擎在处理此类直接任务( Direct Task )时,为保证极高的调度性能,忽略了对任务来源与合法性的深度校验。调度逻辑将该指针强制转换为 _TP_DIRECT 结构体指针。工作线程直接读取该结构体偏移 0x38 处的 Callback 成员变量。由于该位置已被恶意软件覆写为 Shellcode 的物理内存基址,工作线程随后执行函数指针调用,跳转至预先写入的 Shellcode。
在程序遇到未预料到的异常或者注入已经执行完毕,程序的控制流会进入这里。该恶意软件会拍摄一次进程快照,一旦检测出 bdservicehost.exe ,程序随即触发 Sleep(1000u) (挂起 1 秒),并重新执行快照拍摄与检索逻辑。
如果当前系统运行的程序中不存在 bdservicehost.exe 或者该进程已经被关闭。恶意软件开始执行自毁。
恶意软件首先通过 GetModuleFileNameW 获取当前执行文件的绝对路径,并获取 %TEMP% 目录。随后利用 GetTempFileNameW 生成一个临时的、随机命名的文件路径。程序调用 MoveFileW ,将当前正在运行的、位于显眼路径下的原始可执行文件,移动到 %TEMP% 目录下的临时占位文件中。由于 Windows 文件系统允许在同一逻辑分区内对打开了执行句柄的文件进行重命名或目录项迁移,该操作可以合法执行。移动完成后,恶意软件调用了 MoveFileExW 并传入参数 4u ,对应 Windows 内核常量 MOVEFILE_DELAY_UNTIL_REBOOT 。执行该调用时,系统并不会立刻删除文件,而是向注册表的以下路径写入一个删除标记项: HKLM\System\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations
最终,程序调用 ExitProcess(0) 终止当前进程。在系统下一次重启时,内核的会话管理器进程( smss.exe )会在配置子系统初始化之前、且在任何用户态程序启动之前,优先读取该注册表项,由内核将 %TEMP% 目录下的临时文件彻底抹除。
该 Shellcode 的布局如下:
偏移
大小
意义
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。