1. 缘起:从 Edge 下载的“deepseek 电脑版”

这次分析的样本来自 Edge 搜索下载的所谓“deepseek 电脑版”。把它放进隔离虚拟机后,表面上几乎没有任何动作:没有明显窗口、没有稳定的网络行为、连预期中的安装流程也没有出现。

起初怀疑是样本损坏。后来结合静态分析的入口结构和系统重启后出现的异常,才怀疑它存在虚拟机环境检测,并进一步发现它释放了驱动级组件、修改了系统代码完整性策略。整个过程并不顺利,但留下的证据链比较完整,值得整理成一篇日报。

2. 启动路径:一段可疑的运行时初始化

样本的入口 start 非常短,只做了三件事:

1
2
3
4
5
start
├─ sub rsp, 28h
├─ call sub_14001CE6C ; 初始化运行时值
├─ add rsp, 28h
└─ jmp sub_140001106 ; 进入真正的启动逻辑

sub_14001CE6C 依次调用 GetSystemTimeAsFileTime、GetCurrentProcessId、GetCurrentThreadId、GetTickCount 和 QueryPerformanceCounter,把这些结果异或组合成一个运行时值,保存在 qword_14003D0A8,同时在 qword_14003D0B0 保存它的按位取反结果。

单看这段代码,它更像安全 cookie / 运行时值初始化,并不能单独证明存在虚拟机检测。真正引起怀疑的是后面的启动路径:

1
2
3
4
sub_140001106
└─ call sub_14000113C
├─ rax != 0 → 继续进入后续逻辑
└─ rax == 0 → jz short loc_14000112F,走退出路径

在虚拟机中运行原始文件时,样本没有继续表现出明显行为。为了确认这个条件分支的含义,我在隔离环境中做了一次最小 patch 实验。

3. 验证分支含义:一次最小 patch 实验

方案 A 很直接:把 test rax, rax 后面的

1
jz short loc_14000112F

改成两个字节的 NOP NOP(90 90),让“检测失败”也不再跳向退出分支:

1
2
3
4
5
call sub_14000113C
test rax, rax
nop
nop ; jz 被抹掉,不再跳转退出分支
jmp loc_1400014E0 ; 无条件进入真实逻辑

最初的担心是样本存在内存完整性校验:进程启动后会校验 .text 段内存是否与磁盘原始内容一致,一旦发现被 patch 就判定篡改并退出。实际测试发现该样本没有 CRC 校验,修改后成功在虚拟机中继续运行。

调试器中的观察也印证了这一点:在 install_q0_3_02+0x166a8 下断,单步进入 sub_14001CE6C 后 rax=00002b992ddfa233,qword_14003D0A8 与取反值均已初始化;随后跳入 sub_140001106,当 sub_14000113C 返回 rax=2 时,test rax, rax 后不会走退出分支。

这个实验支持“启动条件判断 / 环境检测”的假设,但必须保持克制:它只能证明该分支控制样本是否继续执行,不能单独断言样本只检测虚拟机。条件判断可能混合了调试器、系统版本、权限、网络或其他运行环境特征。

4. 一次代价很高的失误

确认启动分支后,我继续运行样本,却没有提前在关键位置设置足够的断点和行为监控。结果样本跑飞,感染了虚拟机并自动重启。

这次失误的教训很直接:“能跑起来”并不等于“值得继续跑”。对驱动级样本,运行前必须准备好快照、行为监控和关键点断点,否则一次误操作就会污染整个实验环境,让后续分析从“分析样本”变成“处理事故”。

5. 重启后的异常:驱动加载被代码完整性策略拦截

重启之后,虚拟机中的 SnowSword 驱动加载出现异常,用户态调用 ZwLoadDriver 返回 0xC0E90002,但用户态功能尚可使用。同时,干掉了银狐驱动 ranchserv.jpg 的服务后,它还会被自动拉起。

奇怪的是,挂上 WinDbg 后,同一个驱动又表现为可以加载。这个差异不能简单归结为“驱动文件时好时坏”。沿着内核加载路径下断点,经过 NtLoadDriver → IopLoadDriverImage → IopLoadDriver → MmLoadSystemImage 后,WinDbg 直接给出了关键提示:

1
2
violated SI policy, will fail to load for scenario 0 !
It is now allowed to load because BD is attached.

这说明加载失败是由 SI(代码完整性)策略拦截导致的,而不是文件损坏或注册表缺失;BD 可以理解为内核调试器附加状态。调试器附加后允许加载,恰好解释了“普通启动失败、挂 WinDbg 成功”的表象,但它不能证明驱动签名有效,也不能证明系统策略已经恢复。

6. CodeIntegrity 日志与 SiPolicy.p7b 时间戳

在 Microsoft-Windows-CodeIntegrity/Operational 日志中,SnowSword.sys 对应了两类关键事件:

  • 事件 3077:代码完整性判断该驱动不满足 Authenticode 签名级别要求,或违反了当前代码完整性策略(Policy ID:{a244370e-44c9-4c06-b551-f6016e563076});
  • 事件 3004:系统无法验证映像完整性,提示找不到文件哈希,可能是签名错误、文件损坏或未知来源软件。

日志时间覆盖 2026/8/27 16:29、17:23 和 2026/8/28 7:27、7:30,被加载对象均为 C:\Users\21607\Desktop\SnowSword.sys。

查看 System32\CodeIntegrity 目录时,SiPolicy.p7b 的大小为 24,972 字节,LastWriteTime 为 2026/8/27 17:54:50,明显晚于同目录下其他代码完整性策略文件。结合样本已经具备修改文件、注册表和内核状态的高风险能力,可以提出一个较强的分析判断:系统代码完整性策略文件被样本篡改,或者至少被置于与干净系统明显不同的状态。不过,在没有同版本干净系统基线、文件哈希和签名链之前,仍应把它表述为“基于现有证据的高度疑似篡改”,而不是绝对结论。

如果需要恢复,正确做法是从干净的同版本 Windows 10 机器导出出厂默认策略,替换虚拟机中被篡改的 SiPolicy.p7b。同时要注意 EFI 分区中仍保留 SiPolicy.p7b 的副本,需要一并修改,否则启动链可能继续使用被篡改的策略。

7. 伪装驱动 ranchserv.jpg 的接口

样本释放出的一个关键文件名是 ranchserv.jpg。从扩展名看它像图片,但实际被当作驱动文件加载。对这个驱动的派遣函数反编译后,可以看到它根据 IRP_MJ_DEVICE_CONTROL 的控制码分派不同功能,其中三个接口尤其值得关注:

控制码 行为 潜在风险
0x22E04C 接收文件路径,打开文件后执行未公开操作 可篡改文件内容、清零 PE 头或签名,导致目标驱动被内核判定为无效镜像
0x22E048 打开并操作注册表键 可删除或修改安全工具的服务项,或修改驱动加载相关注册表配置
0x22E014(材料标注) 向某个内核结构中的函数指针数组写入任意值 若该结构是文件系统过滤回调、映像加载通知或对象管理器回调,可挂钩或破坏驱动加载路径,使安全工具驱动加载被拦截或返回错误

此外,0x22E044 附近还有版本查询分支,会向调用方返回 2.0.2 之类的字符串;其余分支则处理数据复制、结构初始化等操作。

这个接口集合解释了后续现象:如果攻击者通过用户态客户端向该驱动传入 ARK/HIPS 驱动文件路径,它可以清零 PE 头或篡改签名;如果传入注册表路径,它可以破坏安全工具的服务项;而内核结构写入能力则可以直接干预驱动加载路径,使安全工具在加载阶段就被拦截。

8. 隐藏与对抗:安全模式下仍然看不见

分析过程中还发现了一个隐蔽手法:在安全模式下,即使勾选“显示隐藏文件”,C:\ProgramData 的普通 dir 也看不到银狐的可执行文件,只能看到 Dbg、Huorong、Microsoft 等常规目录。

使用 dir /a 后,才暴露出:

1
2
3
4
C:\ProgramData>dir /a
2026/08/27 15:02 <DIR> ePq5kdxo
2026/08/27 15:02 2,949,632 Oaxs10zK.exe
2026/08/27 15:02 <DIR> VMHKT1Zo

原因是被设置了“系统”+“隐藏”双重属性:只勾选“显示隐藏文件”不足以看到它们,必须同时勾选“显示系统文件”才会显形。

处置时,删除文件后不能直接留空,否则服务或残留进程可能会把它重新创建。更稳妥的做法是:删除后用同名空目录占位,取消 SYSTEM、Everyone 和当前账户对该空目录的全部访问权限,并把目录所有者设为 SYSTEM,让恶意组件既无法重建文件,也无法通过目录权限写入新副本。

9. 处置思路与证据保全

样本已经执行过驱动级动作并触发自动重启后,继续在原虚拟机中“修修补补”并不是优先选择。更稳妥的顺序是:

  1. 立即断开网络,保存虚拟磁盘、内存和事件日志副本;
  2. 记录服务项、驱动文件路径、文件哈希、创建时间和签名验证结果;
  3. 对 CodeIntegrity/Operational、System、Service Control Manager 和启动日志做时间线整理;
  4. 使用可信来源重新建立同版本系统快照,再对比 System32\CodeIntegrity 及 EFI 分区中的策略文件;
  5. 只有在证据已经保全后,才在副本上进行策略恢复和驱动加载验证。

恢复策略时应使用与目标系统版本和补丁级别匹配的可信基线,并先确认恢复操作不会破坏取证证据。EFI 分区中的副本也需要纳入检查范围;只替换系统目录中的单个文件,不能自动证明启动链已经恢复。恢复后应重新启动并复核代码完整性事件、策略状态和驱动签名,而不是只看一次 ZwLoadDriver 返回值。

10. IOC 与复核清单

目前能确认或需要重点复核的线索包括:

类型 线索
伪装驱动 ranchserv.jpg,实际按驱动用途加载;对应服务被删除后会被自动拉起
可疑样本 从 Edge 搜索下载的所谓“deepseek 电脑版”安装包(调试符号名 install_q0_3_02)及其释放文件
落地文件 C:\ProgramData\Oaxs10zK.exe(2,949,632 字节)
随机目录 C:\ProgramData\ePq5kdxo、C:\ProgramData\VMHKT1Zo
系统影响 自动重启、ZwLoadDriver 返回 0xC0E90002、SnowSword 驱动加载异常、ranchserv.jpg 服务自动拉起
完整性日志 CodeIntegrity 事件 3004、3077(Policy ID:{a244370e-44c9-4c06-b551-f6016e563076})
关键策略文件 System32\CodeIntegrity\SiPolicy.p7b(24,972 字节,2026/8/27 17:54:50)及 EFI 分区对应副本

上表中的路径和文件名来自本次环境记录,不能直接当作通用 IOC。实际处置时还应补充 SHA-256、签名证书链、服务注册表路径、父子进程关系和网络连接记录,并在多个时间点重复采集,避免把残留文件误当成当前活动组件。

11. 复盘:这次不会忘记的几件事

  • 在运行样本前先做快照,并准备第二份只读分析副本;
  • 先建立基线,再运行样本,至少记录驱动、服务、代码完整性策略和启动项;
  • 关键分支、服务创建、驱动加载和重启触发点必须提前布置观测手段;
  • 不把反编译器命名当成事实,所有高风险结论都要结合交叉引用、调用栈和系统日志;
  • “挂调试器后可以加载”只说明调试上下文改变了系统行为,不表示样本或驱动可信;
  • 对驱动级样本,恢复快照通常比在已感染系统中继续操作更可靠;
  • 排查隐藏文件时,要同时勾选“显示系统文件”,不要只依赖“显示隐藏文件”。

总结

从目前证据看,这个银狐样本至少表现出三条值得警惕的线索:使用图片扩展名伪装驱动、通过高权限接口修改文件/注册表/内核状态,以及根据运行环境决定是否继续执行。随后出现的自动重启和 SnowSword 驱动加载失败,则把样本行为与系统代码完整性策略异常联系到了同一条证据链上:SiPolicy.p7b 的时间戳晚于其他策略文件,CodeIntegrity 日志持续报告 3077/3004,WinDbg 明确提示 SI policy 拦截,而 ranchserv.jpg 恰好具备能够造成这些影响的文件、注册表和内核写入能力。

安全分析仍然需要克制。一次虚拟机实验可以提出假设,却不能替代完整取证;一次日志告警可以指出策略冲突,却不能单凭时间戳证明所有文件都由同一个样本修改。只有把静态分析、运行时观测、代码完整性日志、可信基线和快照前后差异放在同一条证据链上,才能更接近可靠结论。

下一步会在全新快照中补齐文件哈希、服务时间线和策略文件签名信息,并继续完善 SnowSword 对异常驱动和代码完整性事件的关联展示。

相关项目与联系

项目SnowSword —— Windows 10/11 x64 内核分析与 Anti-Rootkit 工具,源码与发布包在 Gitee。

技术合作Windows 内核 / 驱动开发 / 逆向分析 / 兼容性开发,范围与边界见 技术合作。

联系2160752730@qq.com · QQ 2160752730 · 微信 lst_3258110