「影」核心系统「根」需要在一些开启了特殊特性的机器上才能部署成功,逆向找到成功部署条件!成功部署「影」核心系统,即成功运行shadow_panel.exe,控制台程序成功运行进入至输入终止密码的终端。(满分0.5分)
启动阶段执行了如下操作:
Phase 0(sub_144A07540):Hyper-V + IOMMU 检测
CPUID(1) → 检查 ECX bit 31(Hypervisor Present bit)
CPUID(0x40000000) → 验证 Hypervisor vendor = “Microsoft Hv”
IOMMU (DMAR/IVRS) → 通过 NtQuerySystemInformation(76) 查询 ACPI 固件表,检查是否存在:
- Intel VT-d: DMAR 表签名 0x52414D44
- AMD-Vi: IVRS 表签名 0x53525649
Phase 1(sub_144A075F0):VHD路径准备
准备 VHD 部署路径 C:\Windows\Temp\WdiServiceHost\
清理旧文件
Phase 2(sub_144A07620):驱动部署
- 调用 sub_144A05540 进行驱动部署:
1. 先执行 CPUID(0xDEADBEEF, 0x114514),若 EAX == 0x01919810 则说明hypervisor已活跃,跳过部署
2. 生成随机服务名,构建路径 + “.sys”
3. CreateFileW → WriteFile(驱动blob来自 .data 段 unk_145CCA010)→ CloseHandle
4. 通过.tvm0中的代码创建/挂载VHD并部署驱动
5. OpenSCManagerW → 清理服务 → DeleteFileW
Phase 3(sub_144A07650):Hypervisor激活验证
再次 CPUID(0xDEADBEEF, 0x114514)
验证 EAX == 0x01919810(26318864 十进制)
确认 HyperCharge hypervisor 已成功安装并激活
因此需要机器需要支持IOMMU,并且开启了Hyper-V之后,通过管理员权限启动shadow_panel.exe即可进入输入密码的终端。启动后可以看见输入框

「根」使用特殊方法,对操作系统底层进行了攻击,并借此将关键核心代码隐藏了起来,分析其完整实现流程。(满分2.5分)
在题目中内嵌了一个.sys文件,在初始化时将该驱动重命名为一个随机字符串,然后加载驱动。

该驱动受到tvm保护,虚拟化程序较深,逆向不太现实。


尝试去dump驱动调用的API序列

可以发现驱动有申请物理内存并进行mapping的行为,联想到程序强制要求开启Hyper-V,并且通过调试能够发现.exe程序的check是通过CPUID指令实现的。

通过AI分析可以了解大概的check过程

猜测驱动映射了一段VMX内存到EPT的不可见的区域,这样就能做到对操作系统不可见,于是想到通过VMware的debugstub来验证猜想,先获取.exe的内存基地址

之后通过gdb连接debugstub在cpuid前给用户态程序下断点

单步执行后发现进入了一段全新的代码:

并且其中有vmresume\vmlauch等指令,确认走的是hyper-V层
基本可以实锤程序运行后会加载隐藏的驱动,驱动中映射内存到操作系统不可见区域,并且所有VMX指令在运行时由驱动动态生成,防止静态分析,最后程序执行CPUID,通过VMX实际执行校验逻辑,以此来隐藏真正的核心代码逻辑。
编写检测代码,检测(2)中「影」核心系统攻击操作系统底层的特殊方法。(满分2分)
观察到程序每次启动时通过先执行 CPUID(0xDEADBEEF, 0x114514),若 EAX == 0x01919810 则说明hypervisor已活跃,跳过部署,可以通过同样的方法,主动发送同样的请求,若返回结果为0x01919810,则说明驱动已经部署了VMX代码。
这是部署前发请求的结果:

这是部署后发请求的结果:

可以看到正确检测到了题目的程序是否已经攻击操作系统底层。
程序见附件中detect_hyperch.c
也可以通过依照往年题目的思路通过直接检测是否处在VT环境下来检测题目攻击是否已经生效,https://bbs.kanxue.com/thread-286460.htm#msg_header_h2_7,但是最终还是认为这样通过CPUID检测的方法最为直接。
计算出正确的终止密码,输入到shadow_panel.exe中,使得其返回成功。(满分1.0分)
通过(2)中介绍的调试的方式可以进入到实际VMX执行的代码段中

一路跟进走到subleaf的分发逻辑

进入深入可以看到校验密码的逻辑

这里可以通过直接调试读取r8,r9的值得到flag,也可以通过逆向的方式,逆向的话key来自于[page+0x500]和[page+0x508]

通过模拟算法也可以获得最后的flag



编写keygen,使得在任意机器,任何一次运行shadow_panel.exe,都可以正确计算出终止密码。(满分2.5分)
由(4)可知,终止密码的密钥来自于[page+0x500]和[page+0x508],其中page是归属于VMX代码的页,一旦程序进入VT环境,就基本读取不到这个页,因此也无法获取密钥。那就要么转头去逆驱动,看看这一段到底是怎么写入的,调试了一段时间发现VMX代码是由sub_14000FC40处代码动态生成的,这也能解释为什么驱动本身没有出现任何VMX相关的指令。不过这一段虚拟化程序很深,大部分逻辑最终会跑到.tvm0段执行。逆向十分困难。
考虑多获取几个key看看是否有枚举的可能,于是尝试在多个虚拟机中调试获取密钥,获得了几个不同的密钥:
1 | 0x247C8B4C00008AEC, 0x00005714003D8070 |
观察发现只有两个字节不一样,推测存在枚举的可能性。于是最终的keygen,通过枚举两字节并交予CPUID指令判断正误,最终计算出正确的终止密码,具体代码见附件中的dynamic_keygen.py
运行效果如图:


