What is VM
different from virtualbox…
这种虚拟机类似于自己用一套自己定义的操作码实现了完整的逻辑,当然用的指令不太多,不然会很困难且效率很低,显得没有必要。必要的定义有
- 虚拟机的初始化
- 一套自己定义的操作码
- 对应各个操作码的解释器
初次接触也许很生疏,我们从一套题目看起,这是2018NCTF WcyVM,网上wp很多,我在这里通过这道题来详细说明一下虚拟机在逆向方面的各种思路
Analysis
Some differences
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
printf("This is a %s\nplz input your flag:", aWcyvm1);
if ( sub_400DAB() )
puts("flag is your input");
else
puts("didixingwei");
return 0LL;
}
看到主函数逻辑极其简单,我们跟sub_400DAB()
,会发现是一个while
嵌套switch
的一套代码,先看循环以上本函数的初始化
v11 = __readfsqword(0x28u);
v1 = 0;
dest = malloc(0x200uLL);
v3 = malloc(0x1400uLL);
memset(&v4, 0, 0x30uLL);
memcpy(dest, &unk_6021C0, 0x190uLL);
v4 = malloc(0x38uLL);
v5 = v4 + 1;
v6 = v4 + 2;
v7 = v4 + 3;
v8 = v3 + 5120;
v9 = v3 + 5120;
v10 = dest;
LABEL_26:
定义了许多变量,我们只需要注意v8即可,这是一个堆栈指针,在以后有大用,我们看一下后边的case
指令,例如下方
case 8:
sub_40082B((&v4)[v10[1] - 1], v10[2], &v10);
goto LABEL_26;
//本函数
_QWORD *__fastcall sub_40082B(_DWORD *a1, int a2, _QWORD *a3)
{
_QWORD *result; // rax
*a1 = a2;
result = a3;
*result += 12LL;
return result;
}
我们看下这个函数,它做的便是把a2的值赋给了a1,然后把指针向后移,所以可以简单理解为mov a1,a2
,而堆栈指针的使用在下边,我们举例说明
case 9:
sub_40096D((&v4)[v10[1] - 1], &v8, &v10);
goto LABEL_26;
_QWORD *__fastcall sub_40096D(_DWORD *a1, _DWORD **a2, _QWORD *a3)
{
_QWORD *result; // rax
*a1 = **a2;
++*a2;
result = a3;
*result += 8LL;
return result;
}
可以看到函数在直接把a2的值给了a1,之后a2自增,之后与之前的函数类似,这显然就是一个典型的pop a1
操作,而且v8正是我们是我们假定的栈顶指针
这题就是这样需要一步一步逆出指令集即可进行解题,没有别的自动化方法,我们把我们要做的操作码用idc搞出来
auto i,start,end,num;
start = 0x6021c0;
end = 0x60234f;
Message("\n---------\n");
for(i=start;i<end;i=i+4)
{
Message("%d,",Dword(i));
}
然后硬着头皮,像转换一样把它填进去就行,例如根据得到的数据
8,1,0,8,3,70,14,21,10,1,9,2,11,10,1,10,2,9,1,17,1,13,1,3,15,8,8,1,0,8,3,71,14,70,10,1,26,2,6,29,1,4,20,2,1,25,1,2,27,1,1,29,1,110,19,1,99,21,1,116,19,1,102,28,2,1,9,1,17,1,13,1,3,15,34,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
8,1,0
就是mov (&v4)[v10[1] - 1],0
9,1
就是pop (&v4)[v10[1] - 1]
很难受的题,不过这种逆向确实很有趣
正向出题
下面自己完成一套虚拟机,通过正向来看一下 vm 层面需要什么