去年考察了Hypervisor,今年按理应该考察DMA技术,选手手中不一定有DMA怎么办呢?那就利用硬盘作为已有的DMA设备!
(1) 「影」核心系统「根」需要在一些开启了特殊特性的机器上才能部署成功,逆向找到成功部署条件!成功部署「影」核心系统,即成功运行shadow_panel.exe,控制台程序成功运行进入至输入终止密码的终端。(满分0.5分)
系统版本为:Windows 10 1909 x64 + Intel。
首次尝试直接打开shadow_panel.exe,其返回加载失败。

发现程序通过_beginthreadex创建线程于函数0x1449FA120,经分析此处为页面渲染循环,自此开始进入程序主逻辑。
从创建线程处继续向后分析:发现要让程序成功部署,共有以下条件:
(1) 需要UAC权限。
(2) 需要开启VT虚拟化。
(3) 需要开启HyperV虚拟化平台。
cpuid(1) 检查 ECX.bit31 + cpuid(0x40000000) 验证 vendor "Microsoft Hv"。
(4) 需要关闭IOMMU。
通过 NtQuerySystemInformation 枚举 ACPI 表, 查找 DMAR 或 IVRS 签名。
(5) 需要是INTEL或AMD型号的CPU(否则驱动蓝屏)。
驱动调用函数 KeBugCheckEx(0xDEADC0DE)。
满足上述条件后,可以成功启动程序,进入登陆页面。
(2) 「根」使用特殊方法,对操作系统底层进行了攻击,并借此将关键核心代码隐藏了起来,分析其完整实现流程。(满分2.5分)
R3在运行中于C:\Windows\Temp\WdiServiceHost\{%08lX-%04X-...}.vhd释放了一个虚拟磁盘文件映像,然后将其挂载为虚拟磁盘。
R3程序在运行中释放驱动到\\SystemRoot\\system32\\drivers\\<name>.sys,其中<name>为随机10字节,从驱动中可以提取出将被释放的完整驱动程序,其大小为33,433,648 字节,带有Guangzhou Kingteller Technology Co.,Ltd.过期签名。
对驱动进行分析,可以发现其没有任何导入函数,所有导入函数通过FNV1A算法手动导入,编写脚本自动化扫描,可以发现以下导入表。
驱动中所有函数被OLLVM混淆,部分函数被TVM虚拟化,共尝试分析了以下函数。
从R3中可以分析出通过GetProcAddress手动导入了一系列关于虚拟磁盘的函数。
通过分析,还原攻击链条如下:
1. 用户层通过*VirtualDisk系列函数,创建SCSI设备,防止用户环境无对应设备协议栈可用。
2. 驱动层通过0x4D014 = IOCTL_SCSI_PASS_THROUGH_DIRECT向用户层创建的SCSI设备发送请求,从而通过下图中的路径直接DMA读写物理内存。因此需要关闭IOMMU。
这里完整复现了相同的攻击手法,源码在Source/scsi_verify_hv中。
3. 驱动层从不受管理的物理内存中寻找hvix64.exe,然后通过类似开源项目Voyager的攻击手法,找到HyperV中存在的Vmexit路径,通过搜索特征码来劫持HyperV驱动的Vmexit,从而接管虚拟化。
4. 驱动将Shellcode解密,然后映射到不受管理的内存中(通过MmMapIoSpace验证),从而将口令的解密路径代码隐藏到Host内存映射中,通过接管指定的cpuid请求来允许用户层访问。
最终成功DUMP出关键核心代码:
为保持题解结构清晰明确,完整的分析流程将放到第六问答案中。
(3) 编写检测代码,检测(2)中「影」核心系统攻击操作系统底层的特殊方法。(满分2分)
分为两种层面的检测,首先说用户层:
R3一共写了6种检测方式,在shadow_panel.exe运行时打开即可。
所有源码在Souce/detect_r3中,这里没有写cpuid查询这种比较局限的检测。
内核层可以通过挂接到磁盘IRP设备栈,来检测这种恶意的SCSI请求,这里提出两种方法,一种是检测对虚拟设备的READ&WRITE请求,另一种是检测SCSI请求目标缓冲区内容通过MMIO映射后是否全为0xFF(受保护的内存映射后内容为全0xFF)。
也可以通过相同的方式直接校验HyperV的完整性,这里复现了SCSI_DMA的驱动层攻击手法,然后在物理内存中定位到HyperV的代码段,对其与磁盘校验完整性。
这里成功找到了对应的hook和代码洞穴。
把代码放到了Source/scsi_verify_hv。
通常会用内存布局,物理内存中映射时留下的副本两种方式来检测Voyager,但是在这里显然是不适用的,因为这里未受保护的副本没有被修改。
因此这里共以下三种方法:
其中1、3源码在Source/detect-r0中,2的源码在Source/scsi_verify_hv。
(4) 计算出正确的终止密码,输入到shadow_panel.exe中,使得其返回成功。(满分1.0分)
在测试的1909版本中,对应的正确终止密码为:
3CCBC52C0A3769F0B92BFA3ECBA472A2
输入后显示成功。
(5) 编写keygen,使得在任意机器,任何一次运行shadow_panel.exe,都可以正确计算出终止密码。(满分2.5分)
首先根据cpuid查询得到的CPU厂商,从hvix64.exe/hvax64.exe 中根据7套特征码搜索将被接管的vmexit所在页面。
然后根据偏移解析对应call的目标地址,从该地址向前对齐到0x1000后+0x500,+0x508的位置读取两段SEED值,与两个常量值进行异或,得到中间值。
r8 = seed0 ^ 0x5348414430574E54 "SHADOWNT"
r9 = seed1 ^ 0x4859504552564D58 "HYPERVMX"
对r8,r9做8轮混合运算,每轮4步,最终得到的r8,r9按照"{r8:016X}{r9:016X}"拼接为32位16进制字符串,即可得到密钥值。
keygen代码在Source/keygen中。
(6) 详细描述完整的解题过程和思路,提供所有编写的程序的源代码。做到清晰易懂,操作可以复现结果。编码工整风格优雅、注释详尽。相同实现方法下,提交时间靠前者得分更高,AI可用于辅助分析,其产出的内容(代码/文档等)需明确标注并提交完整提示词和聊天记录,赛事方针对此项的判断具有最终解释权。(满分1.5分)
环境分析思路
打开驱动就发现运行不起来,题目给了提示需要通过逆向出条件才能把程序跑起来,因此这里直接乖乖去逆向就可以了。
用户层分析思路
首先分析R3,R3可能为了反AI直接静态分析,把一个函数复制了很多份。因为之前编写了模拟执行工具,所以直接跑一下模拟,找一下真实的执行逻辑。
看到创建线程基本上就是主逻辑开始了,向后分析发现其实这里混淆反而没有多重,基本上都能直接看出来在做什么。
首先关注到GetProcAddress,调用了很多导入表不存在的函数,这样的函数基本上都是分析的重点,在这里发现创建了虚拟磁盘,还觉得可能是要隐藏驱动之类的。
后面就进入了条件判断,通过静态分析很容易就能总结出要加载程序需要的条件了。
然后激活系统,安装HyperV,关闭IOMMU,就可以进入程序了。
进入程序后首先关注到的是右侧展示的系统信息,然后这里提示了密码的长度。
最开始发现程序没有直接重启虚拟机,就几乎排除了HyperV劫持的猜测,因为常规的HyperV劫持一般都是需要重启一下。首先想到的是Enclave保护,但是查询文档发现Enclave版本要求比较高,题目这里要求的环境对不上。这里只能先往后做了,逆向R3的过程中很容易可以看到通信是通过cpuid进行的,因此可以确定是已经进入虚拟化了。
这里通过OpenArk的已卸载驱动枚举首先看到了程序加载了驱动。
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 18小时前
被Saileaxh编辑
,原因: