从[羊城杯 2021]Babyvm探索vm题通用解法

Posted by Qmeimei10086 on December 15, 2025

前言

VM一类题型一直算re里较难的题型,但是大部分题都离不开一下几件事

  • 1.重构VM_ctx结构体
  • 2.重建控制流
  • 3.动态Trace运行时的汇编与寄存器

即使是强网杯的tradre也离不开这几件事,只是复杂化上面的流程

  • 创新VM_ctx结构体
    将VM_ctx结构体链表化表示,在结构体中加入Lchild,Rchild两条分支路线
  • 复杂控制流恢复过程
    通过每个结构体中handler对子进程的的eflag寄存器进行处理,决定跳转方向
  • 创新运行方式
    将子进程作为父虚拟机的代码库,将每一小段代码用int3中断隔开,触发时收回运行权,由父进程通过分析eflag寄存器与决定下一个跳转的节点
    引入plt表,可调用外部系统函数
    本质上,这其实不太算一个“正统”的VM,他并未对主要的汇编进行转译为自写的opcode,而是仅仅在每个节点结束的是时候分析eflag寄存器,决定跳转方向,这实际上更类似于ollvm,将基本块(relevant block)打散进行扁平化(flated),由一个中央调度器决定跳转与分发,类似于ollvm的主分发器(Main dispatcher),运行完基本块汇编代码进入int3父进程收回控制权,类似与预分发器(PreDispatcher)
  • 复杂加密
    往里面塞入了一个aes算法,初始化sbox的时候需要trace上万行,而且有复杂的跳转,恢复控制流难度较大

在重建控制流的过程中,如果加入判断,循环(本质上是判断+跳转),会极大增加恢复难度,有时需要使用广度搜索的算法,避免进入死循环
同时,循环与判断也会增加分析trace的难度,判断会导致trace的记录损失部分无法执行到的路线,循环则会大大增加trace的代码量
所以,vm题一般是两者结合分析

正题

简单分析

拖进ida,前面没什么好说的,用tea算法解密一串数据,替换loc_80487A8函数内容,典型的smc
我们直接来看最关键的VM函数

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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
unsigned int __cdecl sub_80487A9(int a1, _DWORD *a2)
{
  _BYTE *v3; // [esp+18h] [ebp-20h]
  unsigned int v4; // [esp+2Ch] [ebp-Ch]

  v4 = __readgsdword(0x14u);
  while ( 1 )
  {
    if ( *a2[8] == 113 )
    {
      a2[6] -= 4;
      *a2[6] = *(a2[8] + 1);
      a2[8] += 5;
    }
    if ( *a2[8] == 65 )
    {
      a2[1] += a2[2];
      ++a2[8];
    }
    if ( *a2[8] == 66 )
    {
      a2[1] -= a2[4];
      ++a2[8];
    }
    if ( *a2[8] == 67 )
    {
      a2[1] *= a2[3];
      ++a2[8];
    }
    if ( *a2[8] == 55 )
    {
      a2[1] = a2[5];
      ++a2[8];
    }
    if ( *a2[8] == 56 )
    {
      a2[1] ^= a2[4];
      ++a2[8];
    }
    if ( *a2[8] == 57 )
    {
      a2[1] ^= a2[5];
      ++a2[8];
    }
    if ( *a2[8] == 53 )
    {
      a2[5] = a2[1];
      ++a2[8];
    }
    if ( *a2[8] == 0xF7 )
    {
      a2[9] += a2[1];
      ++a2[8];
    }
    if ( *a2[8] == 68 )
    {
      a2[1] /= a2[5];
      ++a2[8];
    }
    if ( *a2[8] == 0x80 )
    {
      a2[sub_804875F(a2, 1)] = *(a2[8] + 2);
      a2[8] += 6;
    }
    if ( *a2[8] == 119 )
    {
      a2[1] ^= a2[9];
      ++a2[8];
    }
    if ( *a2[8] == 83 )
    {
      putchar(*a2[3]);
      a2[8] += 2;
    }
    if ( *a2[8] == 34 )
    {
      a2[1] >>= a2[2];
      ++a2[8];
    }
    if ( *a2[8] == 35 )
    {
      a2[1] <<= a2[2];
      ++a2[8];
    }
    if ( *a2[8] == 0x99 )
      break;
    if ( *a2[8] == 118 )
    {
      a2[3] = *a2[6];
      *a2[6] = 0;
      a2[6] += 4;
      a2[8] += 5;
    }
    if ( *a2[8] == 84 )
    {
      v3 = a2[3];
      *v3 = getchar();
      a2[8] += 2;
    }
    if ( *a2[8] == 48 )
    {
      a2[1] |= a2[2];
      ++a2[8];
    }
    if ( *a2[8] == 49 )
    {
      a2[1] &= a2[2];
      ++a2[8];
    }
    if ( *a2[8] == 50 )
    {
      a2[3] = *(a2[8] + 1);
      a2[8] += 2;
    }
    if ( *a2[8] == 9 )
    {
      a2[1] = 1877735783;
      ++a2[8];
    }
    if ( *a2[8] == 16 )
    {
      a2[9] = a2[1];
      ++a2[8];
    }
    if ( *a2[8] == 51 )
    {
      a2[4] = a2[1];
      ++a2[8];
    }
    if ( *a2[8] == 52 )
    {
      a2[2] = *(a2[8] + 1);
      a2[8] += 2;
    }
    if ( *a2[8] == 0xFE )
    {
      a2[1] = a2[9];
      ++a2[8];
    }
    if ( *a2[8] == 17 )
    {
      printf("%x\n", a2[1]);
      ++a2[8];
    }
    if ( *a2[8] == 0xA0 )
    {
      if ( a2[1] != 1877735783 )
        exit(0);
      ++a2[8];
    }
    if ( *a2[8] == 0xA1 )
    {
      read(0, s, 0x2Cu);
      if ( strlen(s) != 44 )
        exit(0);
      ++a2[8];
    }
    if ( *a2[8] == 0xB1 )
    {
      a2[9] = dword_804B080[0];
      ++a2[8];
    }
    if ( *a2[8] == 0xB2 )
    {
      a2[9] = dword_804B084;
      ++a2[8];
    }
    if ( *a2[8] == 0xA4 )
    {
      dword_804B080[*(a2[8] + 1)] = a2[1];
      a2[8] += 4;
    }
    if ( *a2[8] == 0xB3 )
    {
      a2[9] = dword_804B088;
      ++a2[8];
    }
    if ( *a2[8] == 0xB4 )
    {
      a2[9] = dword_804B08C;
      ++a2[8];
    }
    if ( *a2[8] == 0xC1 )
    {
      a2[1] = s[*(a2[8] + 1)];
      a2[8] += 2;
    }
    if ( *a2[8] == 0xC7 )
    {
      if ( dword_804B060 != a2[1] )
        exit(0);
      ++a2[8];
    }
    if ( *a2[8] == 0xC8 )
    {
      if ( dword_804B064 != a2[1] )
        exit(0);
      ++a2[8];
    }
    if ( *a2[8] == 0xC2 )
    {
      if ( *(a2[8] + 1) != a2[1] )
        exit(0);
      a2[8] += 5;
    }
  }
  return __readgsdword(0x14u) ^ v4;
}

根据上面流程,我们要做的第一步便是重建VM_CTX
很明显a2便是我们的ctx,ida将a2识别为_DWORD* 并且里面的每一个成员都能不经过偏移即可直接通过数组直接取到,我们不难联想到他的每一个成员长度都是dword,也就是32位下的int
首先a2[8]很明显是我们的ip寄存器,a2[6]有个操作时a2[6] -= 4;,不难让我们联想到时sub esp, 4操作,明显时函数开始的开辟栈空间的操作所以a2[6]是sp寄存器
a2[1] a2[8] a2[2] a2[9] a2[4] a2[3] a2[5]主要在一些运算过程中用到,我们姑且将其定位为通用寄存器,最后的结构体是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct VM_CONTEXT
{
    int VM_REG1;
    int VM_REG2;
    int VM_REG3;
    int VM_REG4;
    int VM_REG5;
    int VM_REG6;
    int VM_SP;
    int VM_BP;
    unsigned char* VM_IP;
    int VM_REG7;
    int VM_STACK_ADDR[4];
    int VM_NAN;
}VM_CONTEXT;

(ps:这一步比较吃经验,其实丢给ai最好,连强网杯那题ai都能恢复结构体,真是太强了)
将其导入ida就好看了许多,我们定位VM_IP的地址,提取所对应的数据,这就是所有的opcode,我们将其dump下来

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
unsigned char opcode[550] = {
    0xA1, 0xC1, 0x00, 0xB1, 0x77, 0xC2, 0x4A, 0x01, 0x00, 0x00, 0xC1, 0x01, 0xB2, 0x77, 0xC2,
    0x19, 0x01, 0x00, 0x00, 0xC1, 0x02, 0xB4, 0x77, 0xC2, 0xDD, 0x01, 0x00, 0x00, 0xC1, 0x03, 0xB3,
    0x77, 0xC2, 0x0F, 0x01, 0x00, 0x00, 0xC1, 0x04, 0xB2, 0x77, 0xC2, 0x1B, 0x01, 0x00, 0x00, 0xC1,
    0x05, 0xB4, 0x77, 0xC2, 0x89, 0x01, 0x00, 0x00, 0xC1, 0x06, 0xB1, 0x77, 0xC2, 0x19, 0x01, 0x00,
    0x00, 0xC1, 0x07, 0xB3, 0x77, 0xC2, 0x54, 0x01, 0x00, 0x00, 0xC1, 0x08, 0xB1, 0x77, 0xC2, 0x4F,
    0x01, 0x00, 0x00, 0xC1, 0x09, 0xB1, 0x77, 0xC2, 0x4E, 0x01, 0x00, 0x00, 0xC1, 0x0A, 0xB3, 0x77,
    0xC2, 0x55, 0x01, 0x00, 0x00, 0xC1, 0x0B, 0xB3, 0x77, 0xC2, 0x56, 0x01, 0x00, 0x00, 0xC1, 0x0C,
    0xB4, 0x77, 0xC2, 0x8E, 0x00, 0x00, 0x00, 0xC1, 0x0D, 0xB2, 0x77, 0xC2, 0x49, 0x00, 0x00, 0x00,
    0xC1, 0x0E, 0xB3, 0x77, 0xC2, 0x0E, 0x01, 0x00, 0x00, 0xC1, 0x0F, 0xB1, 0x77, 0xC2, 0x4B, 0x01,
    0x00, 0x00, 0xC1, 0x10, 0xB3, 0x77, 0xC2, 0x06, 0x01, 0x00, 0x00, 0xC1, 0x11, 0xB3, 0x77, 0xC2,
    0x54, 0x01, 0x00, 0x00, 0xC1, 0x12, 0xB2, 0x77, 0xC2, 0x1A, 0x00, 0x00, 0x00, 0xC1, 0x13, 0xB1,
    0x77, 0xC2, 0x42, 0x01, 0x00, 0x00, 0xC1, 0x14, 0xB3, 0x77, 0xC2, 0x53, 0x01, 0x00, 0x00, 0xC1,
    0x15, 0xB1, 0x77, 0xC2, 0x1F, 0x01, 0x00, 0x00, 0xC1, 0x16, 0xB3, 0x77, 0xC2, 0x52, 0x01, 0x00,
    0x00, 0xC1, 0x17, 0xB4, 0x77, 0xC2, 0xDB, 0x00, 0x00, 0x00, 0xC1, 0x18, 0xB1, 0x77, 0xC2, 0x19,
    0x01, 0x00, 0x00, 0xC1, 0x19, 0xB4, 0x77, 0xC2, 0xD9, 0x00, 0x00, 0x00, 0xC1, 0x1A, 0xB1, 0x77,
    0xC2, 0x19, 0x01, 0x00, 0x00, 0xC1, 0x1B, 0xB3, 0x77, 0xC2, 0x55, 0x01, 0x00, 0x00, 0xC1, 0x1C,
    0xB2, 0x77, 0xC2, 0x19, 0x00, 0x00, 0x00, 0xC1, 0x1D, 0xB3, 0x77, 0xC2, 0x00, 0x01, 0x00, 0x00,
    0xC1, 0x1E, 0xB1, 0x77, 0xC2, 0x4B, 0x01, 0x00, 0x00, 0xC1, 0x1F, 0xB2, 0x77, 0xC2, 0x1E, 0x00,
    0x00, 0x00, 0xC1, 0x20, 0x80, 0x02, 0x18, 0x00, 0x00, 0x00, 0x23, 0x10, 0xC1, 0x21, 0x80, 0x02,
    0x10, 0x00, 0x00, 0x00, 0x23, 0xF7, 0xC1, 0x22, 0x80, 0x02, 0x08, 0x00, 0x00, 0x00, 0x23, 0xF7,
    0xC1, 0x23, 0xF7, 0xFE, 0x80, 0x02, 0x05, 0x00, 0x00, 0x00, 0x22, 0x77, 0x10, 0x80, 0x02, 0x07,
    0x00, 0x00, 0x00, 0x23, 0x80, 0x02, 0x23, 0x77, 0xF1, 0x98, 0x31, 0x77, 0x10, 0x80, 0x02, 0x18,
    0x00, 0x00, 0x00, 0x23, 0x80, 0x02, 0x20, 0xB9, 0xE4, 0x35, 0x31, 0x77, 0x10, 0x80, 0x02, 0x12,
    0x00, 0x00, 0x00, 0x22, 0x77, 0xA0, 0xC1, 0x24, 0x80, 0x02, 0x18, 0x00, 0x00, 0x00, 0x23, 0x10,
    0xC1, 0x25, 0x80, 0x02, 0x10, 0x00, 0x00, 0x00, 0x23, 0xF7, 0xC1, 0x26, 0x80, 0x02, 0x08, 0x00,
    0x00, 0x00, 0x23, 0xF7, 0xC1, 0x27, 0xF7, 0xFE, 0x32, 0x20, 0x43, 0x33, 0x77, 0x80, 0x02, 0x11,
    0x00, 0x00, 0x00, 0x22, 0x35, 0x37, 0x38, 0x77, 0x80, 0x02, 0x0D, 0x00, 0x00, 0x00, 0x23, 0x77,
    0x38, 0x39, 0x10, 0x32, 0x20, 0x43, 0x33, 0x77, 0x80, 0x02, 0x11, 0x00, 0x00, 0x00, 0x22, 0x35,
    0x37, 0x38, 0x77, 0x80, 0x02, 0x0D, 0x00, 0x00, 0x00, 0x23, 0x77, 0x38, 0x39, 0xC7, 0xC1, 0x28,
    0x80, 0x02, 0x18, 0x00, 0x00, 0x00, 0x23, 0x10, 0xC1, 0x29, 0x80, 0x02, 0x10, 0x00, 0x00, 0x00,
    0x23, 0xF7, 0xC1, 0x2A, 0x80, 0x02, 0x08, 0x00, 0x00, 0x00, 0x23, 0xF7, 0xC1, 0x2B, 0xF7, 0xFE,
    0x32, 0x20, 0x43, 0x33, 0x77, 0x80, 0x02, 0x11, 0x00, 0x00, 0x00, 0x22, 0x35, 0x37, 0x38, 0x77,
    0x80, 0x02, 0x0D, 0x00, 0x00, 0x00, 0x23, 0x77, 0x38, 0x39, 0x10, 0x32, 0x20, 0x43, 0x33, 0x77,
    0x80, 0x02, 0x11, 0x00, 0x00, 0x00, 0x22, 0x35, 0x37, 0x38, 0x77, 0x80, 0x02, 0x0D, 0x00, 0x00,
    0x00, 0x23, 0x77, 0x38, 0x39, 0xC8, 0x99
};

重建控制流

我们在所有对opcode的操作里并没有发现直接修改ip值,都是VM_IP++之类的操作,这意味着可能没有特别难的跳转,程序为一条路向下执行,我们可以直接模拟执行恢复控制流

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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
#include <stdio.h>
unsigned char opcode[550] = {
    0xA1, 0xC1, 0x00, 0xB1, 0x77, 0xC2, 0x4A, 0x01, 0x00, 0x00, 0xC1, 0x01, 0xB2, 0x77, 0xC2,
    0x19, 0x01, 0x00, 0x00, 0xC1, 0x02, 0xB4, 0x77, 0xC2, 0xDD, 0x01, 0x00, 0x00, 0xC1, 0x03, 0xB3,
    0x77, 0xC2, 0x0F, 0x01, 0x00, 0x00, 0xC1, 0x04, 0xB2, 0x77, 0xC2, 0x1B, 0x01, 0x00, 0x00, 0xC1,
    0x05, 0xB4, 0x77, 0xC2, 0x89, 0x01, 0x00, 0x00, 0xC1, 0x06, 0xB1, 0x77, 0xC2, 0x19, 0x01, 0x00,
    0x00, 0xC1, 0x07, 0xB3, 0x77, 0xC2, 0x54, 0x01, 0x00, 0x00, 0xC1, 0x08, 0xB1, 0x77, 0xC2, 0x4F,
    0x01, 0x00, 0x00, 0xC1, 0x09, 0xB1, 0x77, 0xC2, 0x4E, 0x01, 0x00, 0x00, 0xC1, 0x0A, 0xB3, 0x77,
    0xC2, 0x55, 0x01, 0x00, 0x00, 0xC1, 0x0B, 0xB3, 0x77, 0xC2, 0x56, 0x01, 0x00, 0x00, 0xC1, 0x0C,
    0xB4, 0x77, 0xC2, 0x8E, 0x00, 0x00, 0x00, 0xC1, 0x0D, 0xB2, 0x77, 0xC2, 0x49, 0x00, 0x00, 0x00,
    0xC1, 0x0E, 0xB3, 0x77, 0xC2, 0x0E, 0x01, 0x00, 0x00, 0xC1, 0x0F, 0xB1, 0x77, 0xC2, 0x4B, 0x01,
    0x00, 0x00, 0xC1, 0x10, 0xB3, 0x77, 0xC2, 0x06, 0x01, 0x00, 0x00, 0xC1, 0x11, 0xB3, 0x77, 0xC2,
    0x54, 0x01, 0x00, 0x00, 0xC1, 0x12, 0xB2, 0x77, 0xC2, 0x1A, 0x00, 0x00, 0x00, 0xC1, 0x13, 0xB1,
    0x77, 0xC2, 0x42, 0x01, 0x00, 0x00, 0xC1, 0x14, 0xB3, 0x77, 0xC2, 0x53, 0x01, 0x00, 0x00, 0xC1,
    0x15, 0xB1, 0x77, 0xC2, 0x1F, 0x01, 0x00, 0x00, 0xC1, 0x16, 0xB3, 0x77, 0xC2, 0x52, 0x01, 0x00,
    0x00, 0xC1, 0x17, 0xB4, 0x77, 0xC2, 0xDB, 0x00, 0x00, 0x00, 0xC1, 0x18, 0xB1, 0x77, 0xC2, 0x19,
    0x01, 0x00, 0x00, 0xC1, 0x19, 0xB4, 0x77, 0xC2, 0xD9, 0x00, 0x00, 0x00, 0xC1, 0x1A, 0xB1, 0x77,
    0xC2, 0x19, 0x01, 0x00, 0x00, 0xC1, 0x1B, 0xB3, 0x77, 0xC2, 0x55, 0x01, 0x00, 0x00, 0xC1, 0x1C,
    0xB2, 0x77, 0xC2, 0x19, 0x00, 0x00, 0x00, 0xC1, 0x1D, 0xB3, 0x77, 0xC2, 0x00, 0x01, 0x00, 0x00,
    0xC1, 0x1E, 0xB1, 0x77, 0xC2, 0x4B, 0x01, 0x00, 0x00, 0xC1, 0x1F, 0xB2, 0x77, 0xC2, 0x1E, 0x00,
    0x00, 0x00, 0xC1, 0x20, 0x80, 0x02, 0x18, 0x00, 0x00, 0x00, 0x23, 0x10, 0xC1, 0x21, 0x80, 0x02,
    0x10, 0x00, 0x00, 0x00, 0x23, 0xF7, 0xC1, 0x22, 0x80, 0x02, 0x08, 0x00, 0x00, 0x00, 0x23, 0xF7,
    0xC1, 0x23, 0xF7, 0xFE, 0x80, 0x02, 0x05, 0x00, 0x00, 0x00, 0x22, 0x77, 0x10, 0x80, 0x02, 0x07,
    0x00, 0x00, 0x00, 0x23, 0x80, 0x02, 0x23, 0x77, 0xF1, 0x98, 0x31, 0x77, 0x10, 0x80, 0x02, 0x18,
    0x00, 0x00, 0x00, 0x23, 0x80, 0x02, 0x20, 0xB9, 0xE4, 0x35, 0x31, 0x77, 0x10, 0x80, 0x02, 0x12,
    0x00, 0x00, 0x00, 0x22, 0x77, 0xA0, 0xC1, 0x24, 0x80, 0x02, 0x18, 0x00, 0x00, 0x00, 0x23, 0x10,
    0xC1, 0x25, 0x80, 0x02, 0x10, 0x00, 0x00, 0x00, 0x23, 0xF7, 0xC1, 0x26, 0x80, 0x02, 0x08, 0x00,
    0x00, 0x00, 0x23, 0xF7, 0xC1, 0x27, 0xF7, 0xFE, 0x32, 0x20, 0x43, 0x33, 0x77, 0x80, 0x02, 0x11,
    0x00, 0x00, 0x00, 0x22, 0x35, 0x37, 0x38, 0x77, 0x80, 0x02, 0x0D, 0x00, 0x00, 0x00, 0x23, 0x77,
    0x38, 0x39, 0x10, 0x32, 0x20, 0x43, 0x33, 0x77, 0x80, 0x02, 0x11, 0x00, 0x00, 0x00, 0x22, 0x35,
    0x37, 0x38, 0x77, 0x80, 0x02, 0x0D, 0x00, 0x00, 0x00, 0x23, 0x77, 0x38, 0x39, 0xC7, 0xC1, 0x28,
    0x80, 0x02, 0x18, 0x00, 0x00, 0x00, 0x23, 0x10, 0xC1, 0x29, 0x80, 0x02, 0x10, 0x00, 0x00, 0x00,
    0x23, 0xF7, 0xC1, 0x2A, 0x80, 0x02, 0x08, 0x00, 0x00, 0x00, 0x23, 0xF7, 0xC1, 0x2B, 0xF7, 0xFE,
    0x32, 0x20, 0x43, 0x33, 0x77, 0x80, 0x02, 0x11, 0x00, 0x00, 0x00, 0x22, 0x35, 0x37, 0x38, 0x77,
    0x80, 0x02, 0x0D, 0x00, 0x00, 0x00, 0x23, 0x77, 0x38, 0x39, 0x10, 0x32, 0x20, 0x43, 0x33, 0x77,
    0x80, 0x02, 0x11, 0x00, 0x00, 0x00, 0x22, 0x35, 0x37, 0x38, 0x77, 0x80, 0x02, 0x0D, 0x00, 0x00,
    0x00, 0x23, 0x77, 0x38, 0x39, 0xC8, 0x99
};



typedef struct VM_CONTEXT
{
    int VM_REG1;
    int VM_REG2;
    int VM_REG3;
    int VM_REG4;
    int VM_REG5;
    int VM_REG6;
    int VM_SP;
    int VM_BP;
    unsigned char* VM_IP;
    int VM_REG7;
    int VM_STACK_ADDR[4];
    int VM_NAN;
}VM_CONTEXT;
unsigned int dword_804B080 = 0x7B;

int sub_804875F(VM_CONTEXT* VM_CONTEXT, unsigned int n2)
{
    int result; // eax

    result = 0;
    if (n2 <= 2)
        return VM_CONTEXT->VM_IP[n2];
    return result;
}

void vm_init(VM_CONTEXT* context, unsigned char* bytecode)
{
    context->VM_REG1 = 0;
    context->VM_REG2 = 0;
    context->VM_REG3 = 0;
    context->VM_REG4 = 0;
    context->VM_REG5 = 0;
    context->VM_REG6 = 0;
    context->VM_REG7 = 0;
    context->VM_SP = -1;
    context->VM_BP = 0;
    context->VM_IP = opcode;
    context->VM_NAN = 0;
}

void runvm(VM_CONTEXT* VM_CONTEXT) {


    while (1)
    {
        if (*VM_CONTEXT->VM_IP == 113)
        {
            // VM_CONTEXT->VM_SP -= 4;
            // *VM_CONTEXT->VM_SP = *(VM_CONTEXT->VM_IP + 1);
            // VM_CONTEXT->VM_IP += 5;
            printf("sub esp , 4\n");
            printf("push, [ip+1] \n");
            VM_CONTEXT->VM_IP += 5;
        }

        if (*VM_CONTEXT->VM_IP == 65)
        {
            // VM_CONTEXT->VM_REG2 += VM_CONTEXT->VM_REG3;
            printf("add reg1 , reg3\n");

            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 66)
        {
            // VM_CONTEXT->VM_REG2 -= VM_CONTEXT->VM_REG5;
            printf("sub reg2 , reg5\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 67)
        {
            // VM_CONTEXT->VM_REG2 *= VM_CONTEXT->VM_REG4;
            printf("mul reg2 , reg4\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 55)
        {
            //VM_CONTEXT->VM_REG2 = VM_CONTEXT->VM_REG6;
            printf("mov reg2 , reg6\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 56)
        {
            //VM_CONTEXT->VM_REG2 ^= VM_CONTEXT->VM_REG5;
            printf("xor reg2 , reg5\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 57)
        {
            // VM_CONTEXT->VM_REG2 ^= VM_CONTEXT->VM_REG6;
            printf("xor reg2 , reg6\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 53)
        {
            // VM_CONTEXT->VM_REG6 = VM_CONTEXT->VM_REG2;
            printf("mov reg6 , reg2\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0xF7)
        {
            //VM_CONTEXT->VM_REG7 += VM_CONTEXT->VM_REG2;
            printf("add reg7 , reg2\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 68)
        {
            //VM_CONTEXT->VM_REG2 /= VM_CONTEXT->VM_REG6;
            printf("div reg2 , reg6\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0x80)
        {
            //*(&VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)) = *(VM_CONTEXT->VM_IP + 2);
            //printf("push 1 \n");
            //printf("push VM_CONTEXT \n");
            //printf("mov eax, sub_804875F(VM_CONTEXT, 1)                ; sub_804875F(VM_CONTEXT, 1) \n");
            printf("mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)] \n");
            VM_CONTEXT->VM_IP += 6;
        }
        if (*VM_CONTEXT->VM_IP == 119)
        {
            //VM_CONTEXT->VM_REG2 ^= VM_CONTEXT->VM_REG7;
            printf("xor reg2 , reg7\n");

            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 83)
        {
            //putchar(*VM_CONTEXT->VM_REG4);
            printf("push reg4\n");
            printf("call putchar                                 ; putchar(*VM_CONTEXT->VM_REG4)\n");

            VM_CONTEXT->VM_IP += 2;
        }
        if (*VM_CONTEXT->VM_IP == 34)
        {
            //VM_CONTEXT->VM_REG2 = VM_CONTEXT->VM_REG2 >> VM_CONTEXT->VM_REG3;
            printf("shr reg2 , reg3\n");

            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 35)
        {
            //VM_CONTEXT->VM_REG2 <<= VM_CONTEXT->VM_REG3;
            printf("shl reg2 , reg3\n");

            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0x99) {
            printf("exit\n");
            break;
        }
        if (*VM_CONTEXT->VM_IP == 118)
        {
            //VM_CONTEXT->VM_REG4 = *VM_CONTEXT->VM_SP;
            //*VM_CONTEXT->VM_SP = 0;
            //VM_CONTEXT->VM_SP += 4;

            printf("pop reg4\n");
            printf("add esp , 4\n");
            VM_CONTEXT->VM_IP += 5;
        }
        if (*VM_CONTEXT->VM_IP == 84)
        {
            //VM_REG4 = VM_CONTEXT->VM_REG4;
            //*VM_REG4 = getchar();
            printf("call getchar\n");
            printf("mov [reg4] , eax\n");
            VM_CONTEXT->VM_IP += 2;
        }
        if (*VM_CONTEXT->VM_IP == 48)
        {
            VM_CONTEXT->VM_REG2 |= VM_CONTEXT->VM_REG3;
            printf("or reg2 , reg3\n");

            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 49)
        {
            //VM_CONTEXT->VM_REG2 &= VM_CONTEXT->VM_REG3;
            printf("and reg2 , reg3\n");

            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 50)
        {
            //VM_CONTEXT->VM_REG4 = VM_CONTEXT->VM_IP[1];
            printf("mov reg4 , [ip +1]\n");
            VM_CONTEXT->VM_IP += 2;
        }
        if (*VM_CONTEXT->VM_IP == 9)
        {
            //VM_CONTEXT->VM_REG2 = 1877735783;
            printf("mov reg2 , 0x7B\n");

            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 16)
        {
            //VM_CONTEXT->VM_REG7 = VM_CONTEXT->VM_REG2;
            printf("mov reg7 , reg2\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 51)
        {
            //VM_CONTEXT->VM_REG5 = VM_CONTEXT->VM_REG2;
            printf("mov reg5 , reg2\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 52)
        {
            //VM_CONTEXT->VM_REG3 = VM_CONTEXT->VM_IP[1];
            printf("mov reg3 , [ip +1]\n");
            VM_CONTEXT->VM_IP += 2;
        }
        if (*VM_CONTEXT->VM_IP == 0xFE)
        {
            //VM_CONTEXT->VM_REG2 = VM_CONTEXT->VM_REG7;
            printf("mov reg2 , reg7\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 17)
        {
            //printf("%x\n", VM_CONTEXT->VM_REG2);

            printf("push reg2\n");
            printf("call _printf                              ; printf(reg2)\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0xA0)
        {
            // if ( VM_CONTEXT->VM_REG2 != 1877735783 ) exit(0);

            printf("cmp reg2 , 0x7B\n");
            printf("jz short loc_8048D3B                      ; call exit\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0xA1)
        {
            //read(0, s, 0x2Cu);
            printf("push 0x2Cu\n");
            printf("push buf_addr\n");
            printf("push 0\n");
            printf("call _read                                      ;read(0,buf_addr,0x2Cu)\n");

            // if ( strlen(s) != 44 )
            //     exit(0);

            printf("push    offset buf_addr                        ; buf_addr \n");
            printf("call    _strlen \n");
            printf("cmp     eax, 2Ch  \n");
            printf("jz      short loc_8048D3B                      ; call exit\n");
            printf("jz      short loc_8048D3B                      ; call exit\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0xB1)
        {
            //VM_CONTEXT->VM_REG7 = dword_804B080[0];
            printf("mov reg7 , 0x7B;\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0xB2)
        {
            //VM_CONTEXT->VM_REG7 = VM_REG7;
            printf("mov reg7 , 0x2F\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0xA4)
        {
            //dword_804B080[VM_CONTEXT->VM_IP[1]] = VM_CONTEXT->VM_REG2;
            printf("mov dword_804B080[0x%X] reg2 \n", VM_CONTEXT->VM_IP[1]);
            VM_CONTEXT->VM_IP += 4;
        }
        if (*VM_CONTEXT->VM_IP == 0xB3)
        {
            //VM_CONTEXT->VM_REG7 = VM_REG7_0;
            printf("mov reg7 , 0x37\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0xB4)
        {
            //VM_CONTEXT->VM_REG7 = VM_REG7_1;
            printf("mov reg7 , 0xE8\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0xC1)
        {
            //VM_CONTEXT->VM_REG2 = buff[VM_CONTEXT->VM_IP[1]];
            printf("mov reg2 , buf_addr[0x%X]\n", VM_CONTEXT->VM_IP[1]);

            VM_CONTEXT->VM_IP += 2;
        }
        if (*VM_CONTEXT->VM_IP == 0xC7)
        {
            // if ( VM_REG2 != VM_CONTEXT->VM_REG2 )
            //     exit(0);
            printf("cmp reg2 , reg2_0\n");
            printf("jz short loc_8048D3B                      ; call exit\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0xC8)
        {
            // if ( VM_REG2_0 != VM_CONTEXT->VM_REG2 )
            //     exit(0);
            printf("cmp 0xCF1304DC , reg2\n");
            printf("jz short loc_8048D3B                      ; call exit\n");
            ++VM_CONTEXT->VM_IP;
        }
        if (*VM_CONTEXT->VM_IP == 0xC2)
        {
            // if ( *(VM_CONTEXT->VM_IP + 1) != VM_CONTEXT->VM_REG2 )
            //     exit(0);
            printf("cmp 0x%X , reg2\n", *(VM_CONTEXT->VM_IP + 1));
            printf("jz short loc_8048D3B                      ; call exit\n");
            VM_CONTEXT->VM_IP += 5;
        }
    }
}

int main() {
    VM_CONTEXT VM_CONTEXT;
    vm_init(&VM_CONTEXT, opcode);
    runvm(&VM_CONTEXT);


}

恢复的汇编代码

push 0x2Cu
push buf_addr
push 0
call _read                                      ;read(0,buf_addr,0x2Cu)
push    offset buf_addr                        ; buf_addr
call    _strlen
cmp     eax, 2Ch
jz      short loc_8048D3B                      ; call exit
jz      short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x0]
mov reg7 , 0x7B;
xor reg2 , reg7
cmp 0x4A , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x1]
mov reg7 , 0x2F
xor reg2 , reg7
cmp 0x19 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x2]
mov reg7 , 0xE8
xor reg2 , reg7
cmp 0xDD , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x3]
mov reg7 , 0x37
xor reg2 , reg7
cmp 0xF , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x4]
mov reg7 , 0x2F
xor reg2 , reg7
cmp 0x1B , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x5]
mov reg7 , 0xE8
xor reg2 , reg7
cmp 0x89 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x6]
mov reg7 , 0x7B;
xor reg2 , reg7
cmp 0x19 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x7]
mov reg7 , 0x37
xor reg2 , reg7
cmp 0x54 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x8]
mov reg7 , 0x7B;
xor reg2 , reg7
cmp 0x4F , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x9]
mov reg7 , 0x7B;
xor reg2 , reg7
cmp 0x4E , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0xA]
mov reg7 , 0x37
xor reg2 , reg7
cmp 0x55 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0xB]
mov reg7 , 0x37
xor reg2 , reg7
cmp 0x56 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0xC]
mov reg7 , 0xE8
xor reg2 , reg7
cmp 0x8E , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0xD]
mov reg7 , 0x2F
xor reg2 , reg7
cmp 0x49 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0xE]
mov reg7 , 0x37
xor reg2 , reg7
cmp 0xE , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0xF]
mov reg7 , 0x7B;
xor reg2 , reg7
cmp 0x4B , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x10]
mov reg7 , 0x37
xor reg2 , reg7
cmp 0x6 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x11]
mov reg7 , 0x37
xor reg2 , reg7
cmp 0x54 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x12]
mov reg7 , 0x2F
xor reg2 , reg7
cmp 0x1A , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x13]
mov reg7 , 0x7B;
xor reg2 , reg7
cmp 0x42 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x14]
mov reg7 , 0x37
xor reg2 , reg7
cmp 0x53 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x15]
mov reg7 , 0x7B;
xor reg2 , reg7
cmp 0x1F , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x16]
mov reg7 , 0x37
xor reg2 , reg7
cmp 0x52 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x17]
mov reg7 , 0xE8
xor reg2 , reg7
cmp 0xDB , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x18]
mov reg7 , 0x7B;
xor reg2 , reg7
cmp 0x19 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x19]
mov reg7 , 0xE8
xor reg2 , reg7
cmp 0xD9 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x1A]
mov reg7 , 0x7B;
xor reg2 , reg7
cmp 0x19 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x1B]
mov reg7 , 0x37
xor reg2 , reg7
cmp 0x55 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x1C]
mov reg7 , 0x2F
xor reg2 , reg7
cmp 0x19 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x1D]
mov reg7 , 0x37
xor reg2 , reg7
cmp 0x0 , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x1E]
mov reg7 , 0x7B;
xor reg2 , reg7
cmp 0x4B , reg2
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x1F]
mov reg7 , 0x2F
xor reg2 , reg7
cmp 0x1E , reg2

//32 - 36
jz short loc_8048D3B                      ; call exit
mov reg2 , buf_addr[0x20]
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
mov reg7 , reg2
mov reg2 , buf_addr[0x21]
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
add reg7 , reg2
mov reg2 , buf_addr[0x22]
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
add reg7 , reg2
mov reg2 , buf_addr[0x23]
add reg7 , reg2
mov reg2 , reg7
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shr reg2 , reg3
xor reg2 , reg7
mov reg7 , reg2
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
and reg2 , reg3
xor reg2 , reg7
mov reg7 , reg2
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
and reg2 , reg3
xor reg2 , reg7
mov reg7 , reg2
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shr reg2 , reg3
xor reg2 , reg7
cmp reg2 , 0x7B
jz short loc_8048D3B                      ; call exit

//36-40

mov reg2 , buf_addr[0x24]
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
mov reg7 , reg2
mov reg2 , buf_addr[0x25]
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
add reg7 , reg2
mov reg2 , buf_addr[0x26]
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
add reg7 , reg2
mov reg2 , buf_addr[0x27]
add reg7 , reg2
mov reg2 , reg7
mov reg4 , [ip +1]
mul reg2 , reg4
mov reg5 , reg2
xor reg2 , reg7
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shr reg2 , reg3
mov reg6 , reg2
mov reg2 , reg6
xor reg2 , reg5
xor reg2 , reg7
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
xor reg2 , reg7
xor reg2 , reg5
xor reg2 , reg6
mov reg7 , reg2
mov reg4 , [ip +1]
mul reg2 , reg4
mov reg5 , reg2
xor reg2 , reg7
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shr reg2 , reg3
mov reg6 , reg2
mov reg2 , reg6
xor reg2 , reg5
xor reg2 , reg7
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
xor reg2 , reg7
xor reg2 , reg5
xor reg2 , reg6
cmp reg2 , reg2_0
jz short loc_8048D3B                      ; call exit

// 41-44
mov reg2 , buf_addr[0x28]
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
mov reg7 , reg2
mov reg2 , buf_addr[0x29]
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
add reg7 , reg2
mov reg2 , buf_addr[0x2A]
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
add reg7 , reg2
mov reg2 , buf_addr[0x2B]
add reg7 , reg2
mov reg2 , reg7
mov reg4 , [ip +1]
mul reg2 , reg4
mov reg5 , reg2
xor reg2 , reg7
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shr reg2 , reg3
mov reg6 , reg2
mov reg2 , reg6
xor reg2 , reg5
xor reg2 , reg7
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
xor reg2 , reg7
xor reg2 , reg5
xor reg2 , reg6
mov reg7 , reg2
mov reg4 , [ip +1]
mul reg2 , reg4
mov reg5 , reg2
xor reg2 , reg7
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shr reg2 , reg3
mov reg6 , reg2
mov reg2 , reg6
xor reg2 , reg5
xor reg2 , reg7
mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]
shl reg2 , reg3
xor reg2 , reg7
xor reg2 , reg5
xor reg2 , reg6
cmp 0xCF1304DC , reg2
jz short loc_8048D3B                      ; call exit
exit

分析汇编

分析汇编流程得到它要求我们输入一串字符串,长度限制为44,前面32字节校验比较简单,我们看到一串重复的代码

mov reg2 , buf_addr[0x0]
mov reg7 , 0x7B;
xor reg2 , reg7
cmp 0x4A , reg2
jz short loc_8048D3B  

大致流程就是将输入的每一个字符异或一个key,然后和密文比较,如果不对就退出,我们用密文异或key就能得到前32位明文
16584abc45baff901c59dde3b1bb6701

后12位我们可以拆成三个相似的部分,这部分是对四个字节进行流加密然后对应密文,但是在汇编中存在一部分难以看懂的代码

mov [VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)] , [(VM_CONTEXT->VM_IP + 2)]

我们不确定他改了什么值,更不确定他改成什么(其实我们偷偷动调试一下就能发现VM_CONTEXT->VM_REG1 + sub_804875F(VM_CONTEXT, 1)是reg3)
不过这明显是流加密不是块加密,trace的结果也不会特别难看
这时候我们就该辅助trace结果进行分析进行,我们需要编写ida-python脚本
我们为每个opcode判断的尾部添加hook,建立汇编映射表

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
asm_opcode_dict = {
    0x80487F9:["sub esp , 4","push, [ip+1]"],
    0x8048829:["add reg1 , reg3"],
    0x8048859:["sub reg2 , reg5;     now:reg5: reg5_value"],
    0x804888A:["mul reg2 , reg4"],
    0x80488B2:["mov reg2 , reg6"],
    0x80488E2:["xor reg2 , reg5      now:reg5: reg5_value"],
    0x8048912:["xor reg2 , reg6"],
    0x804893A:["mov reg6 , reg2"],
    0x804896A:["add reg7 , reg2"],
    0x80489A1:["div reg2 , reg6"],
    0x80489E1:["mov reg3 , reg3_value"],
    0x8048A11:["xor reg2 , reg7"],
    0x8048A45:["putchar([reg4])"],
    0x8048A77:["shr reg2 , reg3"],
    0x8048AA9:["shl reg2 , reg3"],
    #0x8048AB7:["exit"],
    0x8048AFF:["pop reg4","add esp , 4"],
    0x8048B30:["call getchar","mov [reg4] , eax"],
    0x8048B60:["or reg2 , reg3"],
    0x8048B90:["and reg2 , reg3"],
    0x8048BC1:["mov reg4 , reg4_value"],
    0x8048BE7:["mov reg2 , 0x6FEBF967"],
    0x8048C0F:["mov reg7 , reg2"],
    0x8048C37:["mov reg5 , reg2"],
    0x8048C68:["mov reg3 , reg3_value"],
    0x8048C90:["mov reg2 , reg7"],
    0x8048CC3:["print(reg2)"],
    0x8048CEC:["cmp reg2 , 0x6FEBF967","jz short loc_8048D3B                      ; call exit"],
    0x8048D47:["read(0,buf_addr,0x2Cu)","call _strlen(buf_addr)","cmp     eax, 2Ch","jz      short loc_8048D3B                      ; call exit"],
    0x8048D6F:["mov reg7 , 0x2F"],
    0x8048D97:["mov dword_804B080[VM_CONTEXT->VM_IP[1]] reg2"],
    0x8048E03:["mov reg7 , 0x37"],
    0x8048E2B:["mov reg7 , 0xE8"],
    0x8048E6D:["mov reg2 , buf_addr[VM_CONTEXT->VM_IP[1]]"],
    0x8048E99:["cmp reg2 , 0xCF1304DC","jz short loc_8048D3B                      ; call exit"],
    0x8048ED1:["cmp reg2 , 0x283B8E84","jz short loc_8048D3B                      ; call exit"],
    0x8048F28:["cmp *(VM_CONTEXT->VM_IP + 1), reg2"]
}

批量建立断点

1
2
3
4
def add_breakpoint():
    for addr in asm_opcode_dict.keys():
        ida_dbg.add_bpt(addr)
        print(f"Breakpoint set at {hex(addr)} for opcode: {asm_opcode_dict[addr]}")

建立hook,必要时将我们需要的数值打印出

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
class hook_all_opecode(ida_dbg.DBG_Hooks):
    def get_reg3_value(self):
        reg3_addr = VM_CONTEXT_ADDR + 8
        reg3_val = ida_bytes.get_dword(reg3_addr)
        return reg3_val
    def get_reg4_value(self):
        reg4_addr = VM_CONTEXT_ADDR + 12
        reg4_val = ida_bytes.get_dword(reg4_addr)
        return reg4_val
    def get_reg5_value(self):
        reg5_addr = VM_CONTEXT_ADDR + 16
        reg5_val = ida_bytes.get_dword(reg5_addr)
        return reg5_val
    def dbg_bpt(self, tid, ea):
        for addr in asm_opcode_dict.keys():
            if ea == addr:
                opcode_list = asm_opcode_dict[addr]
                for opcode in opcode_list:
                    if "reg3_value" in opcode:
                        reg3_val = self.get_reg3_value()
                        opcode = opcode.replace("reg3_value", hex(reg3_val))
                    if "reg4_value" in opcode:
                        reg4_val = self.get_reg4_value()
                        opcode = opcode.replace("reg4_value", hex(reg4_val))
                    if "reg5_value" in opcode:
                        reg5_val = self.get_reg5_value()
                        opcode = opcode.replace("reg5_value", hex(reg5_val))
                    print(opcode)
        
        
        
        return 0
def install_hook():
    # 清理旧的 hook (如果存在于全局变量中)
    global my_hook
    try:
        if 'my_hook' in globals():
            my_hook.unhook()
            print("Removed old hook")
    except:
        pass

    # 安装新的 hook
    my_hook = hook_all_opecode()
    my_hook.hook()
    print("Hook installed. Please set the breakpoint manually.")

我们输入16584abc45baff901c59dde3b1bb6701aaaaaaaaaaaa,然后按住f9不松手,得到的汇编结果就很明显了

mov reg2 , buf_addr[VM_CONTEXT->VM_IP[1]]
mov reg3 , 0x18
shl reg2 , reg3
mov reg7 , reg2
mov reg2 , buf_addr[VM_CONTEXT->VM_IP[1]]
mov reg3 , 0x10
shl reg2 , reg3
add reg7 , reg2
mov reg2 , buf_addr[VM_CONTEXT->VM_IP[1]]
mov reg3 , 0x8
shl reg2 , reg3
add reg7 , reg2
mov reg2 , buf_addr[VM_CONTEXT->VM_IP[1]]
add reg7 , reg2
mov reg2 , reg7
mov reg3 , 0x5
shr reg2 , reg3
xor reg2 , reg7
mov reg7 , reg2
mov reg3 , 0x7
shl reg2 , reg3
mov reg3 , 0x98f17723
and reg2 , reg3
xor reg2 , reg7
mov reg7 , reg2
mov reg3 , 0x18
shl reg2 , reg3
mov reg3 , 0x35e4b920
and reg2 , reg3
xor reg2 , reg7
mov reg7 , reg2
mov reg3 , 0x12
shr reg2 , reg3
xor reg2 , reg7
cmp reg2 , 0x6FEBF967
jz short loc_8048D3B                      ; call exit


我们只能得到对第一部分对32-36字节的校验,因为校验失败就退出去了
这里的结合恢复的控制流汇编分析,buf_addr[VM_CONTEXT->VM_IP[1]]其实就是按顺序输入的按字节
前面的部分似乎是将输入的四个字节合并成一个32位整数,然后进行一系列的移位和异或操作,最后和一个常数比较
四字节又不多,我们直接模拟一下寄存器然后爆破

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
#include <stdio.h>

int main() {
    unsigned int enc[4] = { 0x61, 0x32, 0x35 ,0x34 }; // 假设有一些初始值

    unsigned int reg2 = 0;
    unsigned int reg3 = 0;
    unsigned int reg7 = 0;
    unsigned int reg4 = 0;

    // 初始化enc数组(这里只是示例,实际应该从某个地方获取值)
    // enc[0] = ...;
    // enc[1] = ...;
    // enc[2] = ...;
    // enc[3] = ...;

    reg2 = enc[0];              // mov reg2, buf_addr[VM_CONTEXT->VM_IP[1]]
    reg3 = 0x18;                // mov reg3, 0x18
    reg2 = reg2 << reg3;        // shl reg2, reg3
    reg7 = reg2;                // mov reg7, reg2

    reg2 = enc[1];              // mov reg2, buf_addr[VM_CONTEXT->VM_IP[1]]
    reg3 = 0x10;                // mov reg3, 0x10
    reg2 = reg2 << reg3;        // shl reg2, reg3
    reg7 = reg7 + reg2;         // add reg7, reg2

    reg2 = enc[2];              // mov reg2, buf_addr[VM_CONTEXT->VM_IP[1]]
    reg3 = 0x8;                 // mov reg3, 0x8
    reg2 = reg2 << reg3;        // shl reg2, reg3
    reg7 = reg7 + reg2;         // add reg7, reg2

    reg2 = enc[3];              // mov reg2, buf_addr[VM_CONTEXT->VM_IP[1]]
    reg7 = reg7 + reg2;         // add reg7, reg2

    reg2 = reg7;                // mov reg2, reg7
    reg3 = 0x5;                 // mov reg3, 0x5
    reg2 = reg2 >> reg3;        // shr reg2, reg3
    reg2 = reg2 ^ reg7;         // xor reg2, reg7
    reg7 = reg2;                // mov reg7, reg2

    reg3 = 0x7;                 // mov reg3, 0x7
    reg2 = reg2 << reg3;        // shl reg2, reg3
    reg3 = 0x98f17723;          // mov reg3, 0x98f17723
    reg2 = reg2 & reg3;         // and reg2, reg3
    reg2 = reg2 ^ reg7;         // xor reg2, reg7
    reg7 = reg2;                // mov reg7, reg2

    reg3 = 0x18;                // mov reg3, 0x18
    reg2 = reg2 << reg3;        // shl reg2, reg3
    reg3 = 0x35e4b920;          // mov reg3, 0x35e4b920
    reg2 = reg2 & reg3;         // and reg2, reg3
    reg2 = reg2 ^ reg7;         // xor reg2, reg7
    reg7 = reg2;                // mov reg7, reg2

    reg3 = 0x12;                // mov reg3, 0x12
    reg2 = reg2 >> reg3;        // shr reg2, reg3
    reg2 = reg2 ^ reg7;         // xor reg2, reg7

    // 最终比较
    if (reg2 == 0x6FEBF967) {    // cmp reg2, 0x6FEBF967
        printf("Match found!\n");
    }
    else {
        printf("No match.\n");
    }

    printf("Result: 0x%08X\n", reg2);
    return 0;
}

我这里直接给出答案了,把enc的生成一个改成一个爆破就行,找到就可以接下来爆破后面的8字节

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
#include <stdio.h>

int main() {
    unsigned int enc[4] = { 0x62 ,0x30 ,0x36 ,0x63 };

    unsigned int reg2 = 0;
    unsigned int reg3 = 0;
    unsigned int reg7 = 0;
    unsigned int reg4 = 0;
    unsigned int reg5 = 0;
    unsigned int reg6 = 0;


    reg2 = enc[0];        // mov reg2 , buf_addr[0x28]
    reg3 = 0x18;          // mov reg3 , 0x18
    reg2 = reg2 << reg3;  // shl reg2 , reg3
    reg7 = reg2;          // mov reg7 , reg2

    reg2 = enc[1];        // mov reg2 , buf_addr[0x29]
    reg3 = 0x10;          // mov reg3 , 0x10
    reg2 = reg2 << reg3;  // shl reg2 , reg3
    reg7 = reg7 + reg2;   // add reg7 , reg2

    reg2 = enc[2];        // mov reg2 , buf_addr[0x2A]
    reg3 = 0x8;           // mov reg3 , 0x8
    reg2 = reg2 << reg3;  // shl reg2 , reg3
    reg7 = reg7 + reg2;   // add reg7 , reg2

    reg2 = enc[3];        // mov reg2 , buf_addr[0x2B]
    reg7 = reg7 + reg2;   // add reg7 , reg2

    // 第一轮运算
    reg2 = reg7;          // mov reg2 , reg7
    reg4 = 0x20;          // mov reg4 , 0x20
    reg2 = reg2 * reg4;   // mul reg2 , reg4
    reg5 = reg2;          // mov reg5 , reg2
    reg2 = reg2 ^ reg7;   // xor reg2 , reg7

    reg3 = 0x11;          // mov reg3 , 0x11
    reg2 = reg2 >> reg3;  // shr reg2 , reg3
    reg6 = reg2;          // mov reg6 , reg2

    reg2 = reg6;          // mov reg2 , reg6
    reg2 = reg2 ^ reg5;   // xor reg2 , reg5
    reg2 = reg2 ^ reg7;   // xor reg2 , reg7

    reg3 = 0xd;           // mov reg3 , 0xd
    reg2 = reg2 << reg3;  // shl reg2 , reg3

    reg2 = reg2 ^ reg7;   // xor reg2 , reg7
    reg2 = reg2 ^ reg5;   // xor reg2 , reg5
    reg2 = reg2 ^ reg6;   // xor reg2 , reg6
    reg7 = reg2;          // mov reg7 , reg2

    // 第二轮运算(与第一轮完全相同)
    reg2 = reg7;          // mov reg2 , reg7
    reg4 = 0x20;          // mov reg4 , 0x20
    reg2 = reg2 * reg4;   // mul reg2 , reg4
    reg5 = reg2;          // mov reg5 , reg2
    reg2 = reg2 ^ reg7;   // xor reg2 , reg7

    reg3 = 0x11;          // mov reg3 , 0x11
    reg2 = reg2 >> reg3;  // shr reg2 , reg3
    reg6 = reg2;          // mov reg6 , reg2

    reg2 = reg6;          // mov reg2 , reg6
    reg2 = reg2 ^ reg5;   // xor reg2 , reg5
    reg2 = reg2 ^ reg7;   // xor reg2 , reg7

    reg3 = 0xd;           // mov reg3 , 0xd
    reg2 = reg2 << reg3;  // shl reg2 , reg3

    reg2 = reg2 ^ reg7;   // xor reg2 , reg7
    reg2 = reg2 ^ reg5;   // xor reg2 , reg5
    reg2 = reg2 ^ reg6;   // xor reg2 , reg6

    // 比较结果
    if (reg2 == 0xCF1304DC) {
        // jz short loc_8048D3B
        printf("Success!\n");
    }
    else {
        printf("Failed!\n");
    }


    return 0;
}

第三段

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
#include <stdio.h>

int main() {
    unsigned int enc[4] = { 0x64, 0x63, 0x32, 0x33 };

    // 假设我们有输入数据
    // 这里假设 enc 数组已经初始化
    // 例如: enc[0] = 0x28, enc[1] = 0x29, enc[2] = 0x3A, enc[3] = 0x2B
    

    unsigned int reg2 = 0;
    unsigned int reg3 = 0;
    unsigned int reg7 = 0;
    unsigned int reg4 = 0;
    unsigned int reg5 = 0;
    unsigned int reg6 = 0;

    // 第一部分:将4个字节组合成一个32位整数
    reg2 = enc[0];                // mov reg2, enc[0]
    reg3 = 0x18;                  // mov reg3, 0x18
    reg2 = reg2 << reg3;          // shl reg2, reg3
    reg7 = reg2;                  // mov reg7, reg2

    reg2 = enc[1];                // mov reg2, enc[1]
    reg3 = 0x10;                  // mov reg3, 0x10
    reg2 = reg2 << reg3;          // shl reg2, reg3
    reg7 = reg7 + reg2;           // add reg7, reg2

    reg2 = enc[2];                // mov reg2, enc[2]
    reg3 = 0x8;                   // mov reg3, 0x8
    reg2 = reg2 << reg3;          // shl reg2, reg3
    reg7 = reg7 + reg2;           // add reg7, reg2

    reg2 = enc[3];                // mov reg2, enc[3]
    reg7 = reg7 + reg2;           // add reg7, reg2

    printf("初始组合值 reg7: 0x%08X\n", reg7);

    // 第一部分变换
    reg2 = reg7;                  // mov reg2, reg7
    reg4 = 0x20;                  // mov reg4, 0x20
    reg2 = reg2 * reg4;           // mul reg2, reg4
    reg5 = reg2;                  // mov reg5, reg2

    reg2 = reg2 ^ reg7;           // xor reg2, reg7
    reg3 = 0x11;                  // mov reg3, 0x11
    reg2 = reg2 >> reg3;          // shr reg2, reg3
    reg6 = reg2;                  // mov reg6, reg2

    reg2 = reg6;                  // mov reg2, reg6
    reg2 = reg2 ^ reg5;           // xor reg2, reg5
    reg2 = reg2 ^ reg7;           // xor reg2, reg7
    reg3 = 0xd;                   // mov reg3, 0xd
    reg2 = reg2 << reg3;          // shl reg2, reg3
    reg2 = reg2 ^ reg7;           // xor reg2, reg7
    reg2 = reg2 ^ reg5;           // xor reg2, reg5
    reg2 = reg2 ^ reg6;           // xor reg2, reg6
    reg7 = reg2;                  // mov reg7, reg2

    printf("第一次变换后 reg7: 0x%08X\n", reg7);

    // 第二部分变换(与第一部分相同)
    reg2 = reg7;                  // mov reg2, reg7
    reg4 = 0x20;                  // mov reg4, 0x20
    reg2 = reg2 * reg4;           // mul reg2, reg4
    reg5 = reg2;                  // mov reg5, reg2

    reg2 = reg2 ^ reg7;           // xor reg2, reg7
    reg3 = 0x11;                  // mov reg3, 0x11
    reg2 = reg2 >> reg3;          // shr reg2, reg3
    reg6 = reg2;                  // mov reg6, reg2

    reg2 = reg6;                  // mov reg2, reg6
    reg2 = reg2 ^ reg5;           // xor reg2, reg5
    reg2 = reg2 ^ reg7;           // xor reg2, reg7
    reg3 = 0xd;                   // mov reg3, 0xd
    reg2 = reg2 << reg3;          // shl reg2, reg3
    reg2 = reg2 ^ reg7;           // xor reg2, reg7
    reg2 = reg2 ^ reg5;           // xor reg2, reg5
    reg2 = reg2 ^ reg6;           // xor reg2, reg6

    printf("最终结果 reg2: 0x%08X\n", reg2);

    // 比较结果
    if (reg2 == 0x283B8E84) {
        printf("验证成功!\n");
        // jz short loc_8048D3B
        // 这里应该是成功后的处理
    }
    else {
        printf("验证失败!\n");
        // 这里应该是失败后的处理
    }

    return 0;
}

最终flag 16584abc45baff901c59dde3b1bb6701a254b06cdc23

后记

这真的是babyvm吗。。。。唉我还是太菜了,不过这次除了机械化的部分交给了ai,大部分代码还是手写的
过几天整理一下把强网杯tradre的详细wp发出来,网上的全部都是谜语人大手子,ai大手子,对着5w行的trace能够手撕加密的,我真给你了