利用双机调试环境对杀软内核研究
前言
在 Windows 架构中,杀软内核驱动通常扮演着“超级裁判”的角色。它通过在内核中植入一系列通知回调(Notification Routines)和微过滤监听器(Minifilter Callbacks),将自己强行插入到系统处理关键动作(如进程启动、文件读写、内存映射)的路径上。
分析这些驱动的概念核心可以归纳为以下三个维度:
监控点的“锚定”与“劫持”
杀软并不生成系统动作,它只“订阅”动作。分析的首要目标是确认它在内核中注册了哪些眼线。
- 注册回调:利用
PsSetCreateProcessNotifyRoutine等标准接口,确保任何新进程的诞生都必须先经过它的逻辑判断。 - 对象过滤:通过
ObRegisterCallbacks监控句柄操作,防止恶意程序尝试打开 LSASS 等敏感进程的句柄。 - 文件过滤:利用 Minifilter 框架,在文件到达磁盘前进行扫描,这是拦截勒索软件或恶意载荷(Payload)落地的核心战场。
策略池的“匹配”与“决策”
当一个动作被拦截下来后,驱动内部会进入逻辑决策阶段。
- 静态特征匹配:将当前进程名、路径或证书信息与内存中的全局规则链表(例如
MpBmDocOpenRules)进行快速比对。 - 动态行为评分:根据进程短时间内的 API 调用序列(例如:申请内存 -> 修改权限 -> 远程注入)进行启发式评估。
- 内核态与用户态的通信:内核驱动(EDR Driver)通过
FilterCommunicationPort将复杂的判定任务“外包”给用户态的高性能分析服务,并根据返回的结果决定是否给该动作判“死刑”。
“阻断”与“放行”的终极对抗
分析的终点在于理解杀软如何下达指令。
- 状态码注入:在回调中将
CreationStatus修改为STATUS_ACCESS_DENIED,从内核底层撤销该动作的合法性。 - 上下文擦除:在恶意代码执行前,通过抹除其内存映射或强制结束其线程池来实现无感阻断。
搭建双机调试环境
本文以主机 Windows 11 调试虚拟机 Windows 10 为例。
虚拟机配置步骤
- 打开虚拟机设置,移除打印机,避免占用串口位置。
- 添加一个串行端口,配置参数需与示例保持一致。
- 添加串口管道,路径设置为
\.\pipe\com_1。 - 启动虚拟机,以管理员身份运行命令提示符,执行以下命令:
bcdedit /set “{current}” bootmenupolicy Legacy bcdedit /dbgsettings SERIAL DEBUGPORT:1 BAUDRATE:115200 bcdedit /copy “{current}” /d “Debug” bcdedit /debug “{<上一步返回的GUID>}” on - 运行
msconfig,在“引导”选项卡中选择名为“Debug”的启动项。
主机配置与连接
- 在主机上使用新版 Windbg 进行调试。
- 在 Windbg 中配置串行端口(例如
com1,波特率115200)。 - 重启虚拟机,在启动菜单中选择“Debug”项。
- 如果 Windbg 显示“connect”,则表示双机调试环境搭建成功。
深入分析杀软内核回调机制
一、 创建进程回调分析 (PsSetCreateProcessNotifyRoutineEx)
杀软通过 PsSetCreateProcessNotifyRoutineEx 注册回调。每当一个新的 EPROCESS 被分配并准备运行时,内核会暂停进程,并逐一询问杀软进程的安全性。
1. 定位与查看回调
在 Windbg 中,可以查看 Windows Defender (WdFilter) 的进程创建回调函数:
1: kd> u WdFilter!MpCreateProcessNotifyRoutineEx L50
通过对反汇编代码的分析,可以识别出 Defender 的多个安全检查点。
2. 关键检测点分析
- 文件名检查与规范化:
在地址fffff80a83ab8d6c处,代码会获取ImageFileName指针。随后,在fffff80a83ab8d8f调用WdFilter!MpGetImageNormalizedName。此举并非直接信任原始路径,而是进行路径格式化,旨在防止通过符号链接或长路径名等方式进行的路径欺骗。 - 恶意文件处理与状态码注入:
在地址fffff80a83ab8ea2处,指令mov dword ptr [rdi+40h],0C0000022h将拒绝访问的状态码 (STATUS_ACCESS_DENIED) 写入_PS_CREATE_NOTIFY_INFO结构的CreationStatus` 字段,从根本上否决进程创建。 - 父进程上下文检测:
在fffff80a83ab921e调用WdFilter!MpGetProcessContextById获取父进程上下文。随后在fffff80a83ab9230进行标记 (bts dword ptr [rcx+34h], 0Fh),这是为了检测父进程的上下文信息,可能用于判断创建链条是否可疑。 - 文档编辑器/宏病毒防御:
在fffff80a83ab9241调用WdFilter!MpSetProcessDocOpenRule。此函数会回溯父进程,检查其是否为文档编辑器(如msaccess.exe,powerpoint.exe,acrord32.exe),并检测其是否试图启动子进程(如cmd,powershell`),以防御利用文档宏或脚本的威胁。 - 用户态通信(云查杀/启发式):
在fffff80a83ab93e4调用WdFilter!MpSendProcessMessage`。此调用可能负责将进程信息发送给用户态服务,进行更复杂的云端查杀或启发式分析。 - 特殊进程处理:
MpSetProcessExempt: 判断进程是否为关键系统进程,这类进程可能享有豁免权。MpSetProcessHardening: 应用相关安全策略,例如禁止进程加载非微软签名的 DLL。
- 检测创建进程的调用者:
在fffff80a83ab9300调用IoThreadToProcess`,旨在获取发起创建请求的线程所属的进程,并与当前目标进程进行比较,以检测是否存在跨进程的远程创建行为。 - 白名单校验:
通过调用FltParseFileName获取文件全路径,再调用RtlEqualUnicodeString或RtlPrefixUnicodeString与预定义的白名单路径或字符串进行比对,若匹配则放行。 - 扫描超时设置:
查询全局数据结构,可发现 Defender 默认的扫描超时时间(例如,示例中地址fffff80a83a779a8处的值000013880000ea60可能代表 60 秒)。
二、 线程创建回调分析 (PsSetCreateThreadNotifyRoutine)
现代 EDR/杀软广泛利用 PsSetCreateThreadNotifyRoutine 监控线程创建,使得传统的 CreateRemoteThread 注入方式极易被检测。
1. 定位回调数组
通过反汇编系统函数 PspSetCreateThreadNotifyRoutine,可以定位到线程回调数组 PspCreateThreadNotifyRoutine 的地址(例如 fffff800'16f89970)。
2. 分析已注册的回调
查询该地址,可以发现多个驱动已注册回调。以 Windows Defender 为例:
1: kd> dps ffff8283`48008580 L2
ffff8283`48008580 00000000`00000020
ffff8283`48008588 fffff80a`83aaf690 WdFilter!MpCreateThreadNotifyRoutine
1: kd> dps ffff8283`480086d0 L2
ffff8283`480086d0 00000000`00000020
ffff8283`480086d8 fffff80a`83ab5db0 WdFilter!MpCreateThreadNotifyRoutineEx
MpCreateThreadNotifyRoutine:通常进行基础过滤,例如检查全局数据结构MpData以判断创建者是否在白名单内。MpCreateThreadNotifyRoutineEx:此函数更为关键,它能够获取PS_CREATE_THREAD_NOTIFY_ROUTINE_EX结构,其中包含线程的初始栈空间和起始地址。它的一项核心检测是检查StartAddress是否指向一个合法的、具有SEC_IMAGE属性的内存映射区域。如果线程的起始地址指向通过VirtualAlloc等 API 分配的普通内存区域,则会被立即识别为可疑的代码注入。
3. 关键检测逻辑
- 系统进程豁免:代码中
cmp r12d,4的指令表明,系统进程(PID 通常为 4)创建线程可能不受监控。 - 跨进程注入检测:函数会获取发起线程创建的当前进程 PID,并与目标进程 PID 进行比较。若不相等,则标记为跨进程注入。
4. 第三方杀软分析(以火绒为例)
安装并重启后,可以在同一回调数组中发现火绒驱动 (sysdiag) 注册的回调函数。通过反汇编其函数(如 KslD!tk::COSCallback::CreateThreadNotifyRoutineEx),可以发现其内部同样维护了一套复杂的回调链,对线程创建进行迭代检查和过滤。