0x00 前言
本文提到的vm是ctf里的vm,最近vm还是很热门的,最近的虎符,de1ctf,再到网鼎杯都有vm的身影,但是vm的知识在网上挺散的(我只找到了绿盟那篇比较系统),vm的wp也相对比较简单,所以身为小白的我就在这里归纳一下网上大佬们的文章,和我的思考和学习总结,希望可以给各位看官一些帮助。
参考文章:
《加密与解密》第四版 P592 ~P593
0x01 简单结构
《加密与解密》“中说ctf_vm不是VMware,,它类似于 P-CODE ,将一系列指令解释成bytecode 后放到一个解释引擎中执行,从而对软件进行保护。”简单来说,就是先设立一个一个不重复的标志(一个字节就是一个标志),然后每个标志通过分发器(dispatcher),寻找到对应的解释(handler),之后把这些标志通过不同的排列组合,并把组合后的标志存到一个固定的地方(这个大数组就是opcode),而组合后的逻辑与将保护程序逻辑相同,实现替换,从而进行保护。
一下是图解:
说明:在ctf_vm中 分发器(dispatcher) 与解释器 (handler)通常是用switch和case实现的,通常有如下结构
switch(opcode)
{
case 1: // handler1
......
break;
case 2: // handler2
......
break;
case 3: // handler3
......
break;
}
为了更好的理解,我们来看一道类似vm思想的题(伪虚拟机)南邮的一道ctf
本文着重看vm保护,其他本文不再赘述。进入vm_protect。
一般分为两个部分,在分发器之前(红框),会先对一些辅助的值进行赋值(这个下文具体讲解),第二部分(紫框)就是具体的分发,和每一个handler,
在这道伪虚拟机里
1)handler的含义非常好理解,所以也没有什么必要去理解红框里辅助的值,
2)本道题所保护的flag算法只是最简单的+ - * /,所以没必要通过opcode的顺序自己来还原(15000个也不可能自己还原)
小结:本题并没有逆向虚拟机最重要的东西(翻译handler含义,翻译所保护代码的含义),放在这是为了初步认识ctf_vm的结构。
放上师傅写的脚本(拿idapython跑一下):
adr1 = 0x6010C0
adr2 = 0x601060
flag =[]
for i in range(0,24*4,4):
print "%x"%Dword(adr2+i)
flag.append(Dword(adr2+i))
for i in range(14997,-1,-3):
a = Byte(adr1+i)
b = Byte(adr1+i+1)
c = Byte(adr1+i+2)
if(a == 1):
flag[b] -= c
flag[b] = flag[b]&0xff
if(a == 2):
flag[b] += c
flag[b] = flag[b] & 0xff
if(a == 3):
flag[b] ^= c
flag[b] = flag[b] & 0xff
if(a == 4):
flag[b] /= c
flag[b] = flag[b] & 0xff
if(a == 5):
flag[b] ^= flag[c]
flag[b] = flag[b] & 0xff
print(''.join(map(chr,flag)))
flag: nctf{Embr4ce_Vm_j0in_R3} 嘿嘿嘿,很简单吧,我们来进入下一个阶段。
0x02 网鼎_2020
具体的wp可以看这位师傅写的:网鼎杯2020 伪虚拟机逆向 wp
咱们直接看vm_protect的部分:
v10 = 0;
v9 = 0;
v8 = 0;
v7 = 0;
v6 = 0;
while ( 1 )
{
result = v10;
if ( v10 >= size )
return result;
switch ( opcode[v10] )
{
case 1:
v4[v7] = v5;
++v10;
++v7;
++v9;
break;
case 2:
v5 = opcode[v10 + 1] + flag[v9];
v10 += 2;
break;
case 3:
v5 = flag[v9] - LOBYTE(opcode[v10 + 1]);
v10 += 2;
break;
case 4:
v5 = opcode[v10 + 1] ^ flag[v9];
v10 += 2;
break;
case 5:
v5 = opcode[v10 + 1] * flag[v9];
v10 += 2;
break;
case 6:
++v10;
break;
case 7:
if ( v4[v8] != opcode[v10 + 1] )
{
printf("what a shame...");
exit(0);
}
++v8;
v10 += 2;
break;
case 8:
flag[v6] = v5;
++v10;
++v6;
break;