正文
(这里可能不确切,更大的可能是VMProtect的handler表变大了,由于测试指令过少,所以没有发生重复。如果真的每条指令都单独生成一系列handler,那加密指令多的时候文件会膨胀的可怕。但确定的一点是,原本复杂的循环结构消失了。)
0x02 版本对比
前面已经说过没有了循环结构,那么具体分析一下3.x的虚拟机是如何工作的。
首先简单说一下2.x的虚拟机,资料很多,不多说了,只提几个要点。
VMP虚拟机是栈式机,与x86体系结构的寄存器式虚拟机不同。x86的add eax, ebx指令,在VMP中会变成push ebx, push eax, add, pop eax的形式。这里的栈称为虚拟栈。
EBP指向虚拟机栈顶,充当x86中的ESP功能,即虚拟机的vm_esp。
虚拟机执行的字节码是编码在程序中的,ESI指向其地址,ESI也就是vm_eip
EDI指向寄存器数组,也就是vm_context。2.x版本的vm_context也在栈中,在EBP上方。(其实最特别早期的VMP版本中vm_context也曾位于全局数据区,过段时间会写篇关于VMP各版本变化过程的文章,敬请期待)
vm_context包含16个4字节寄存器(32位的情况下)
栈结构是这样:
栈底--->ebp------>edi(vm_context)---> esp
EBX 经常参与解密,保存密钥。
说了关于旧版本的废话,这里直接抛出新版本的结构对比:
相同点:
ESI依然是vm_eip,指向要解析的字节码。
EBP依然是vm_esp,指向虚拟栈顶
EBX 依然经常参与解密。
不同点:
栈结构是这样:
栈底--->ebp------>esp(vm_context)
ESP成为vm_context指针,EDI作为跳转寄存器,统一的dispatcher消失,每次跳转需要从ESI取出4字节进行解密加到EDI上,然后jmp edi(或push edi,ret 功能一样)
这里ESP成为vm_context指针的设计对我们来说是个好消息,因为vm_context不能随便移动,因此涉及到栈操作的垃圾指令大大减少了。
但jmp edi的跳转方式很让人头疼。
FKVMP和VMP分析插件等神器的基本逻辑是找到dispatcher 即jmp dword ptr ds:[eax*4+0x403103]类似的代码
然后枚举eax的值0~0xFF即可得到所有handler的入口,根据指令特征识别handler。依次解密出bytecode。
3.x不再有统一的dispatch过程,使得之前的神器从架构上无法应用到3.x版本。
0x3 具体分析
前面是为了有个统一的印象。
接下来具体分析。
我们直接看前面OD得到的Trace,实际的trace中有大量的垃圾指令,但正如前面所说,多不涉及栈操作,因此比较简单,以下的分析均是去了垃圾指令的,所以会发现地址不太连续。
虚拟机初始化部分
跳转到第一个handler
这里就开始是handler部分了,与2.x 一样,最开始的几条bytecode指令是把栈中的真实寄存器pop到虚拟寄存器数组vm_context中
第一条handler结束后,又是跳转
虽然功能完全一样,但VMP又实现出了完全独立了另一套代码
以此规避掉了循环
这里又是一个pop handler。