[GWCTF 2019]babyvm

程序是一个 vm 逆向的题。

程序存在两端 vmcode 分析每个 指令的功能 和作用

主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
__int64 v3[2]; // [rsp+10h] [rbp-10h] BYREF

v3[1] = __readfsqword(0x28u);
v3[0] = 0LL;
puts("Please input something:");
sub_CD1(v3);
sub_E0B(v3);
sub_F83(v3);
puts("And the flag is GWHT{true flag}");
exit(0);
}

看一下sub_F83函数,这应该是check函数了,qword_2022A8存放的应该是加密后的flag。前两个应该就是加密函数了。

之前看过文章用angr解vm题,在wp中也看到了这个

1
2
3
4
5
import angr
p=angr.Project("./Desktop/attachment",auto_load_libs=False)
sm=p.factory.simulation_manager(p.factory.entry_state())
sm.explore(find=0x401081)
print(sm.found[0].posix.dumps(0))

解出来的是假的,看下CD1函数

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
unsigned __int64 __fastcall sub_CD1(__int64 a1)
{
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u); // 反调试
*a1 = 0;
*(a1 + 4) = 18;
*(a1 + 8) = 0;
*(a1 + 12) = 0;
*(a1 + 16) = &unk_202060; // unk_202060是操作码的内容
*(a1 + 24) = 0xF1; // 0xF1操作码代表sub_B5F函数
*(a1 + 32) = sub_B5F;
*(a1 + 40) = 0xF2; // 0xF2操作码代表sub_A64函数
*(a1 + 48) = sub_A64;
*(a1 + 56) = 0xF5; // 0xF5操作码代表sub_AC5函数
*(a1 + 64) = sub_AC5;
*(a1 + 72) = 0xF4; // 0xF4操作码代表sub_956函数
*(a1 + 80) = sub_956;
*(a1 + 88) = 0xF7; // 0xF7操作码代表sub_A08函数
*(a1 + 96) = sub_A08;
*(a1 + 104) = 0xF8; // 0xF8操作码代表sub_8F0函数
*(a1 + 112) = sub_8F0;
*(a1 + 120) = 0xF6; // 0xF6操作码代表sub_99C函数
*(a1 + 128) = sub_99C;
qword_2022A8 = malloc(0x512uLL);
memset(qword_2022A8, 0, 0x512uLL);
return __readfsqword(0x28u) ^ v2;
}

每一个操作数都代表不同的段

0xE1,0xE2,0xE3,0xE4对应4个寄存器,实际的具体位置分别是a1,a1+4,a1+8,a1+12,而*(qword_2022A8 + *v2)对应内存单元。a1+16则是unk_202060操作码的内容

0xF1

0xF1进行的操作类似于mov操作符,

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
unsigned __int64 __fastcall sub_B5F(__int64 a1)
{
int *v2; // [rsp+28h] [rbp-18h]
unsigned __int64 v3; // [rsp+38h] [rbp-8h]

v3 = __readfsqword(0x28u);
v2 = (*(a1 + 16) + 2LL);
switch ( *(*(a1 + 16) + 1LL) )
{
case 0xE1:
*a1 = *(qword_2022A8 + *v2);
break;
case 0xE2:
*(a1 + 4) = *(qword_2022A8 + *v2);
break;
case 0xE3:
*(a1 + 8) = *(qword_2022A8 + *v2);
break;
case 0xE4:
*(qword_2022A8 + *v2) = *a1;
break;
case 0xE5:
*(a1 + 12) = *(qword_2022A8 + *v2);
break;
case 0xE7:
*(qword_2022A8 + *v2) = *(a1 + 4);
break;
default:
break;
}
*(a1 + 16) += 6LL;
return __readfsqword(0x28u) ^ v3;
}

0xF2

异或xor,两个操作数间隔4。

1
2
3
4
5
6
7
8
9
unsigned __int64 __fastcall sub_A64(__int64 a1)
{
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u);
*a1 ^= *(a1 + 4);
++*(a1 + 16);
return __readfsqword(0x28u) ^ v2;
}

0xF5

是个读取输入字符并判断长度的操作,存入qword_2022A8。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
unsigned __int64 __fastcall sub_AC5(__int64 a1)
{
const char *buf; // [rsp+10h] [rbp-10h]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
buf = qword_2022A8;
read(0, qword_2022A8, 0x20uLL);
dword_2022A4 = strlen(buf);
if ( dword_2022A4 != 21 )
{
puts("WRONG!");
exit(0);
}
++*(a1 + 16);
return __readfsqword(0x28u) ^ v3;
}

0xF4

空指令nop

1
2
3
4
5
6
7
8
unsigned __int64 __fastcall sub_956(__int64 a1)
{
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u);
++*(a1 + 16);
return __readfsqword(0x28u) ^ v2;
}

0xF7

相乘操作mul,两个操作数间隔12。

1
2
3
4
5
6
7
8
9
unsigned __int64 __fastcall sub_A08(__int64 a1)
{
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u);
*a1 *= *(a1 + 12);
++*(a1 + 16);
return __readfsqword(0x28u) ^ v2;
}

0xF8

交换swap

1
2
3
4
5
6
7
8
9
10
11
12
unsigned __int64 __fastcall sub_8F0(int *a1)
{
int v2; // [rsp+14h] [rbp-Ch]
unsigned __int64 v3; // [rsp+18h] [rbp-8h]

v3 = __readfsqword(0x28u);
v2 = *a1;
*a1 = a1[1];
a1[1] = v2;
++*(a1 + 2);
return __readfsqword(0x28u) ^ v3;
}

0xF6

线性运算

1
2
3
4
5
6
7
8
9
unsigned __int64 __fastcall sub_99C(__int64 a1)
{
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u);
*a1 = *(a1 + 8) + 2 * *(a1 + 4) + 3 * *a1;
++*(a1 + 16);
return __readfsqword(0x28u) ^ v2;
}

反汇编

0xF1 mov
0xF2 xor
0xF4 nop
0xF5 input
0xF7 mul
0xF8 swap
0xF6 线性运算
0xE1 寄存器r1
0xE2 寄存器r2
0xE3 寄存器r3
0xE4 内存单元flag
0xE5 寄存器r4

操作码解析

操作码一共有两段

静态分析 加 动态调试发现程序走的 是 第一段 vmcode 但是解密发现

1 数组 解密出来不是正确 flag 换第二段解密

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
0xF5,                               #获取输入
0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, #mov r1,flag[0]
0xF2, #xor r1,r2
0xF1, 0xE4, 0x20, 0x00, 0x00, 0x00, #mov flag[32],r1
0xF1, 0xE1, 0x01, 0x00, 0x00, 0x00, #mov r1,flag[1]
0xF2, #xor r1,r2
0xF1, 0xE4, 0x21, 0x00, 0x00, 0x00, #mov flag[33],r1
0xF1, 0xE1, 0x02, 0x00, 0x00, 0x00, #mov r1,flag[2]
0xF2, #xor r1,r2
0xF1, 0xE4, 0x22, 0x00, 0x00, 0x00, #mov flag[34],r1
0xF1, 0xE1, 0x03, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x23, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x04, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x24, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x05, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x25, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x06, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x26, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x07, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x27, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x08, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x28, 0x00, 0x00,0x00,
0xF1, 0xE1, 0x09, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x29, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x0A, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x2A, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x0B, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x2B, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x0C, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x2C, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x0D, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x2D, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x0E, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x2E, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x0F, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x2F, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x10, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x30, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x11, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x31, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x12, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x32, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x13, 0x00, 0x00, 0x00,
0xF2,
0xF1, 0xE4, 0x33, 0x00, 0x00, 0x00,
0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

#主要是下面这部分字节码,stack即flag
0xf5,#read(0,buf,0x20)
0xf1,0xe1,0x0,0x0,0x0,0x0,#r1 = flag[0]
0xf1,0xe2,0x1,0x0,0x0,0x0,#r2 = flag[1]
0xf2,#r1 = r1^r2
0xf1,0xe4,0x0,0x0,0x0,0x0,#stack[0] = r1

0xf1,0xe1,0x1,0x0,0x0,0x0,#r1 = flag[1]
0xf1,0xe2,0x2,0x0,0x0,0x0,#r2 = flag[2]
0xf2,#r1 = r1^r2
0xf1,0xe4,0x1,0x0,0x0,0x0,#stack[1] = r1

0xf1,0xe1,0x2,0x0,0x0,0x0,#r1 = flag[2]
0xf1,0xe2,0x3,0x0,0x0,0x0,#r2 = flag[3]
0xf2,#r1 = r1^r2
0xf1,0xe4,0x2,0x0,0x0,0x0,#stack[2] = r1

0xf1,0xe1,0x3,0x0,0x0,0x0,#r1 = flag[3]
0xf1,0xe2,0x4,0x0,0x0,0x0,#r2 = flag[4]
0xf2,#r1 = r1^r2
0xf1,0xe4,0x3,0x0,0x0,0x0,#stack[3] = r1

0xf1,0xe1,0x4,0x0,0x0,0x0,#r1 = flag[4]
0xf1,0xe2,0x5,0x0,0x0,0x0,#r2 = flag[5]
0xf2,#r1 = r1^r2
0xf1,0xe4,0x4,0x0,0x0,0x0,#stack[4] = r1

0xf1,0xe1,0x5,0x0,0x0,0x0,#r1 = flag[5]
0xf1,0xe2,0x6,0x0,0x0,0x0,#r2 = flag[6]
0xf2,#r1 = r1^r2
0xf1,0xe4,0x5,0x0,0x0,0x0,#stack[5] = r1

0xf1,0xe1,0x6,0x0,0x0,0x0,#r1 = flag[6]
0xf1,0xe2,0x7,0x0,0x0,0x0,#r2 = flag[7]
0xf1,0xe3,0x8,0x0,0x0,0x0,#r3 = flag[8]
0xf1,0xe5,0xC,0x0,0x0,0x0,#r4 = flag[12]
0xf6, #r1 = (3*r1+2*r2+r3)
0xf7, #r1 = r1*r4
0xf1,0xe4,0x6,0x0,0x0,0x0,#stack[6] = r1

0xf1,0xe1,0x7,0x0,0x0,0x0,#r1 = flag[7]
0xf1,0xe2,0x8,0x0,0x0,0x0,#r2 = flag[8]
0xf1,0xe3,0x9,0x0,0x0,0x0,#r3 = flag[9]
0xf1,0xe5,0xC,0x0,0x0,0x0,#r4 = flag[12]
0xf6, #r1 = (3*r1+2*r2+r3)
0xf7, #r1 = r1*r4
0xf1,0xe4,0x7,0x0,0x0,0x0,#stack[7] = r1

0xf1,0xe1,0x8,0x0,0x0,0x0,#r1 = flag[8]
0xf1,0xe2,0x9,0x0,0x0,0x0,#r2 = flag[9]
0xf1,0xe3,0xA,0x0,0x0,0x0,#r3 = flag[10]
0xf1,0xe5,0xC,0x0,0x0,0x0,#r4 = flag[12]
0xf6, #r1 = (3*r1+2*r2+r3)
0xf7, #r1 = r1*r4
0xf1,0xe4,0x8,0x0,0x0,0x0,#stack[8] = r1

0xf1,0xe1,0xD,0x0,0x0,0x0,#r1 = flag[13]
0xf1,0xe2,0x13,0x0,0x0,0x0,#r2 = flag[19]
0xf8,#r1 = r2,r2 = r1
0xf1,0xe4,0xD,0x0,0x0,0x0,#stack[13] = r1
0xf1,0xe7,0x13,0x0,0x0,0x0,#stack[19] = r2

0xf1,0xe1,0xE,0x0,0x0,0x0,#r1 = flag[14]
0xf1,0xe2,0x12,0x0,0x0,0x0,#r2 = flag[18]
0xf8,#r1 = r2,r2 = r1
0xf1,0xe4,0xE,0x0,0x0,0x0,#stack[14] = r1
0xf1,0xe7,0x12,0x0,0x0,0x0,#stack[18] = r2

0xf1,0xe1,0xF,0x0,0x0,0x0,#r1 = flag[15]
0xf1,0xe2,0x11,0x0,0x0,0x0,#r2 = flag[17]
0xf8,#r1 = r2,r2 = r1
0xf1,0xe4,0xF,0x0,0x0,0x0,#stack[15] = r1
0xf1,0xe7,0x11,0x0,0x0,0x0,#stack[17] = r2
0xf4#ret

EXP

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
byte=[0x69, 0x45, 0x2A, 0x37, 0x09, 0x17, 0xC5, 0x0B, 0x5C, 0x72, 0x33, 0x76, 0x33, 0x21, 0x74, 0x31, 0x5F, 0x33, 0x73, 0x72,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0]

temp=byte[15]
byte[15]=byte[17]
byte[17]=temp

temp=byte[14]
byte[14]=byte[18]
byte[18]=temp

temp=byte[13]
byte[13]=byte[19]
byte[19]=temp
cmp = [0x5C, 0x0b, 0xc5]
b=[0x33,0x72]
for j in range(3):
for i in range(200):
a = (3*i+2*b[j+1]+b[j])*0x33
a %=0x100
if a == cmp[j]:
b.append(i)
byte[8-j] = i


byte[5]^=(byte[6])
byte[4]^=(byte[5])
byte[3]^=(byte[4])
byte[2]^=(byte[3])
byte[1]^=(byte[2])
byte[0]^=(byte[1])

flag=''
for i in range(32):
flag+=chr(byte[i])
print(flag)

看师傅用z3求解的第8,7,6,但是后面的数不知道哪里来的,可能是太菜了,就换了个爆破直接硬试