一开始基本上看不懂,题解说是虚拟机指令逆向,那我们必须学一手,长长见识;

上来先给v4赋值:

qmencpy函数

从unk_403040地址开始作为int数组赋值给v4,由于intel是小端存储,所以每四个字节,从后往前查看字节:

还是回到View-A窗口,转换成dword大小的数组,取消dup()显示:

于是现在得到了v4这个指令数组,114是它的长度,然后查看操作的函数:

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
int __cdecl vm_operad(int *operation, int a2)
{
int result; // eax
char flag[100]; // [esp+13h] [ebp-E5h]
char v4[100]; // [esp+77h] [ebp-81h]
char v5; // [esp+DBh] [ebp-1Dh]
int v6; // [esp+DCh] [ebp-1Ch]
int v7; // [esp+E0h] [ebp-18h]
int v8; // [esp+E4h] [ebp-14h]
int v9; // [esp+E8h] [ebp-10h]
int i; // [esp+ECh] [ebp-Ch]

i = 0;
v9 = 0;
v8 = 0;
v7 = 0;
v6 = 0;
while ( 1 )
{
result = i;
if ( i >= a2 )
return result;
switch ( operation[i] )
{
case 1:
v4[v7] = v5;
++i;
++v7;
++v9;
break;
case 2:
v5 = operation[i + 1] + flag[v9];
i += 2;
break;
case 3:
v5 = flag[v9] - LOBYTE(operation[i + 1]);
i += 2;
break;
case 4:
v5 = operation[i + 1] ^ flag[v9];
i += 2;
break;
case 5:
v5 = operation[i + 1] * flag[v9];
i += 2;
break;
case 6:
++i;
break;
case 7:
if ( v4[v8] != operation[i + 1] )
{
printf("what a shame...");
exit(0);
}
++v8;
i += 2;
break;
case 8:
flag[v6] = v5;
++i;
++v6;
break;
case 10:
read(flag);
++i;
break;
case 11:
v5 = flag[v9] - 1;
++i;
break;
case 12:
v5 = flag[v9] + 1;
++i;
break;
default:
continue;
}
}
}

可以发现,第一个指令是10也只有一个10,这个指令的操作是读取输入也就是flag,然后再进行一系列操作,最后在指令为7的时候一个个地将v4与指令的下一个比特(不是int的下一个元素了,v4是字符数组,比较的是一个字节)一个个比较。

因为指令是确定的,那么这个函数操作的顺序也是确定的。又因为v4是和指令有对应关系所以v4其实可以确定,那么我们可以写一个一样的函数,但是将case 7部分的代码改成将v4赋成正确的数,并直接打印出来,就能得到v4应该变成的值。

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
139
140
141
142
143
144
145
#include "pch.h"
#include <stdio.h>
#include <string.h>

unsigned char vmcode[] =
{
0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x21, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x51, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x08, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00,
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00,
0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x41, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0C, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3F, 0x00,
0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
0x07, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x07, 0x00,
0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x33, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x00,
0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xA7, 0xFF, 0xFF, 0xFF,
0x07, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00,
0x00, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x84, 0xFF,
0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF,
0x07, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00,
0x00, 0x00, 0x7A, 0x00, 0x00, 0x00
};

int * a = (int *)vmcode;

int __cdecl vm_operad(int *opcode, int len_114)
{
char order[114] = {}; //顺序 即i
char flag[100]; // [esp+13h] [ebp-E5h]
char v4[100]; // [esp+77h] [ebp-81h]
char v5; // [esp+DBh] [ebp-1Dh]
int j; // [esp+DCh] [ebp-1Ch]
int m; // [esp+E0h] [ebp-18h]
int k; // [esp+E4h] [ebp-14h]
int n; // [esp+E8h] [ebp-10h]
int i; // [esp+ECh] [ebp-Ch]
int s = 0;

i = 0;
n = 0;
k = 0;
m = 0;
j = 0;
while (1)
{
if (i >= len_114) // 成功的地方
break;
switch (opcode[i])
{
case 1:
v4[m] = v5;
++i;
++m;
++n;
break;
case 2:
v5 = opcode[i + 1] + flag[n];
i += 2;
break;
case 3:
v5 = flag[n] - opcode[i + 1];
i += 2;
break;
case 4:
v5 = opcode[i + 1] ^ flag[n];
i += 2;
break;
case 5:
v5 = opcode[i + 1] * flag[n];
i += 2;
break;
case 6:
++i;
break;
case 7:
v4[k] = opcode[i + 1]; // 打印关键数据V4
printf("%#X, ", v4[k]);
++k;
i += 2;
break;
case 8:
flag[j] = v5;
++i;
++j;
break;
case 10:
scanf("%s", flag); // 输入flag //长度是15
++i;
break;
case 11:
v5 = flag[n] - 1;
++i;
break;
case 12:
v5 = flag[n] + 1;
++i;
break;
}
order[s++] = i;
}
printf("执行顺序是: ");
for (int ss = 0; ss < strlen(order); ss++) {
printf("%d, ", order[ss]);
}

return 0;
}

int main()
{
vm_operad(a, 114);

return 0;
}

v4 = { 0X22, 0X3F, 0X34, 0X32, 0X72, 0X33, 0X18, 0XFFFFFFA7, 0X31, 0XFFFFFFF1, 0X28, 0XFFFFFF84, 0XFFFFFFC1, 0X1E, 0X7A };
order[100] = { 1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, 28, 29, 30, 31, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 46, 47, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, 75, 76, 78, 79, 81, 82, 83, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114 };

再往前看,其实每一步v4都是v5的值,而v5由flag和指令算出来的。现在已经得到v4的值,所以就相当于知道了v5的值,指令也知道,于是反着算就能算出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
int decode(int * opcode, int len_114)
{
int i;
//获取flag 的关键数据
unsigned char v4[] = { 0X22, 0X3F, 0X34, 0X32, 0X72, 0X33, 0X18, 0XFFFFFFA7, 0X31, 0XFFFFFFF1, 0X28, 0XFFFFFF84, 0XFFFFFFC1, 0X1E, 0X7A };
//执行opcode 的索引,即执行顺序
char order[100] = { 1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, 28, 29, 30, 31, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 46, 47, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, 75, 76, 78, 79, 81, 82, 83, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114 };
unsigned char flag[100] = {};
int m = 15;
int n = 15;
int j = 15;
int v5;
for (int k = strlen(order) - 1; k >= 0 ; k--)
{
i = order[k];
switch (opcode[i]) // 倒序执行
{
case 1:
--m;
--n;
v5 = v4[m];
break;
case 2:
flag[n] = v5 - char(opcode[i + 1]) ;
break;
case 3:
flag[n] = v5 + char(opcode[i + 1]);
break;
case 4:
flag[n] = (v5 ^ opcode[i + 1]) & 0XFF;
break;
case 5:
flag[n] = v5 / opcode[i + 1];
break;
case 6:
break;
case 8:
v5 = flag[--j];
break;
case 11:
flag[n] = v5 + 1;
break;
case 12:
flag[n] = v5 - 1;
break;
}
}

printf("%s", flag);
return 0;
}

可以得到输入的数:757515121f3d478

参考帖子:2020网鼎杯青龙组部分逆向题 - 『脱壳破解区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

2021-07-23
Contents

⬆︎TOP