0%

Vmprotect逆向分析

源程序

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main()
{
auto v1 = 1;
auto v2 = 2;
auto vsum = v1 + v2;
printf("1 + 2 = %d", vsum);
getchar();
return 0;
}

动态分析

首先有几个寄存器需要关注:

寄存器 所存储数据
R12 Handler表(未赋值)
R13 Handler基址
RBP 栈顶指针
RSI 字节码缓冲区(偏移)
RDI 伪寄存器组
RBX 解密Seed

VMProtect首先需要将所需要的寄存器环境进行初始化,下面一次是:赋值handler表,字节码缓冲区操作,取操作码, 操作码解密

image-20220831160233365

接下来跳转到下方代码,使用操作码索引handler偏移,然后加上基址,跳转到handler处

image-20220831162154098

跟着跳转来到这里,红框中取操作数

image-20220831192745437

可以看到是一个POP操作,将栈中数据POP到一个伪寄存器(BYTE:[RSI-1]指示了要POP到哪个寄存器)

image-20220831163555847

紧跟着跳回这里,操作数索引伪寄存器组,赋值为rdx

image-20220831192943018

到这里第一个handler就完成了,接下来的操作就是继续重复上面的取操作码,索引handler表,然后跳转到新的handler。

新的handler如下,取四字节操作数

image-20220831194547606

下面应该是对操作数进行解码

image-20220831194635009

然后push rax

image-20220831194748341

image-20220831195037885

第一条jmp会跳到如下位置,ja操作其实是栈溢出错误检查(如果溢出了,那rbp会超过rdi+0xe0),结合上图中的红框代码,可以知道RDI指向的位寄存器组大小为0xE0

image-20220831195243264

image-20220831195350669

小总结

上面的一些handler大概都是一些vmp虚拟的push、pop、jmp的操作,也就是和前面笔记所说的一样,就是把基于寄存器的CPU代码,改造成基于堆栈的CPU的伪代码。

vmp3与vmp1和vmp2的最大区别,解析bytescode不在由VMDispatcher 分发下一个指令执行什么了(每个指令记为一个handle) 而是有vm_bytescode掌管,执行上一个指令才能得到下一个指令地址这样一来代码的膨胀可想而知。在VM_Instruct内部应该是没有CALL指令的。