0ctf2018

babyvm

第一次做类似的题,记录一下解题思路:

既然是vm,必须要找到dispatcher,找到dispatcher之后,先看其初始化的过程,即dispatcher往上的内容
尽量理解。而后便是分发函数的部分,从case0-case1F逐次慢慢理解,可以先跳过case2。在长时间的理解
之后,我们发现初始化的两个函数得到的值在相与3,以及右移2位相与8之后得到两个变量,记为tmp1,tmp2
。通过tmp1,和tmp2我们可以索引到一个值,这个值和程序的处理十分重要,或者说程序的处理就是由这个
值确定的。联系堆栈式虚拟机的操作,我们可以得到一下猜想:

  1. v229是esp
  2. 每一次从while(1)开始的包含那两个函数的整套操作就是一次取栈顶值,并且v229–操作就是pop操作
  3. 0x13FD132C0处的函数就是push操作
  4. 0-0xffff的空间就是vm.bin的范围
  5. v225是pc
    根据我们的猜想,我们得到程序每次pop出栈顶的值,进行分析,根据分析的结果进行相关操作。
    从而我们得到case0-case1f的猜想:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
case 0:
exit
case 1:
pop eax;push 2;jmp (eax&0x3fffffff)+1
case 2:
进行函数调用,得到flag
case 5:
pop eax;jmp (eax&0x3fffffff)+1
case 6:
pop eax;pop ebx;jmp (ebx&0x3fffffff)+1
case 7:
push 0
case 8:
push 1
case 0xD:
push offset;offset = 4 || 2 || 1 byte
case 0xE:
push offset;offset = 4 || 2 || 1 byte no | 0x80000000
case 0xF:
v10 = eax;pop eax
case 0x10:
push [esp]
case 0x11:
push(~[esp])
case 0x12:
pop eax;pop ebx;push eax+ebx pc+=1
case 0x13:
pop eax;pop ebx;push eax-ebx pc+=1
case 0x14:
pop eax;pop ebx;push eax*ebx pc+=1
case 0x15:
pop eax;pop ebx;push eax/ebx pc+=1
case 0x16:
pop eax;pop ebx;push eax%ebx pc+=1
case 0x17:
pop eax;pop ebx;push 3 pc+=1
case 0x18:
pop eax;pop ebx;push eax&ebx pc+=1
case 0x19:
pop eax;pop ebx;push eax|ebx pc+=1
case 0x1A:
case 0x1B:
case 0x1C:
case 0x1D:
case 0x1E:
pop eax;pop ebx;push 3 pc+=1
case 0x1F:
pop eax;pop ebx;push 3 pc+=1
default:
next_instruction

根据CreateFile,ReadFile,puts的顺序写如下shellcode(右侧伪代码):

1
2
3
4
5
6
7
8
9
10
11
12
13
8D 50 00 00 00 //push 0x80000050
0E 03 //push 3
0E ff // push 0xff
8D 50 00 00 00 //push 0x80000050
0E 80 // push 0x80
8E 01 00 00 00 //push 0x00000001
8D 40 00 00 00 //push 0x80000040
07 // push 0
02 // CreateFile pop 0;pop 0x80000040;pop 0x00000001;pop 0x80
08 // push 1
02 // ReadFile pop 1;pop hFile;pop 0x80000050;pop 0xff
02 // puts pop 3;pop 0x80000050
00 // exit

得到flag
flag