0%

护网杯RERERE题解

前话

本来我是不喜欢去写wp的…因为懒()…就算有做出来题目也是在文档中简单写一下就好了…这道题目之所以去写,可能不是因为它出的很好(由于VM题我做的太少了,这才算是第一道完整做掉的题目),而是我在接触这个题目的时候,对逆VM的经验或是看法有了自己的见解,这点比起我后来成功做出这道题更令我开心,希望下次遇到VM的题目时我能成功解出)

程序大体流程以及题解

程序流程还是比较简单易读的。用IDA打开后,差不多这个模样

Alt text

这里调用了三个函数,由一个函数表进行调用。

Alt text

调用的三个函数是最后三个,分别做了虚拟寄存器/栈的分配以及初始化,VM的调用以及最后赋值的还原。重点在于中间这一 步,

Alt text

一点都不花里胡哨,也没有混淆。就是这里用虚函数表做调用也太烦了= =翻得翻半天。再后面就是正常的VM流程了。逆出每个操作,然后打log,读汇编就可以解决…其实这一点都不算难题。要是以前有经验我觉得一小时到两小时就可以做完了= =还是输在too young。。打log的脚本如下

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
opcode = [79, 0, 0, 0, 47, 85, 5, 84, 48, 70, 0, 71, 34, 72, 2, 75, 51, 73, 79, 0, 0, 0, 70, 84, 16, 72, 1, 77, 39, 79, 0, 0, 0, 48, 84, 16, 72, 1, 68, 22, 79, 0, 0, 0, 57, 84, 16, 72, 1, 68, 11, 79, 0, 0, 0, 65, 84, 1, 72, 1, 68, 6, 71, 0, 72, 0, 75, 5, 71, 0, 80, 0, 67, 85, 64, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 51, 180, 136, 172, 84, 32, 72, 18, 71, 0, 75, 3, 80, 0, 67, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 74, 11, 148, 63, 84, 32, 72, 18, 71, 0, 75, 3, 80, 0, 67, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 124, 92, 220, 236, 84, 32, 72, 18, 71, 0, 75, 3, 80, 0, 67, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 57, 41, 117, 27, 84, 32, 72, 18, 71, 0, 75, 3, 80, 0, 67, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 30, 242, 107, 45, 84, 32, 72, 18, 71, 0, 75, 3, 80, 0, 67, 79, 0, 0, 0, 7, 84, 48, 71, 17, 86, 70, 0, 79, 0, 0, 0, 48, 84, 32, 89, 2, 79, 0, 0, 0, 10, 84, 32, 72, 2, 68, 9, 79, 0, 0, 0, 7, 84, 32, 89, 2, 79, 0, 0, 0, 16, 84, 32, 88, 18, 83, 16, 85, 43, 79, 131, 141, 181, 46, 84, 32, 72, 18, 71, 0, 75, 2, 80, 0, 67]

length = len(opcode)
i = 0
j = 0
stack = []
reg = [0] * 5
while(i < length):
if(opcode[i] == 67):
i += 1
continue
#break
elif(opcode[i] == 68):
print(i,' jl $%d'%(i + opcode[i + 1] + 2))
# if(reg[4] == -1):
# i += (opcode[i + 1] + 2)
# else:
i += 2
elif(opcode[i] == 69):
print(i,' div reg%d,reg%d'%(opcode[i + 1] >> 4,opcode[i + 1] & 0xf))
#reg[opcode[i + 1] >> 4] = (opcode[i + 1] >> 4) // (opcode[i + 1] & 0xf)
i += 2
elif(opcode[i] == 70):
print(i,' mov reg%d,input'%(opcode[i + 1] >> 4))
#reg[opcode[i + 1] >> 4] = ord('A')
i += 2
elif(opcode[i] == 71):
print(i,' xor reg%d,reg%d'%(opcode[i + 1] >> 4,(opcode[i + 1] & 0xf)))
#reg[opcode[i + 1] >> 4] = reg[(opcode[i + 1] >> 4)] ^ reg[(opcode[i + 1] & 0xf)]
i += 2
elif(opcode[i] == 72):
num = reg[(opcode[i + 1] >> 4)] - reg[(opcode[i + 1] & 0xf)]
print(i,' cmp reg%d,reg%d'%(opcode[i + 1] >> 4,(opcode[i + 1] & 0xf)))
# if num == 0:
# reg[4] = 0
# elif num > 0:
# reg[4] = 1
# else:
# reg[4] = -1
i += 2
elif(opcode[i] == 73):
print(i,' inc input')
i += 1
j += 1
elif(opcode[i] == 74):
print(i,' and reg%d,reg%d'%(opcode[i + 1] >> 4,(opcode[i + 1] >> 4)))
#reg[opcode[i + 1] >> 4] = (opcode[i + 1] >> 4) & (opcode[i + 1] & 0xf)
i += 2
elif(opcode[i] == 75):
print(i,' je $%d'%(i + opcode[i + 1] + 2))
# if(reg[4] == 0):
# i += (2 + opcode[i + 1])
# else:
# i += 2
i += 2
elif(opcode[i] == 76):
print(opcode)
for k in range(15):
opcode[i + k + 1] ^= 0x66
print(opcode)
i += 16
elif(opcode[i] == 77):
print(i,' jg $%d'%(opcode[i + 1] + 2 + i))
# if(reg[4] == 1):
# i += (2 + opcode[i + 1])
# else:
# i += 2
i += 2
elif(opcode[i] == 78):
print(i,' dec reg%d'%(opcode[i + 1] >> 4))
#reg[opcode[i + 1] >> 4] -= 1
i += 2
elif(opcode[i] == 79):
subAddr = 0
for k in range(4):
subAddr <<= 8
subAddr |= opcode[i + k + 1]
print(i,' push %d'%subAddr)
stack.append(subAddr)
i += 5
elif(opcode[i] == 80):
print(i,' inc reg%d'%(opcode[i + 1] >> 4))
#reg[opcode[i + 1] >> 4] = (opcode[i + 1] >> 4) + 1
i += 2
elif(opcode[i] == 81):
print(i,' mov reg%d,reg%d'%((opcode[i + 1] >> 4),opcode[i + 1] & 0xf))
#reg[opcode[i + 1] >> 4] = (opcode[i + 1] >> 4)
i += 2
elif(opcode[i] == 82):
print(i,' push %d'%reg[(opcode[i + 1] >> 4)])
i += 2
elif(opcode[i] == 83):
print(i,' add reg%d,reg%d'%(opcode[i + 1] >> 4,opcode[i + 1] & 0xf))
#reg[opcode[i + 1] >> 4] = (opcode[i + 1] >> 4) + (opcode[i + 1] & 0xf)
i += 2
elif(opcode[i] == 84):
print(i,' pop reg%d'%(opcode[i + 1] >> 4))
#reg[opcode[i + 1] >> 4] = stack.pop()
i += 2
elif(opcode[i] == 85):
# if(reg[2] == 0):
# i += 2
# else:
# reg[2] -= 1
# i -= opcode[i + 1]
print(i,' je $%d'%(i - opcode[i + 1]))
i += 2
elif(opcode[i] == 86):
print(i,' dec input')
i += 1
elif(opcode[i] == 87):
print(i,' getNum%d'%reg[opcode[i + 1]])
i += 2
elif(opcode[i] == 88):
print(i,' mul reg%d,reg%d'%(opcode[i + 1] >> 4,opcode[i + 1] & 0xf))
#reg[opcode[i + 1] >> 4] = (opcode[i + 1] >> 4) * (opcode[i + 1] & 0xf)
i += 2
elif(opcode[i] == 89):
print(i,' sub reg%d,reg%d'%(opcode[i + 1] >> 4,opcode[i + 1] & 0xf))
#reg[opcode[i + 1] >> 4] = (opcode[i + 1] >> 4) - (opcode[i + 1] & 0xf)
i += 2
else:
print(opcode[i])
i += 1

最后打出来的log长这样,里面有一些重要的注释

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
0  push 47
5 je $0
7 pop reg3
9 mov reg0,input
11 xor reg2,reg2
13 cmp reg0,reg2 //与\x00做比较
15 je $68 //结束循环
17 inc input
18 push 70
23 pop reg1
25 cmp reg0,reg1
27 jg $68 //小于'F'
29 push 48
34 pop reg1
36 cmp reg0,reg1
38 jl $62 //大于'0'
40 push 57
45 pop reg1
47 cmp reg0,reg1 //且小于'9'
49 jl $62
51 push 65
56 pop reg0
58 cmp reg0,reg1
60 jl $68 //大于'A'
62 xor reg0,reg0
64 cmp reg0,reg0
66 je $73
68 xor reg0,reg0
70 inc reg0
73 je $9 //遍历检查
75 push 7
80 pop reg3
82 xor reg1,reg1
84 dec input
85 mov reg0,input
87 push 48
92 pop reg2
94 sub reg0,reg2 //-'0'
96 push 10
101 pop reg2
103 cmp reg0,reg2
105 jl $116 //若范围在'0' - '9'之间,输入为数字
107 push 7 //若不在,则继续-9,将'A' - 'F'转换成数字
112 pop reg2
114 sub reg0,reg2
116 push 16
121 pop reg2
123 mul reg1,reg2
125 add reg1,reg0 //十六进制转换
127 je $84 //这也许是loop?? 反正就是十六进制比较
129 push 867469484
134 pop reg2
136 cmp reg1,reg2 //比较结果为0x33b488ac
138 xor reg0,reg0
140 je $145
142 inc reg0
145 push 7
150 pop reg3
152 xor reg1,reg1
154 dec input
155 mov reg0,input
157 push 48
162 pop reg2
164 sub reg0,reg2
166 push 10
171 pop reg2
173 cmp reg0,reg2
175 jl $186
177 push 7
182 pop reg2
184 sub reg0,reg2
186 push 16
191 pop reg2
193 mul reg1,reg2
195 add reg1,reg0
197 je $154
199 push 1242272831
204 pop reg2
206 cmp reg1,reg2
208 xor reg0,reg0
210 je $215 //与上面没有差别,此时十六进制为0x4a0b943f
212 inc reg0
215 push 7
220 pop reg3
222 xor reg1,reg1
224 dec input
225 mov reg0,input
227 push 48
232 pop reg2
234 sub reg0,reg2
236 push 10
241 pop reg2
243 cmp reg0,reg2
245 jl $256
247 push 7
252 pop reg2
254 sub reg0,reg2
256 push 16
261 pop reg2
263 mul reg1,reg2
265 add reg1,reg0
267 je $224
269 push 2086460652 //0x7c5cdcec
274 pop reg2
276 cmp reg1,reg2
278 xor reg0,reg0
280 je $285
282 inc reg0
285 push 7
290 pop reg3
292 xor reg1,reg1
294 dec input
295 mov reg0,input
297 push 48
302 pop reg2
304 sub reg0,reg2
306 push 10
311 pop reg2
313 cmp reg0,reg2
315 jl $326
317 push 7
322 pop reg2
324 sub reg0,reg2
326 push 16
331 pop reg2
333 mul reg1,reg2
335 add reg1,reg0
337 je $294
339 push 959018267 //0x3929751b
344 pop reg2
346 cmp reg1,reg2
348 xor reg0,reg0
350 je $355
352 inc reg0
355 push 7
360 pop reg3
362 xor reg1,reg1
364 dec input
365 mov reg0,input
367 push 48
372 pop reg2
374 sub reg0,reg2
376 push 10
381 pop reg2
383 cmp reg0,reg2
385 jl $396
387 push 7
392 pop reg2
394 sub reg0,reg2
396 push 16
401 pop reg2
403 mul reg1,reg2
405 add reg1,reg0
407 je $364
409 push 519203629 //0x1ef26b2d
414 pop reg2
416 cmp reg1,reg2
418 xor reg0,reg0
420 je $425
422 inc reg0
425 push 7
430 pop reg3
432 xor reg1,reg1
434 dec input
435 mov reg0,input
437 push 48
442 pop reg2
444 sub reg0,reg2
446 push 10
451 pop reg2
453 cmp reg0,reg2
455 jl $466
457 push 7
462 pop reg2
464 sub reg0,reg2
466 push 16
471 pop reg2
473 mul reg1,reg2
475 add reg1,reg0
477 je $434
479 push 2207102254 //0x838db52e
484 pop reg2
486 cmp reg1,reg2
488 xor reg0,reg0
490 je $494
492 inc reg0

/*最后我们拿到了这六个数字
0x33b488ac
0x4a0b943f
0x7c5cdcec
0x3929751b
0x1ef26b2d
0x838db52e
但需要注意的是,这里遍历是从前往后,最后得到数字是从后往前
故需要将其还原回去*/

最后就只要将这些字符倒序再大写就可以拿到flag

Alt text

至此,本题就结束了。

对VM的看法

由于以前对于VM只有粗浅的认识,导致一开始我在打log的时候非常吃力。。我只关心指令本身,而对操作数的分析直接忽视了。。导致打出来的log非常奇怪。。我还很担心中间数字对于程序流的影响,毕竟有je,jmp,jg这些跳转。。还不知道输入该怎么处理,全部置’a’还是啥。。经验的缺乏导致做题缺乏自信,中间很多指令分析都有问题,逻辑越来越乱。。今天去复现才慢慢理解到,跳转指令只要分析出来就好了,你不需要去完整的走一遍程序流,寄存器值也不需要太过担心,唯一要去担心的是立即数,所以你需要将这些数字还原。。VM作为机器的模拟是非常有趣的,对reg的选择方法真是惊到我了233,一个字节分别4位作为选择子也太帅了吧!有机会我想看看机器指令的操作数是怎么配合的hhh

后记

逆VM也太JB有趣了!

政博学长说的一些操作有空会去尝试一波。

立个flag…下周要把锅全done了!