系列第1篇 对一个极弱的虚拟机 VMProtect 1.81 Demo 版进行了分析,初步展示了一下对虚拟机保护代码的分析方法。
Demo 版因为代码没有混淆处理,因此在 IDA 中可以分析的很清楚了,甚至还可以根据字节码(VM_DATA)一点点静态还原虚拟指令。
然而正式版本的 VMProtect 虚拟机是有比较严重的混淆的(实际是一种冗余指令的添加),直接使用 IDA 分析十分困难,许多基本块会截断,也没有第1篇中介绍的明显的解释循环图结构。动态调试也不方便,里面大量的CALL\JMP,跳来跳去。ESI 还有指令的立即数等还有加密,整体复杂度有很大的提升。
对于这种情况如何处理呢?本文以 VMProtect 2.13.8 为例,展示如何在混淆比较严重的情况下找到虚拟机关键结构、快速的分析 Handler,提取出虚拟指令。
本文主要介绍两个关键的部分:
这里使用的样本仍是第1篇中的代码加 VM 的。
加密级别为最大速度 ,目前我们只讨论虚拟机本身,其他诸如IAT保护、反调试等其他 VMP 保护选项都关闭,让我们专注于虚拟机的分析。
其实即使到了 2.13.8 版本, VMProcect 的整体结构仍是和 VMProtect 1.81 一致的。 因此虽然存在混淆,经验丰富的人还是可以迅速找到关键的指令分发点(Dispatcher)位置,从而找到跳转表,提取所有的 Handler。
如下指令(加*的部分)就是典型的 dispatcher 代码,根据 0x404cf8 跳转表,再找到 ESI 解密的方式,就可以提取所有 Handler。
这是前人总结出来的特征。如果是不了解 VMProtect 的人,分析起来就要困难一些。
如果我们对 VMProtect 并不十分清楚,直接面对 VMProtect 2.13.8 版本的样本该如何处理?
首先仍 IDA 打开样本,跳到 0x401000 处,看下前几条指令:
其实这些代码并没有什么有用的操作,先向栈中压入一些无用的数据,然后又通过 lea esp, [esp+44h]
把栈顶降回来,相当于把压入的数据弹出来,结果就是什么都没有做。同时使用了大量的 jmp 和 call 将代码切割成若干小块,使 IDA 无法正常分析。
我们还是很喜欢第1篇中的 IDA 生成的控制流图的。通过图中明显的解释循环结构,可以很快的定位哪里是 Dispatcher,哪里是 Handler。但现在 jmp 和 call 干扰了 IDA 的分析,IDA 没法生成完整的 CFG 图。
为了恢复这个图,我们还是使用 Trace 分析的方法。Trace 是 x86 指令执行的序列,Trace 中所有 jmp 和 call 会天然的和跳转目标指令连接起来。Trace 分析其实是很强大的功能。单步执行时我们的注意力可能会被寄存器、内存的值所分散,而忽略了程序整体的执行情况。而通过对 Trace 的整体分析,则可以让我们跳出局部,从整体去观察。
虚拟机解释执行的过程是一个循环:首先取指令,解码,然后跳转到 Handler 代码,执行完成后再跳转回来。在 Trace 中我们如何捕捉这个关键的循环?我想到的方法是,由 Trace 构造一个像之前 IDA 显示的 CFG 图类似的图。 通过找图中的中心结点,来确定 dispatcher 的位置。
由 Trace 构造的图,是反映了程序执行过程的基本块图,我们可以称之为执行流图 。构造图的方法说起来比较费力,直接看图。
假设ABCD是执行的指令序列,按照每条指令在Trace中的先后顺序,就可以构造一个图。相邻结点合并一下,就可以得到最终的执行流图。最终 AB 形成一个块,执行到 CDE 块,再跳转回来,再执行FG块,再跳转回来,再执行HI块,再跳转回来。整个执行的过程就很清楚了。通过写脚本分析 OD 的 Trace 文件,就可以构造出这个图,编程有基础的同学实现一下并不困难。
我做过一个工具,具备生成这种图的功能。不过为了 Trace 的效率,这个工具没有使用 OD Trace,而是利用 Intel 的插装工具 Pin 记录指令序列。(这个工具还在写,目前比较渣,不多介绍了。如果写好了会单独写文章介绍。想尝鲜可以自己下载下来编译源码,传送门在 https://github.com/lmy375/pinvmp )
下面是用这个工具分析 VMP 样本生成的执行流图。
(图太大无法显示清楚,看得清基本的结构就好,完整的SVG格式图见附件)
这回已经非常直观了。图中间的结点跳转到其他结点中再跳转回来,是典型的 dispatcher 结构,并列成排的每个代码块,就是 Handler 代码块。
dispatcher 结点的代码如下:
除了注释外的代码,大多是混淆作用,不用关心。
接下来看 Handler 代码。当然我们也可以一个一个人工分析 Handler 代码。因为 VMProtect 的混淆没有代码变形,只是添加了很多的栈指令。
因为原本的代码没变,人工分析也不困难,对于了解 VMP 的人,即使人工分析,分析每个Handler也不会太困难。
然而如果不了解 VMP Handler 的特征,看这种混淆代码是很痛苦的。
这里介绍一种利用符号执行分析 Handler 的方法。(参考:http://www.miasm.re/blog/2016/09/03/zeusvm_analysis.html)
首先简单说明一下什么是符号执行。符号执行是将所有寄存器和内存当作符号变量,然后模拟执行所有语句。执行完毕后,每个寄存器和写入的内存都会变成符号表达式。
比如如下代码
记eax和ebx的值是eax_init
和ebx_init
。符号执行会将每条指令翻译成对应的语义表达式。执行过程如注释说明。
最终的结果
说明执行了上述代码, eax 增加了初始值的 1;ebx 变成 ebx 初始值加 ecx;ecx 还是初始值不变。值得注意的是 ebx 加 eax 又减 eax 这个过程没有显示在最终的结果里,符号执行引擎把结果化简了。VMP 添加的指令就是这类不影响最终结果的指令,因此可以被符号执行引擎化简掉 。
下面证明我们的想法。
使用 Miasm 这个符号执行引擎去分析混淆的代码。(Miasm:https://github.com/cea-sec/miasm)
以如下的 Handler 代码块为例:
我们把所有跳转指令去掉,当作一个连续的代码块。然后把每条指令的二进制字节提取出来,拼接在一起,十进制表示如下:
之所以转化成二进制是为了方便使用 Miasm 进行符号执行(代码参考:https://github.com/lmy375/pinvmp/blob/master/py/symexec.py )
符号执行后,可以输出如下结果:
上面的表达式是Miasm使用的表达式 其中@
表示内存访问。上面的表达式的含意就是,执行上面的Handler 后。
因此可以很容易的确定这条指令是 vPushImm1。
这样分析起来就容易了非常多。(后面有时间会写一篇利用符号执行处理 Code Virtualizer 混淆的例子,效果也十分明显!)
很方便就可以分析出每条 Handler。
分析完每条 handler 后,就可以从 Trace 中取出 Handler 的调用序列。
方法和第1篇一样,首先用 OD2 跑 Trace,再写脚本还原伪代码。
同样的对于需要使用寄存器或者立即数的指令,需要额外处理一下。
比如前面的 vPushImm1
Handler,只要查找 Trace 中0x405be0 mov word ptr [ebp], ax
中 ax 的值,就可以知道具体的立即数压入的值是多少了。
而对于 push 和 pop 指令 我们这次除了从Trace中提取出寄存器下标,还提取出读写的寄存器值,方便我们分析伪代码。
VMProtect 2.13.8 相对之前的 Demo 版本,不但虚拟机内部混淆有明显增强,关键是伪代码级别变得更加难复杂了。(这其实才是 VMP 强度的关键)
其实伪代码也是可以利用前面介绍的符号执行方法进行分析,不过需要自行处理伪指令的语义(后面有空的话会专门写东西讲利用符号执行分析伪指令)
这里简单人肉分析一下:
本篇介绍了利用 Trace 和符号执行分析 VMProtect 2.13.8 的方法。其实只是大概提下思路,想要完全自动化分析还有很长的路要走。
Trace 分析这种方法对混淆代码其实还是不错的,动态运行信息有了,比静态看 IDA 要好很多。但这种方法也是有局限的,首先Trace 文件可能会非常大,处理起来很麻烦。另一方面 Trace 只记录跑过的指令,没有跑过的代码 Trace 是分析不到的。就像文中所述,这个VMP样本使用 Trace 大概只能提取到 20 多个 Handler,因为样本实际只用到了这么多。
符号执行是个新东西。对于学术界可能不新了,但是开始出现好用的实用工具如 angr, miasm, triton 等也是近年的事。这种新方法如果应用到虚拟机保护上也许会有奇效。
(文章涉及的样本、IDB文件、Trace文件、部分脚本见附件,密码123456)
Moon 2018/04/08
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2018-4-9 18:13
被穆恩编辑
,原因:
上传的附件: