目录

VMP壳分析(1):查壳、定位OEP、解密IAT

Exeinfo PE

  1. 这个工具是目前查壳效果最好的(很多以前更好用的查壳工具,已经停更了,新壳识别不准、或识别不出来)

GitHub - ExeinfoASL/Exeinfo: exeinfo PE for Windows by A.S.L

识别编译器入口点

  1. 32位:使用VS2019编译一个控制台程序,其入口点如下,call xxx jmp xxx组合

/vmplocateoep/Image/1-32%E4%BD%8D%E7%BC%96%E8%AF%91%E5%99%A8%E5%85%A5%E5%8F%A3%E7%82%B9.png

  1. 64位:使用VS2019编译一个控制台程序,其入口点如下,其实也是call xxx jmp xxx组合,只是多了抬栈操作

/vmplocateoep/Image/2-64%E4%BD%8D%E7%BC%96%E8%AF%91%E5%99%A8%E5%85%A5%E5%8F%A3%E7%82%B9.png

识别VMP加壳后的OEP(3.5.x ~ 3.9.x)

  1. 我们知道VMP是基于栈的虚拟机,那么在进入、退出VM时必定会操作栈,那么我们这里分析的入手点就是对栈下访问断点
  2. 这种方法支持32、64位的3.5.x ~ 3.9.x等已知版本的VMP,这里使用3.9.4进行测试,加壳参数如下(未对OEP添加额外保护):

/vmplocateoep/Image/3-VMP%E5%8A%A0%E5%A3%B3%E5%8F%82%E6%95%B0.png

  1. 拖入一个加壳后的程序,在EP处断下

/vmplocateoep/Image/4-EP%E5%A4%84%E6%96%AD%E4%B8%8B.png

  1. 在RSP - 8处下访问断点(32位同理ESP - 4)

/vmplocateoep/Image/5-RSP%E5%87%8F8%E4%B8%8B%E8%AE%BF%E9%97%AE%E6%96%AD%E7%82%B9.png

  1. 之后一路F9放行,直到看到call xxx jmp xxx组合即可,如图所示

/vmplocateoep/Image/6-OEP%E7%89%B9%E5%BE%81call%20jmp%E7%BB%84%E5%90%88.png

VMP代码变异

  1. 要明确一点,OEP正常样子如下:

/vmplocateoep/Image/6-OEP%E7%89%B9%E5%BE%81call%20jmp%E7%BB%84%E5%90%88.png

  1. 我们选择OEP函数,添加变异保护

/vmplocateoep/Image/7-OEP%E5%87%BD%E6%95%B0%E6%B7%BB%E5%8A%A0%E5%8F%98%E5%BC%82%E4%BF%9D%E6%8A%A4.png

  1. 此时再次使用堆栈法定位OEP,可以发现多多少少还是有原函数样子的,能依稀分辨出call xxx jmp xxx这个经典组合

/vmplocateoep/Image/8-%E5%8F%98%E5%BC%82%E5%90%8E%E5%A0%86%E6%A0%88%E6%B3%95%E5%AE%9A%E4%BD%8DOEP.png

VMP代码虚拟化

  1. 我们还是选中OEP函数,添加虚拟保护

/vmplocateoep/Image/9-OEP%E5%87%BD%E6%95%B0%E6%B7%BB%E5%8A%A0%E8%99%9A%E6%8B%9F%E4%BF%9D%E6%8A%A4.png

  1. 此时沿用堆栈法,已经找不到常规OEP函数特征了

VMP导入表保护

  1. 笔者这里测试了3.5.1、3.9.4版本的导入表保护
  2. 正常的PE程序调用导入表函数,是FF 15 xxxx,即call [IatSlot]这种间接调用函数

/vmplocateoep/Image/10-%E6%AD%A3%E5%B8%B8FF15%E8%B0%83%E7%94%A8%E5%AF%BC%E5%85%A5%E5%87%BD%E6%95%B0.png

  1. VMP加壳后,调用导入表方式直接修改为
// 原始调用
FF15 xxxx		---> call [IatSlot]

// VMP导入表加密
E8 xxxx			---> call VmpStubFunc

/vmplocateoep/Image/11-VMP%E5%AF%BC%E5%85%A5%E8%A1%A8%E5%8A%A0%E5%AF%86E8%E8%B0%83%E7%94%A8.png

  1. 但是由于E8 xxx这种call方式只占5字节,还剩下1字节,在上图中,VMP在call指令上一行放置一个push rdi的单字节指令

调试器模拟解密IAT

  1. 这种方式支持VMP 3.x版本,VMP的解密Stub,执行到ret时,此时栈顶就是原始函数的地址
  2. 我们来到call xxx jmp xxx入口点处,对着第一个call进去
  3. 内部会看到4个call,我们选择最后一个16E7的call

/vmplocateoep/Image/12-Stub%E5%86%85%E9%83%A84%E4%B8%AAcall.png

  1. 双击进去后,转到内存布局,会发现这个VmpStub函数是处于VMP区段的

/vmplocateoep/Image/13-VmpStub%E4%BD%8D%E4%BA%8EVMP%E5%8C%BA%E6%AE%B5.png

  1. 我们回到函数,直接将Rip设置到函数头部,如下所示

/vmplocateoep/Image/14-Rip%E8%AE%BE%E7%BD%AE%E5%88%B0%E5%87%BD%E6%95%B0%E5%A4%B4%E9%83%A8.png

  1. 这里利用x64Dbg的追踪功能,让其自动跑Trace,停止条件是遇到ret指令

/vmplocateoep/Image/15-x64DbgTrace%E5%8A%9F%E8%83%BD.png

/vmplocateoep/Image/16-Trace%E5%81%9C%E6%AD%A2%E6%9D%A1%E4%BB%B6%E8%AE%BE%E7%BD%AE.png

  1. 点击确定后,就会一直跑,直到满足断下条件,顺利得到解密函数

/vmplocateoep/Image/17-Trace%E5%BE%97%E5%88%B0%E8%A7%A3%E5%AF%86%E5%87%BD%E6%95%B0.png

VMProtect 脱壳 逆向工程