为什么需要脱壳?
在 IDA 中打开驱动后,发现没有导入函数,所有调用都是间接调用,引用的是未初始化的内存——这是 VMProtect 的 IAT 保护机制。
驱动运行时才会解密并恢复 IAT,因此需要让驱动执行起来后将其内存转储出来。幸运的是,该驱动未被虚拟化,因此只需处理 IAT 加密。
脱壳步骤
1. 安装驱动服务(作为启动驱动)
sc.exe create ksophon_x64 binPath= "C:\Windows\system32\drivers\ksophon_x64.sys" start= boot type= kernel
2. WinDbg 内核调试设置
在调试机上设置 "Break on connection",重启目标虚拟机,WinDbg 在启动时中断。
3. 设置断点
sxe ld:ksophon_x64.sys— 在驱动加载时中断- 使用
!dh获取入口点地址,然后设置断点bp <module>+<entry_offset> - 在
DriverEntry返回地址处再设一个断点,此时 VMProtect 和驱动的所有初始化代码已执行完毕,IAT 已解密
4. 内存转储
使用以下命令将驱动镜像从内存中导出:
.writemem C:\temp\dumped.sys START START+SIZE
记录 ntoskrnl.exe 的基地址(lm k),因为 IAT 中的偏移与此相关。
修复 PE 文件
转储后的文件是内存映射布局,需要使用 PeBear 等工具将各节的 RAW_ADDR 设置为对应的 VIRTUAL_ADDR,使 IDA 能正确解析节区。
恢复导入地址表(IAT)
转储后的 IAT 中存在两类条目:
直接引用
直接指向 ntoskrnl 中的函数地址,无需额外处理。
跳板函数
指向驱动 .text 段中的小段代码,通过计算得到实际函数地址。
跳板函数示例:
- 加载一个立即数
- 减去某个值得到驱动内指针地址
- 加上该指针指向的值
- 最终结果指向
PsGetCurrentProcess
使用 Unicorn 模拟跳板函数
由于跳板函数是位置无关代码,可以编写 Unicorn Python 脚本自动化模拟所有跳板函数:
- 加载转储后的驱动
- 遍历
.rdata节,识别指向.text段的条目 - 模拟执行直到遇到
xchg rcx, [rsp+imm]指令(计算结果在rcx中) - 根据
ntoskrnl基地址计算偏移并输出
最终效果
- 使用 WinDbg 的
ln命令或 PDB 解析库,将计算出的地址解析为函数名 - IDA 中 IAT 恢复后,代码从混乱的间接调用变为可读的函数调用,可以正常进行静态分析
关键工具
| 工具 | 用途 |
|---|---|
| WinDbg | 内核调试、断点设置、内存转储 |
| PeBear | PE 文件修复(对齐节区) |
| Unicorn Engine | 模拟跳板函数执行以解析 IAT |
| IDA Pro | 静态分析 |
总结
- 对于未虚拟化的 VMProtect 保护驱动,脱壳只需在正确的时机(IAT 解密后)进行内存转储
- IAT 恢复的关键在于区分直接引用和跳板函数,并利用模拟执行自动解析跳板函数的目标地址
- 整个过程需要内核调试环境的搭建和基本的 PE 文件结构知识
