【wp】2020XCTF_逆向

前几天的XCTF最后一场终于打完了,三场比赛下来对逆向部分的大概感觉是从第一场的啥都不会做到后来的终于能有参与度了TvT,至少后两场的题目都是pc逆向,虽然特殊架构但好歹能做(tcl

本文是三场XCTF所有逆向题目的wp+复现整理。赛中拿到了7/10的flag,很多是跟着队里的大佬做出来的(tqltql),这边就试着独立复现一下,至少打完应该长长记性(

官方wp出了,借助wp提供的思路复现填坑√。(9/10)

1220那场的apk逆向还没复现完,因为博客要更新点配置所以就先放上来了qvq。

比赛官网:XCTF高校网络安全专题挑战赛

官方wp:

  1. XCTF高校网络安全专题挑战赛-华为云专场 官方Writeup - XCTF社区
  2. XCTF高校网络安全专题挑战赛-鲲鹏计算专场 官方Writeup - XCTF社区
  3. XCTF高校网络安全专题挑战赛-HarmonyOS和HMS专场 官方Writeup - XCTF社区

[12.20] 华为云专场

Weird_lua

根据官方wp,说是改了loadbyte和opcode顺序。这里假装没看过wp,看能不能分析出怎么看出来是这里魔改的。

首先看附件是luacheck_license_out.lua,可以用file分别看一下

单独运行./lua,可以看到版本号是5.3.3

这里运行目标文件的方式是./lua ./check_license_out.lua

感觉这个.lua文件实际可能是.luac文件,即lua字节码文件。

于是先随便写一个lua源码(除了不知名check以外,大致对目标文件check_license_out.lua进行复刻),用v5.3.3的lua(可以在lua官网Lua: download下载)编译成字节码,看看正常版和魔改版两个文件的区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
-- test.lua
function check(s)
local len=#s
if len==42 then
return "Check!"
else
return "length error"
end
end

io.write("input: ")
str=io.read()
print(check(str))

lua test.lua运行:(注意这里是用正常的lua解释器而不是题目给的)

然后用luac -o test.luac test.lua得到test.luac字节码文件,然后把check_license_out.lua(上)和test.luac(下)用010Editor打开对比十六进制码。

可以看到,除了最开始的一字节为"1B"和"1C"以外,其余文件头中不同的字节都相差较大,感觉是某个特定的换算关系,位置看起来却没什么规律。

定位到lua-5.3.3/src文件夹中,有两个执行luac dump相关操作的头文件,分别是ldump.h(save precompiled Lua chunks)和lundump.h(load precompiled Lua chunks),这里猜测是用于load的lundump.c被魔改。

从这里(类似于主函数的函数)开始是整个的load步骤,可以看到上面对比中第一个差距比较大的字节是在0004h的位置,应该是在checkHeader部分,跟进这个checkHeader()函数。

237行是check文件头的"\x1bLua"部分(题目开头0x1c这个很容易看出就是硬改),而下面开始是check LUAC_VERSION即lua的版本号,本来应该是0x53,但是题目文件对应位置是0xAC,而且往后对照可以看到,不同字节部分都用了LoadByte()函数,猜测是在加载字节的时候有魔改,至于具体怎么魔改那可能得靠直觉了吧(。),大概能靠猜?(或者看lua文件的反编译= =

在官方wp里,魔改的具体信息已经给了:

1
2
3
4
5
6
static lu_byte LoadByte (LoadState *S) {
lu_byte x;
LoadVar(S, x);
//x ^= 0xff; 此行为魔改部分
return x;
}

跟着官方wp走不通的思路

本部分可跳过,最后因为刚刚接触lua,对这个语言不熟悉,卡在创造不出帖子里提到的 “用于 dump 的函数,尽量覆盖所有的指令操作,否则只能提取出已有的指令” 的lua函数,帖子里的test无法在5.3版本的lua中提取出所有字节码,需要自己动手创造(。

因为这里LoadByte的魔改会影响到下一步的opcode顺序还原,所以要先把这些错误字节xor回去。

又因为LoadByte的更改只影响到部分字节,我们并不清楚具体哪些字节遭到了祸害,所以想到了一个比较笨的方法就是重新编译一个lua解释器,这样我们就有了opcode顺序魔改前的解释器和魔改后的解释器(题目所给的)。

(如果有师傅有更好的办法求告知><)

将之前的lua-5.3.3文件夹复制到一个新路径,cd到文件夹中,执行make clean将文件中上一次的编译临时文件清空。其实理论上说不复制出来也应该不会影响到原来的lua,但为了保险起见还是这么干吧。

然后把lua-5.3.3/src/ldump.c(注意文件名!!)的52-56行即DumpByte()函数部分改为:

1
2
3
4
5
static void DumpByte (int y, DumpState *D) {
lu_byte x = (lu_byte)y;
x^=0xff;//增加此行
DumpVar(x, D);
}

保存,退回到lua-5.3.3根目录下,在WSL中用make linux编译。

至于为什么是改ldump.c而不是改官方wp指定魔改的lundump.c,因为ldump.c的作用是保存字节码,而lundump.c的作用是加载字节码。这里参照[原创]用 Lua 简单还原 OpCode 顺序文中的方法进行还原。

帖子里的lua版本为v5.1.5,对这里v5.3.3并不能直接用(opcode有增加+有些5.3中新增的指令),需要对源码(Check_OpCode/check_opcode.lua)做一些修改,修改后的文件如下。

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
-- check_opcode.lua

local bit = require('bit')
local test = require('test')

-- 加载用正常 lua 的 dump 文件
local fp = io.open("test.luac","rb")
local ori_data = fp:read("*all")
fp:close()

-- print('data len '.. #data)
-- print('ori_data len ' .. #ori_data)

local ori_op_name = {
"MOVE",
"LOADK",
"LOADKX",
"LOADBOOL",
"LOADNIL",
"GETUPVAL",
"GETTABUP",
"GETTABLE",
"SETTABUP",
"SETUPVAL",
"SETTABLE",
"NEWTABLE",
"SELF",
"ADD",
"SUB",
"MUL",
"MOD",
"POW",
"DIV",
"IDIV",
"BAND",
"BOR",
"BXOR",
"SHL",
"SHR",
"UNM",
"BNOT",
"NOT",
"LEN",
"CONCAT",
"JMP",
"EQ",
"LT",
"LE",
"TEST",
"TESTSET",
"CALL",
"TAILCALL",
"RETURN",
"FORLOOP",
"FORPREP",
"TFORCALL",
"TFORLOOP",
"SETLIST",
"CLOSURE",
"VARARG",
"EXTRAARG",
}
local data = string.dump(test) -- dump
local new_op = {}
-- 用目标 lua 和正常 lua 的 dump 数据对比
for i = 2, #data do
local by_ori = string.byte(ori_data,i)
local by_new = string.byte(data,i)
if by_ori ~= by_new then
-- 字节码被xor 0xff处理
local op_name = ori_op_name[by_ori + 1]
local op_idx = by_new
-- if by_ori>47 then
-- op_name = ori_op_name[(by_ori~0xff) + 1]
-- op_idx = (by_new~0xff)
-- end
if op_name~=nil then
if new_op[op_name]~=nil and new_op[op_name]~=op_idx then
print(op_name,new_op[op_name])
end
new_op[op_name] = op_idx
end
end
end

print("old \t new \t name")
local cnt=0
for idx, op_name in pairs(ori_op_name) do
local tmp = ''
if new_op[op_name] ~= nil then
tmp = new_op[op_name]
cnt=cnt+1
end
print((idx - 1) .. "\t" .. tmp .. "\t" .. op_name )
end
print(cnt)

将题目附件的lua重命名为lua_problem并复制到Check_OpCode文件夹中,同时将刚刚xor了字节的lua-5.3.3/src/lua也复制过来。

P.S. 由于帖子中给出的test.lua与前文中最开始用到的lua文件重名,故以下test.lua均代表为帖子中的lua文件,前文中的test.lua不再被引用。

依次在WSL中运行:

1
2
./lua dump_test_luac.lua
./lua_problem check_opcode.lua

(然后发现写不出能覆盖所有字节码的test(),遂放弃该思路。

自创思路

我们知道在lua源码里,opcode是按顺序存放在lopcodes.hlopcodes.c里的,故顺序魔改其实可以追踪lua解释器在ida中的反编译即可。(这里的lua就是题目给的那个。

源码中的opcode处:

lua加载进ida中,用shift+F12提取字符串,并用ctrl+F搜索“MOVE”

双击,按x找到交叉引用

这里就是魔改后的opcode顺序!

那么我们现在就掌握了所有魔改的信息,开始恢复还原。

这里用到的是著名的lua反汇编/反编译项目viruscamp/luadec: Lua Decompiler for lua 5.1 , 5.2 and 5.3,由于lua解释器被魔改(xor 0xff和opcode顺序),所以我们需要在原安装步骤的基础上做些调整。

1
2
3
4
5
6
7
8
git clone https://github.com/viruscamp/luadec
cd luadec
git submodule update --init lua-5.3
cd lua-5.3
###### 此处在make之前需要调整源码!
make linux
cd ../luadec
make LUAVER=5.3

调整源码如下:

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
/* luadec/lua-5.3/src/lopcodes.h 原line165-232 */
typedef enum {
OP_MOVE,
OP_LOADK,
OP_LOADKX,
OP_LOADBOOL,
OP_LOADNIL,
OP_GETUPVAL,
OP_LT,
OP_RETURN,
OP_GETTABLE,
OP_TESTSET,
OP_SELF,
OP_NEWTABLE,
OP_LE,
OP_ADD,
OP_SUB,
OP_MUL,
OP_MOD,
OP_POW,
OP_DIV,
OP_IDIV,
OP_BAND,
OP_BOR,
OP_BXOR,
OP_SHL,
OP_SHR,
OP_UNM,
OP_BNOT,
OP_NOT,
OP_LEN,
OP_GETTABUP,
OP_JMP,
OP_TEST,
OP_SETTABUP,
OP_TFORCALL,
OP_FORPREP,
OP_EXTRAARG,
OP_CALL,
OP_FORLOOP,
OP_SETLIST,
OP_CONCAT,
OP_TAILCALL,
OP_TFORLOOP,
OP_VARARG,
OP_SETTABLE,
OP_SETUPVAL,
OP_CLOSURE,
OP_EQ,
} OpCode;
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
/* luadec/lua-5.3/src/lopcodes.c 原line20-69 */
LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES+1] = {
"MOVE",
"LOADK",
"LOADKX",
"LOADBOOL",
"LOADNIL",
"GETUPVAL",
"LT",
"RETURN",
"GETTABLE",
"TESTSET",
"SELF",
"NEWTABLE",
"LE",
"ADD",
"SUB",
"MUL",
"MOD",
"POW",
"DIV",
"IDIV",
"BAND",
"BOR",
"BXOR",
"SHL",
"SHR",
"UNM",
"BNOT",
"NOT",
"LEN",
"GETTABUP",
"JMP",
"TEST",
"SETTABUP",
"TFORCALL",
"FORPREP",
"EXTRAARG",
"CALL",
"FORLOOP",
"SETLIST",
"CONCAT",
"TAILCALL",
"TFORLOOP",
"VARARG",
"SETTABLE",
"SETUPVAL",
"CLOSURE",
"EQ",
NULL
};
1
2
3
4
5
6
7
/* luadec/lua-5.3/src/lundump.c 原line60-64 */
static lu_byte LoadByte (LoadState *S) {
lu_byte x;
LoadVar(S, x);
x^=0xff;
return x;
}
1
2
3
/* luadec/lua-5.3/src/Makefile 原line9 */
CC= gcc -std=gnu99 -m32
//因为题目所给的lua解释器为32位
1
2
3
/* luadec/luadec/Makefile 原line16 */
CC= gcc -m32
//同上

修改完源码后接着make。

走完流程后把check_license_out.lua的第一个字节改为"\x1b",以对上LUA_SIGNATURE,并复制到luadec/luadec文件夹中。

然后在luadec/luadec下运行:

1
./luadec -dis check_license_out.lua > discheck

(如wp所说,反编译会报错,只能反汇编,估计是乱魔改的原因哈哈哈哈。

得到反汇编的discheck。

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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
; Disassembled using luadec 2.2 rev: 895d923 for Lua 5.3 from https://github.com/viruscamp/luadec
; Command line: -dis check_license_out.lua

; Function: 0
; Defined at line: 0
; #Upvalues: 1
; #Parameters: 0
; Is_vararg: 2
; Max Stack Size: 53

0 [-]: __eq R0 0 ; R0 := closure(Function #0_0)
1 [-]: SETTABUP U0 K0 R0 ; U0["to_v"] := R0
2 [-]: NEWTABLE R0 21 0 ; R0 := {} (size = 21,0)
3 [-]: LOADK R1 K1 ; R1 := 172
4 [-]: LOADK R2 K2 ; R2 := 25
5 [-]: LOADK R3 K3 ; R3 := 60
6 [-]: LOADK R4 K4 ; R4 := 95
7 [-]: LOADK R5 K5 ; R5 := 5
8 [-]: LOADK R6 K6 ; R6 := 27
9 [-]: LOADK R7 K7 ; R7 := 49
10 [-]: LOADK R8 K8 ; R8 := 58
11 [-]: LOADK R9 K9 ; R9 := 171
12 [-]: LOADK R10 K5 ; R10 := 5
13 [-]: LOADK R11 K10 ; R11 := 253
14 [-]: LOADK R12 K11 ; R12 := 45
15 [-]: LOADK R13 K12 ; R13 := 87
16 [-]: LOADK R14 K13 ; R14 := 246
17 [-]: LOADK R15 K14 ; R15 := 197
18 [-]: LOADK R16 K15 ; R16 := 12
19 [-]: LOADK R17 K16 ; R17 := 97
20 [-]: LOADK R18 K17 ; R18 := 234
21 [-]: LOADK R19 K18 ; R19 := 159
22 [-]: LOADK R20 K19 ; R20 := 119
23 [-]: LOADK R21 K20 ; R21 := 157
24 [-]: LOADK R22 K21 ; R22 := 169
25 [-]: LOADK R23 K22 ; R23 := 121
26 [-]: LOADK R24 K23 ; R24 := 54
27 [-]: LOADK R25 K24 ; R25 := 242
28 [-]: (null) R0 25 1 ; R0[0] to R0[24] := R1 to R25 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=0, b=25, c=1, FPF=50
29 [-]: NEWTABLE R1 21 0 ; R1 := {} (size = 21,0)
30 [-]: LOADK R2 K25 ; R2 := 94
31 [-]: LOADK R3 K26 ; R3 := 117
32 [-]: LOADK R4 K27 ; R4 := 57
33 [-]: LOADK R5 K28 ; R5 := 37
34 [-]: LOADK R6 K23 ; R6 := 54
35 [-]: LOADK R7 K29 ; R7 := 110
36 [-]: LOADK R8 K30 ; R8 := 15
37 [-]: LOADK R9 K31 ; R9 := 223
38 [-]: LOADK R10 K32 ; R10 := 163
39 [-]: LOADK R11 K33 ; R11 := 133
40 [-]: LOADK R12 K34 ; R12 := 99
41 [-]: LOADK R13 K35 ; R13 := 237
42 [-]: LOADK R14 K36 ; R14 := 8
43 [-]: LOADK R15 K37 ; R15 := 128
44 [-]: LOADK R16 K6 ; R16 := 27
45 [-]: LOADK R17 K23 ; R17 := 54
46 [-]: LOADK R18 K38 ; R18 := 233
47 [-]: LOADK R19 K39 ; R19 := 181
48 [-]: LOADK R20 K24 ; R20 := 242
49 [-]: LOADK R21 K40 ; R21 := 55
50 [-]: LOADK R22 K41 ; R22 := 230
51 [-]: LOADK R23 K42 ; R23 := 62
52 [-]: LOADK R24 K43 ; R24 := 42
53 [-]: LOADK R25 K44 ; R25 := 252
54 [-]: LOADK R26 K45 ; R26 := 116
55 [-]: (null) R1 25 1 ; R1[0] to R1[24] := R2 to R26 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=1, b=25, c=1, FPF=50
56 [-]: NEWTABLE R2 48 0 ; R2 := {} (size = 48,0)
57 [-]: LOADK R3 K46 ; R3 := 81
58 [-]: LOADK R4 K47 ; R4 := 138
59 [-]: LOADK R5 K48 ; R5 := 85
60 [-]: LOADK R6 K49 ; R6 := 142
61 [-]: LOADK R7 K50 ; R7 := 185
62 [-]: LOADK R8 K51 ; R8 := 35
63 [-]: LOADK R9 K52 ; R9 := 229
64 [-]: LOADK R10 K53 ; R10 := 83
65 [-]: LOADK R11 K36 ; R11 := 8
66 [-]: LOADK R12 K54 ; R12 := 225
67 [-]: LOADK R13 K55 ; R13 := 92
68 [-]: LOADK R14 K31 ; R14 := 223
69 [-]: LOADK R15 K56 ; R15 := 222
70 [-]: LOADK R16 K57 ; R16 := 47
71 [-]: LOADK R17 K58 ; R17 := 182
72 [-]: LOADK R18 K59 ; R18 := 158
73 [-]: LOADK R19 K60 ; R19 := 17
74 [-]: LOADK R20 K61 ; R20 := 74
75 [-]: LOADK R21 K62 ; R21 := 34
76 [-]: LOADK R22 K63 ; R22 := 100
77 [-]: LOADK R23 K64 ; R23 := 43
78 [-]: LOADK R24 K65 ; R24 := 103
79 [-]: LOADK R25 K66 ; R25 := 102
80 [-]: LOADK R26 K67 ; R26 := 147
81 [-]: LOADK R27 K35 ; R27 := 237
82 [-]: LOADK R28 K68 ; R28 := 88
83 [-]: LOADK R29 K69 ; R29 := 73
84 [-]: LOADK R30 K70 ; R30 := 28
85 [-]: LOADK R31 K71 ; R31 := 224
86 [-]: LOADK R32 K72 ; R32 := 23
87 [-]: LOADK R33 K73 ; R33 := 44
88 [-]: LOADK R34 K74 ; R34 := 40
89 [-]: LOADK R35 K75 ; R35 := 154
90 [-]: LOADK R36 K76 ; R36 := 127
91 [-]: LOADK R37 K77 ; R37 := 16
92 [-]: LOADK R38 K21 ; R38 := 169
93 [-]: LOADK R39 K78 ; R39 := 160
94 [-]: LOADK R40 K79 ; R40 := 118
95 [-]: LOADK R41 K80 ; R41 := 51
96 [-]: LOADK R42 K81 ; R42 := 194
97 [-]: LOADK R43 K82 ; R43 := 31
98 [-]: LOADK R44 K83 ; R44 := 68
99 [-]: LOADK R45 K84 ; R45 := 89
100 [-]: LOADK R46 K85 ; R46 := 65
101 [-]: LOADK R47 K86 ; R47 := 162
102 [-]: LOADK R48 K87 ; R48 := 13
103 [-]: LOADK R49 K88 ; R49 := 141
104 [-]: LOADK R50 K89 ; R50 := 0
105 [-]: LOADK R51 K90 ; R51 := 244
106 [-]: LOADK R52 K19 ; R52 := 119
107 [-]: (null) R2 50 1 ; R2[0] to R2[49] := R3 to R52 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=50, c=1, FPF=50
108 [-]: LOADK R3 K91 ; R3 := 161
109 [-]: LOADK R4 K92 ; R4 := 198
110 [-]: LOADK R5 K93 ; R5 := 228
111 [-]: LOADK R6 K4 ; R6 := 95
112 [-]: LOADK R7 K94 ; R7 := 10
113 [-]: LOADK R8 K95 ; R8 := 78
114 [-]: LOADK R9 K28 ; R9 := 37
115 [-]: LOADK R10 K22 ; R10 := 121
116 [-]: LOADK R11 K96 ; R11 := 236
117 [-]: LOADK R12 K97 ; R12 := 59
118 [-]: LOADK R13 K3 ; R13 := 60
119 [-]: LOADK R14 K98 ; R14 := 91
120 [-]: LOADK R15 K99 ; R15 := 146
121 [-]: LOADK R16 K100 ; R16 := 46
122 [-]: LOADK R17 K101 ; R17 := 77
123 [-]: LOADK R18 K102 ; R18 := 218
124 [-]: LOADK R19 K103 ; R19 := 66
125 [-]: LOADK R20 K104 ; R20 := 200
126 [-]: LOADK R21 K105 ; R21 := 61
127 [-]: LOADK R22 K106 ; R22 := 241
128 [-]: LOADK R23 K107 ; R23 := 70
129 [-]: LOADK R24 K40 ; R24 := 55
130 [-]: LOADK R25 K108 ; R25 := 39
131 [-]: LOADK R26 K109 ; R26 := 227
132 [-]: LOADK R27 K43 ; R27 := 42
133 [-]: LOADK R28 K110 ; R28 := 2
134 [-]: LOADK R29 K111 ; R29 := 231
135 [-]: LOADK R30 K112 ; R30 := 235
136 [-]: LOADK R31 K113 ; R31 := 122
137 [-]: LOADK R32 K114 ; R32 := 135
138 [-]: LOADK R33 K115 ; R33 := 152
139 [-]: LOADK R34 K116 ; R34 := 137
140 [-]: LOADK R35 K117 ; R35 := 173
141 [-]: LOADK R36 K118 ; R36 := 232
142 [-]: LOADK R37 K119 ; R37 := 101
143 [-]: LOADK R38 K120 ; R38 := 75
144 [-]: LOADK R39 K38 ; R39 := 233
145 [-]: LOADK R40 K121 ; R40 := 21
146 [-]: LOADK R41 K44 ; R41 := 252
147 [-]: LOADK R42 K30 ; R42 := 15
148 [-]: LOADK R43 K33 ; R43 := 133
149 [-]: LOADK R44 K122 ; R44 := 111
150 [-]: LOADK R45 K123 ; R45 := 205
151 [-]: LOADK R46 K27 ; R46 := 57
152 [-]: LOADK R47 K124 ; R47 := 132
153 [-]: LOADK R48 K125 ; R48 := 187
154 [-]: LOADK R49 K126 ; R49 := 96
155 [-]: LOADK R50 K7 ; R50 := 49
156 [-]: LOADK R51 K127 ; R51 := 124
157 [-]: LOADK R52 K128 ; R52 := 86
158 [-]: (null) R2 50 2 ; R2[50] to R2[99] := R3 to R52 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=50, c=2, FPF=50
159 [-]: LOADK R3 K129 ; R3 := 19
160 [-]: LOADK R4 K130 ; R4 := 188
161 [-]: LOADK R5 K131 ; R5 := 80
162 [-]: LOADK R6 K132 ; R6 := 213
163 [-]: LOADK R7 K133 ; R7 := 106
164 [-]: LOADK R8 K134 ; R8 := 214
165 [-]: LOADK R9 K135 ; R9 := 203
166 [-]: LOADK R10 K136 ; R10 := 177
167 [-]: LOADK R11 K137 ; R11 := 56
168 [-]: LOADK R12 K138 ; R12 := 104
169 [-]: LOADK R13 K139 ; R13 := 82
170 [-]: LOADK R14 K29 ; R14 := 110
171 [-]: LOADK R15 K140 ; R15 := 196
172 [-]: LOADK R16 K141 ; R16 := 113
173 [-]: LOADK R17 K142 ; R17 := 155
174 [-]: LOADK R18 K143 ; R18 := 170
175 [-]: LOADK R19 K144 ; R19 := 150
176 [-]: LOADK R20 K26 ; R20 := 117
177 [-]: LOADK R21 K145 ; R21 := 26
178 [-]: LOADK R22 K146 ; R22 := 140
179 [-]: LOADK R23 K147 ; R23 := 144
180 [-]: LOADK R24 K148 ; R24 := 11
181 [-]: LOADK R25 K1 ; R25 := 172
182 [-]: LOADK R26 K149 ; R26 := 67
183 [-]: LOADK R27 K150 ; R27 := 209
184 [-]: LOADK R28 K151 ; R28 := 125
185 [-]: LOADK R29 K23 ; R29 := 54
186 [-]: LOADK R30 K8 ; R30 := 58
187 [-]: LOADK R31 K37 ; R31 := 128
188 [-]: LOADK R32 K152 ; R32 := 204
189 [-]: LOADK R33 K153 ; R33 := 186
190 [-]: LOADK R34 K154 ; R34 := 199
191 [-]: LOADK R35 K155 ; R35 := 189
192 [-]: LOADK R36 K156 ; R36 := 208
193 [-]: LOADK R37 K157 ; R37 := 239
194 [-]: LOADK R38 K158 ; R38 := 143
195 [-]: LOADK R39 K159 ; R39 := 249
196 [-]: LOADK R40 K13 ; R40 := 246
197 [-]: LOADK R41 K160 ; R41 := 1
198 [-]: LOADK R42 K161 ; R42 := 139
199 [-]: LOADK R43 K162 ; R43 := 33
200 [-]: LOADK R44 K12 ; R44 := 87
201 [-]: LOADK R45 K163 ; R45 := 64
202 [-]: LOADK R46 K45 ; R46 := 116
203 [-]: LOADK R47 K164 ; R47 := 84
204 [-]: LOADK R48 K165 ; R48 := 254
205 [-]: LOADK R49 K166 ; R49 := 126
206 [-]: LOADK R50 K167 ; R50 := 202
207 [-]: LOADK R51 K168 ; R51 := 148
208 [-]: LOADK R52 K169 ; R52 := 76
209 [-]: (null) R2 50 3 ; R2[100] to R2[149] := R3 to R52 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=50, c=3, FPF=50
210 [-]: LOADK R3 K170 ; R3 := 247
211 [-]: LOADK R4 K171 ; R4 := 115
212 [-]: LOADK R5 K172 ; R5 := 109
213 [-]: LOADK R6 K173 ; R6 := 3
214 [-]: LOADK R7 K174 ; R7 := 238
215 [-]: LOADK R8 K175 ; R8 := 114
216 [-]: LOADK R9 K176 ; R9 := 156
217 [-]: LOADK R10 K177 ; R10 := 195
218 [-]: LOADK R11 K32 ; R11 := 163
219 [-]: LOADK R12 K18 ; R12 := 159
220 [-]: LOADK R13 K178 ; R13 := 52
221 [-]: LOADK R14 K179 ; R14 := 36
222 [-]: LOADK R15 K180 ; R15 := 245
223 [-]: LOADK R16 K181 ; R16 := 240
224 [-]: LOADK R17 K182 ; R17 := 63
225 [-]: LOADK R18 K183 ; R18 := 153
226 [-]: LOADK R19 K184 ; R19 := 166
227 [-]: LOADK R20 K185 ; R20 := 167
228 [-]: LOADK R21 K186 ; R21 := 175
229 [-]: LOADK R22 K187 ; R22 := 9
230 [-]: LOADK R23 K188 ; R23 := 151
231 [-]: LOADK R24 K9 ; R24 := 171
232 [-]: LOADK R25 K189 ; R25 := 216
233 [-]: LOADK R26 K190 ; R26 := 207
234 [-]: LOADK R27 K191 ; R27 := 179
235 [-]: LOADK R28 K192 ; R28 := 72
236 [-]: LOADK R29 K193 ; R29 := 176
237 [-]: LOADK R30 K194 ; R30 := 48
238 [-]: LOADK R31 K195 ; R31 := 178
239 [-]: LOADK R32 K20 ; R32 := 157
240 [-]: LOADK R33 K196 ; R33 := 20
241 [-]: LOADK R34 K39 ; R34 := 181
242 [-]: LOADK R35 K197 ; R35 := 149
243 [-]: LOADK R36 K198 ; R36 := 53
244 [-]: LOADK R37 K199 ; R37 := 184
245 [-]: LOADK R38 K200 ; R38 := 4
246 [-]: LOADK R39 K201 ; R39 := 136
247 [-]: LOADK R40 K202 ; R40 := 165
248 [-]: LOADK R41 K203 ; R41 := 217
249 [-]: LOADK R42 K204 ; R42 := 50
250 [-]: LOADK R43 K205 ; R43 := 190
251 [-]: LOADK R44 K206 ; R44 := 191
252 [-]: LOADK R45 K207 ; R45 := 192
253 [-]: LOADK R46 K208 ; R46 := 193
254 [-]: LOADK R47 K209 ; R47 := 98
255 [-]: LOADK R48 K210 ; R48 := 215
256 [-]: LOADK R49 K42 ; R49 := 62
257 [-]: LOADK R50 K211 ; R50 := 112
258 [-]: LOADK R51 K212 ; R51 := 38
259 [-]: LOADK R52 K213 ; R52 := 90
260 [-]: (null) R2 50 4 ; R2[150] to R2[199] := R3 to R52 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=50, c=4, FPF=50
261 [-]: LOADK R3 K214 ; R3 := 123
262 [-]: LOADK R4 K215 ; R4 := 105
263 [-]: LOADK R5 K25 ; R5 := 94
264 [-]: LOADK R6 K216 ; R6 := 221
265 [-]: LOADK R7 K34 ; R7 := 99
266 [-]: LOADK R8 K217 ; R8 := 201
267 [-]: LOADK R9 K218 ; R9 := 206
268 [-]: LOADK R10 K219 ; R10 := 251
269 [-]: LOADK R11 K220 ; R11 := 14
270 [-]: LOADK R12 K221 ; R12 := 211
271 [-]: LOADK R13 K222 ; R13 := 220
272 [-]: LOADK R14 K223 ; R14 := 131
273 [-]: LOADK R15 K224 ; R15 := 212
274 [-]: LOADK R16 K225 ; R16 := 130
275 [-]: LOADK R17 K226 ; R17 := 134
276 [-]: LOADK R18 K10 ; R18 := 253
277 [-]: LOADK R19 K227 ; R19 := 120
278 [-]: LOADK R20 K228 ; R20 := 145
279 [-]: LOADK R21 K229 ; R21 := 18
280 [-]: LOADK R22 K230 ; R22 := 219
281 [-]: LOADK R23 K231 ; R23 := 79
282 [-]: LOADK R24 K232 ; R24 := 129
283 [-]: LOADK R25 K15 ; R25 := 12
284 [-]: LOADK R26 K233 ; R26 := 93
285 [-]: LOADK R27 K5 ; R27 := 5
286 [-]: LOADK R28 K234 ; R28 := 183
287 [-]: LOADK R29 K235 ; R29 := 107
288 [-]: LOADK R30 K236 ; R30 := 71
289 [-]: LOADK R31 K237 ; R31 := 226
290 [-]: LOADK R32 K238 ; R32 := 180
291 [-]: LOADK R33 K239 ; R33 := 24
292 [-]: LOADK R34 K17 ; R34 := 234
293 [-]: LOADK R35 K240 ; R35 := 7
294 [-]: LOADK R36 K241 ; R36 := 108
295 [-]: LOADK R37 K242 ; R37 := 174
296 [-]: LOADK R38 K243 ; R38 := 6
297 [-]: LOADK R39 K11 ; R39 := 45
298 [-]: LOADK R40 K244 ; R40 := 29
299 [-]: LOADK R41 K245 ; R41 := 32
300 [-]: LOADK R42 K246 ; R42 := 168
301 [-]: LOADK R43 K41 ; R43 := 230
302 [-]: LOADK R44 K14 ; R44 := 197
303 [-]: LOADK R45 K247 ; R45 := 41
304 [-]: LOADK R46 K2 ; R46 := 25
305 [-]: LOADK R47 K248 ; R47 := 255
306 [-]: LOADK R48 K249 ; R48 := 164
307 [-]: LOADK R49 K6 ; R49 := 27
308 [-]: LOADK R50 K250 ; R50 := 210
309 [-]: LOADK R51 K251 ; R51 := 248
310 [-]: LOADK R52 K16 ; R52 := 97
311 [-]: (null) R2 50 5 ; R2[200] to R2[249] := R3 to R52 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=50, c=5, FPF=50
312 [-]: LOADK R3 K252 ; R3 := 250
313 [-]: LOADK R4 K253 ; R4 := 22
314 [-]: LOADK R5 K24 ; R5 := 242
315 [-]: LOADK R6 K254 ; R6 := 243
316 [-]: LOADK R7 K255 ; R7 := 30
317 [-]: LOADK R8 K256 ; R8 := 69
318 [-]: (null) R2 6 6 ; R2[250] to R2[255] := R3 to R8 ; R(a)[(c-1)*FPF+i] := R(a+i), 1 <= i <= b, a=2, b=6, c=6, FPF=50
319 [-]: LOADK R3 K257 ; R3 := "encrypt"
320 [-]: __eq R4 1 ; R4 := closure(Function #0_1)
321 [-]: SETTABUP U0 R3 R4 ; U0[R3] := R4
322 [-]: LOADK R3 K258 ; R3 := "check_license"
323 [-]: __eq R4 2 ; R4 := closure(Function #0_2)
324 [-]: SETTABUP U0 R3 R4 ; U0[R3] := R4
325 [-]: LOADK R3 K259 ; R3 := "io"
326 [-]: GETTABUP R3 U0 R3 ; R3 := U0[R3]
327 [-]: LOADK R4 K260 ; R4 := "write"
328 [-]: GETTABLE R3 R3 R4 ; R3 := R3[R4]
329 [-]: LOADK R4 K261 ; R4 := "input: "
330 [-]: CALL R3 2 1 ; := R3(R4)
331 [-]: LOADK R3 K262 ; R3 := "license"
332 [-]: LOADK R4 K259 ; R4 := "io"
333 [-]: GETTABUP R4 U0 R4 ; R4 := U0[R4]
334 [-]: LOADK R5 K263 ; R5 := "read"
335 [-]: GETTABLE R4 R4 R5 ; R4 := R4[R5]
336 [-]: LOADK R5 K264 ; R5 := "*l"
337 [-]: CALL R4 2 2 ; R4 := R4(R5)
338 [-]: SETTABUP U0 R3 R4 ; U0[R3] := R4
339 [-]: LOADK R3 K258 ; R3 := "check_license"
340 [-]: GETTABUP R3 U0 R3 ; R3 := U0[R3]
341 [-]: LOADK R4 K262 ; R4 := "license"
342 [-]: GETTABUP R4 U0 R4 ; R4 := U0[R4]
343 [-]: CALL R3 2 1 ; := R3(R4)
344 [-]: RETURN R0 1 ; return


; Function: 0_0
; Defined at line: 2
; #Upvalues: 1
; #Parameters: 1
; Is_vararg: 0
; Max Stack Size: 10

0 [-]: NEWTABLE R1 0 0 ; R1 := {} (size = 0,0)
1 [-]: LOADK R2 K0 ; R2 := 1
2 [-]: GETTABUP R3 U0 K1 ; R3 := U0["string"]
3 [-]: GETTABLE R3 R3 K2 ; R3 := R3["len"]
4 [-]: MOVE R4 R0 ; R4 := R0
5 [-]: CALL R3 2 2 ; R3 := R3(R4)
6 [-]: LOADK R4 K0 ; R4 := 1
7 [-]: FORPREP R2 8 ; R2 -= R4; pc += 8 (goto 16)
8 [-]: LEN R6 R1 ; R6 := #R1
9 [-]: ADD R6 R6 K0 ; R6 := R6 + 1
10 [-]: GETTABUP R7 U0 K1 ; R7 := U0["string"]
11 [-]: GETTABLE R7 R7 K3 ; R7 := R7["byte"]
12 [-]: MOVE R8 R0 ; R8 := R0
13 [-]: MOVE R9 R5 ; R9 := R5
14 [-]: CALL R7 3 2 ; R7 := R7(R8 to R9)
15 [-]: __mode R1 R6 R7 ; R1[R6] := R7
16 [-]: (null) R2 -9 ; R2 += R4; if R2 <= R3 then R5 := R2; PC += -9 , goto 8 end
17 [-]: RETURN R1 2 ; return R1
18 [-]: RETURN R0 1 ; return


; Function: 0_1
; Defined at line: 15
; #Upvalues: 4
; #Parameters: 1
; Is_vararg: 0
; Max Stack Size: 10

0 [-]: GETTABUP R1 U0 K0 ; R1 := U0["to_v"]
1 [-]: MOVE R2 R0 ; R2 := R0
2 [-]: CALL R1 2 2 ; R1 := R1(R2)
3 [-]: NEWTABLE R2 0 0 ; R2 := {} (size = 0,0)
4 [-]: LOADK R3 K1 ; R3 := 1
5 [-]: LEN R4 R1 ; R4 := #R1
6 [-]: LOADK R5 K1 ; R5 := 1
7 [-]: FORPREP R3 10 ; R3 -= R5; pc += 10 (goto 18)
8 [-]: GETTABLE R7 R1 R6 ; R7 := R1[R6]
9 [-]: GETTABUP R8 U1 R6 ; R8 := U1[R6]
10 [-]: BXOR R7 R7 R8 ; R7 := R7 ~ R8
11 [-]: __mode R1 R6 R7 ; R1[R6] := R7
12 [-]: LEN R7 R2 ; R7 := #R2
13 [-]: ADD R7 R7 K1 ; R7 := R7 + 1
14 [-]: GETTABLE R8 R1 R6 ; R8 := R1[R6]
15 [-]: ADD R8 R8 K1 ; R8 := R8 + 1
16 [-]: GETTABUP R8 U2 R8 ; R8 := U2[R8]
17 [-]: __mode R2 R7 R8 ; R2[R7] := R8
18 [-]: (null) R3 -11 ; R3 += R5; if R3 <= R4 then R6 := R3; PC += -11 , goto 8 end
19 [-]: LOADK R3 K2 ; R3 := 0
20 [-]: LOADK R4 K1 ; R4 := 1
21 [-]: LEN R5 R1 ; R5 := #R1
22 [-]: LOADK R6 K1 ; R6 := 1
23 [-]: FORPREP R4 7 ; R4 -= R6; pc += 7 (goto 31)
24 [-]: GETTABLE R8 R2 R7 ; R8 := R2[R7]
25 [-]: GETTABUP R9 U3 R7 ; R9 := U3[R7]
26 [-]: __add 0 R8 R9 ; if R8 == R9 then goto 28 else goto 30
27 [-]: JMP R0 2 ; PC += 2 (goto 30)
28 [-]: ADD R3 R3 K1 ; R3 := R3 + 1
29 [-]: JMP R0 1 ; PC += 1 (goto 31)
30 [-]: ADD R3 R3 K3 ; R3 := R3 + 2
31 [-]: (null) R4 -8 ; R4 += R6; if R4 <= R5 then R7 := R4; PC += -8 , goto 24 end
32 [-]: LEN R4 R1 ; R4 := #R1
33 [-]: __add 1 R3 R4 ; if R3 ~= R4 then goto 35 else goto 39
34 [-]: JMP R0 4 ; PC += 4 (goto 39)
35 [-]: GETTABUP R4 U0 K4 ; R4 := U0["print"]
36 [-]: LOADK R5 K5 ; R5 := "bad"
37 [-]: CALL R4 2 1 ; := R4(R5)
38 [-]: JMP R0 3 ; PC += 3 (goto 42)
39 [-]: GETTABUP R4 U0 K4 ; R4 := U0["print"]
40 [-]: LOADK R5 K6 ; R5 := "good"
41 [-]: CALL R4 2 1 ; := R4(R5)
42 [-]: RETURN R0 1 ; return


; Function: 0_2
; Defined at line: 38
; #Upvalues: 1
; #Parameters: 1
; Is_vararg: 0
; Max Stack Size: 3

0 [-]: LEN R1 R0 ; R1 := #R0
1 [-]: SETTABUP U0 K0 R1 ; U0["size"] := R1
2 [-]: GETTABUP R1 U0 K0 ; R1 := U0["size"]
3 [-]: __add 1 R1 K1 ; if R1 ~= 25 then goto 5 else goto 9
4 [-]: JMP R0 4 ; PC += 4 (goto 9)
5 [-]: GETTABUP R1 U0 K2 ; R1 := U0["print"]
6 [-]: LOADK R2 K3 ; R2 := "length error"
7 [-]: CALL R1 2 1 ; := R1(R2)
8 [-]: RETURN R0 1 ; return
9 [-]: GETTABUP R1 U0 K4 ; R1 := U0["encrypt"]
10 [-]: MOVE R2 R0 ; R2 := R0
11 [-]: CALL R1 2 1 ; := R1(R2)
12 [-]: RETURN R0 1 ; return

说是反汇编其实还是有一点反编译注释的吼。

根据注释来走就很容易理顺逻辑啦(可以参考深入理解 Lua 虚拟机_腾讯技术工程 - MdEditor),算法用python还原是:

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
# Function 0_0:  to_v()
# Function 0_1: encrypt()
# Function 0_2: check_license()

def to_v(s):
return [ord(c) for c in s]
def encrypt(s):
global tb0,tb1,tb2
u1,u2,u3=tb0,tb2,tb1 #这个upvalue对应我也不知道是怎么看出来的,可能靠试吧
l1=to_v(s)
size=len(l1)
l2=[]
for i in range(size):
l1[i]^=u1[i]
l2.append(u2[l1[i]+1])
r3=0
for i in range(size):
if l2[i]==u3[i]:
r3+=1
else:
r3+=2
if r3!=size:
print("bad")
else:
print("good")
return
def check_license(s):
if len(s)!=25:
print("length error")
else:
encrypt(s)
return
if __name__ == '__main__':
tb0=[172, 25, 60, 95, 5, 27, 49, 58, 171, 5, 253, 45, 87, 246, 197, 12, 97, 234, 159, 119, 157, 169, 121, 54, 242]
tb1=[94, 117, 57, 37, 54, 110, 15, 223, 163, 133, 99, 237, 8, 128, 27, 54, 233, 181, 242, 55, 230, 62, 42, 252, 116]
tb2=[81, 138, 85, 142, 185, 35, 229, 83, 8, 225, 92, 223, 222, 47, 182, 158, 17, 74, 34, 100, 43, 103, 102, 147, 237, 88, 73, 28, 224, 23, 44, 40, 154, 127, 16, 169, 160, 118, 51, 194, 31, 68, 89, 65, 162, 13, 141, 0, 244, 119, 161, 198, 228, 95, 10, 78, 37, 121, 236, 59, 60, 91, 146, 46, 77, 218, 66, 200, 61, 241, 70, 55, 39, 227, 42, 2, 231, 235, 122, 135, 152, 137, 173, 232, 101, 75, 233, 21, 252, 15, 133, 111, 205, 57, 132, 187, 96, 49, 124, 86, 19, 188, 80, 213, 106, 214, 203, 177, 56, 104, 82, 110, 196, 113, 155, 170, 150, 117, 26, 140, 144, 11, 172, 67, 209, 125, 54, 58, 128, 204, 186, 199, 189, 208, 239, 143, 249, 246, 1, 139, 33, 87, 64, 116, 84, 254, 126, 202, 148, 76, 247, 115, 109, 3, 238, 114, 156, 195, 163, 159, 52, 36, 245, 240, 63, 153, 166, 167, 175, 9, 151, 171, 216, 207, 179, 72, 176, 48, 178, 157, 20, 181, 149, 53, 184, 4, 136, 165, 217, 50, 190, 191, 192, 193, 98, 215, 62, 112, 38, 90, 123, 105, 94, 221, 99, 201, 206, 251, 14, 211, 220, 131, 212, 130, 134, 253, 120, 145, 18, 219, 79, 129, 12, 93, 5, 183, 107, 71, 226, 180, 24, 234, 7, 108, 174, 6, 45, 29, 32, 168, 230, 197, 41, 25, 255, 164, 27, 210, 248, 97, 250, 22, 242, 243, 30, 69]
print("input: ",end='')
license=input()
check_license(license)

就可以根据逻辑写出exp:

1
2
3
4
5
6
7
tb0=[172, 25, 60, 95, 5, 27, 49, 58, 171, 5, 253, 45, 87, 246, 197, 12, 97, 234, 159, 119, 157, 169, 121, 54, 242]
tb1=[94, 117, 57, 37, 54, 110, 15, 223, 163, 133, 99, 237, 8, 128, 27, 54, 233, 181, 242, 55, 230, 62, 42, 252, 116]
tb2=[81, 138, 85, 142, 185, 35, 229, 83, 8, 225, 92, 223, 222, 47, 182, 158, 17, 74, 34, 100, 43, 103, 102, 147, 237, 88, 73, 28, 224, 23, 44, 40, 154, 127, 16, 169, 160, 118, 51, 194, 31, 68, 89, 65, 162, 13, 141, 0, 244, 119, 161, 198, 228, 95, 10, 78, 37, 121, 236, 59, 60, 91, 146, 46, 77, 218, 66, 200, 61, 241, 70, 55, 39, 227, 42, 2, 231, 235, 122, 135, 152, 137, 173, 232, 101, 75, 233, 21, 252, 15, 133, 111, 205, 57, 132, 187, 96, 49, 124, 86, 19, 188, 80, 213, 106, 214, 203, 177, 56, 104, 82, 110, 196, 113, 155, 170, 150, 117, 26, 140, 144, 11, 172, 67, 209, 125, 54, 58, 128, 204, 186, 199, 189, 208, 239, 143, 249, 246, 1, 139, 33, 87, 64, 116, 84, 254, 126, 202, 148, 76, 247, 115, 109, 3, 238, 114, 156, 195, 163, 159, 52, 36, 245, 240, 63, 153, 166, 167, 175, 9, 151, 171, 216, 207, 179, 72, 176, 48, 178, 157, 20, 181, 149, 53, 184, 4, 136, 165, 217, 50, 190, 191, 192, 193, 98, 215, 62, 112, 38, 90, 123, 105, 94, 221, 99, 201, 206, 251, 14, 211, 220, 131, 212, 130, 134, 253, 120, 145, 18, 219, 79, 129, 12, 93, 5, 183, 107, 71, 226, 180, 24, 234, 7, 108, 174, 6, 45, 29, 32, 168, 230, 197, 41, 25, 255, 164, 27, 210, 248, 97, 250, 22, 242, 243, 30, 69]
flag=""
for i in range(25):
flag+=chr(tb2.index(tb1[i])^tb0[i])
print(flag)

得到flag

flag{th15_15_v3r7_c0mm3n}

divination[TO DO]

先用模拟器跑一下,发现是一个输入字符串判断的逻辑。

因为物理机里装了WSL2,所以用的是能兼容Hyper-V的BlueStacks模拟器。

走流程,先用pxb1988/dex2jar: Tools to work with android .dex and java .class files这个工具把将apk解压后文件夹中的classes.dex文件转换成jar文件,再用Java Decompiler中的JD-GUI查看反编译出的java源代码。

然后可以看到MainActivity.class这里也有一句“大事不妙”,找到主函数。

看了一下逻辑就是调用libdivination.so(在apk解压后的路径divination/lib/arm64-v8a/libdivination.so),只要返回值为真就行。

所以其实做安卓逆向看so硬逆也能逆出来hhh。

于是现在转去逆so。

关键入口函数显而易见,就是这个跟包同名的函数。

为了方便看反编译代码,按y把a1-a3三个参数的变量类型进行更改(ida无法自动识别。

下边的函数也一目了然。

从后往前看,先拿到目标数组unk_3000:


[12.23] 鲲鹏计算专场

mips

真·送分题,可惜当时要上课没来得及抢一血(下午2点放题绝了

老传统走迷宫

mips架构。

ida反编译以后可以看到

v4是我们输入的字符串,很明显是迷宫逻辑,上下左右用wasd走,迷宫存在dword_100111F0里。

sub_10000744()这个初始函数是用来找起点用的(就是迷宫中3所在的地方,在后面可以看到3其实表示的是当前位置)。

这里也可以看到应该有多个迷宫(dword_10011D10是用来表示第几个迷宫的,且<=2,一个迷宫有225个数)+一个迷宫宽为15=三个迷宫,每个迷宫为15*15。

然后就是下面的四个函数,随便挑一个出来(比如sub_10000D28())可以看到

很明显是个往右走的函数,3表示当前位置,并把上一个当前位置标为1(可走路径)。并且可以看到终点是4,就是说我们要把每个迷宫从3走到4。

dump迷宫数组,写脚本打印迷宫:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
aMap=[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 3, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0]
for i in range(45):
for j in range(15):
if aMap[i*15+j]==0:
tmp='*'
elif aMap[i*15+j]==1:
tmp='.'
elif aMap[i*15+j]==3:
tmp='@'
else:
tmp='#'
print(tmp,end='')
print()
if i==14 or i==29:
print()

可以看到打印出了三个迷宫,为了看得清楚所以选用几个特定字符打印。

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
.....**********
.....*@*.******
.....*.*.******
.....*.*.******
.....*.*.....**
.....*.*****.**
.....*.*****.**
.....*.*****..*
.....*........*
.....********#*
...............
...............
...............
...............
...............
#sssssssddddddds

..*************
..*@*....******
..*.****.******
..*.****.******
..*..***.....**
..*..*******.**
..*..*******.**
..*..*****....*
..*..*****.**.*
..*..*****.****
..*......*.*..*
..*...........*
..***********#*
...............
...............
#ssssssssssdddddddddds

***************
*@..***********
***.*...*******
***...*.*******
****.**.*******
*..*.**.*******
**...**.*******
*******.*******
*******....****
**********.****
**********.****
**********.****
**********....*
*************.*
*************#*
#ddssddwddssssssdddssssdddss

走迷宫,然后把路径拼起来,根据提示转md5,get flag。

(有个疑惑哈,第二个迷宫理论上说就算是最短路也有多解?是题目出锅了还是我哪里看漏了= =

(再补一句,题目似乎甚至没要求最短路???神奇.jpg

1
2
3
import hashlib
s=b"sssssssdddddddsssssssssssddddddddddsddssddwddssssssdddssssdddss"
print("flag{%s}"%hashlib.md5(s).hexdigest())

flag{999ea6aa6c365ab43eec2a0f0e5968d5}

pypy

把题目文件拖进ida,搜索字符串能看到

猜测是pyinstaller打包的文件。

也就是这个题让我突然发现pyinstaller还能打包成elf的,于是比赛结束以后赶紧把之前总结的解包指南更新了:RE套路 - 关于pyinstaller打包文件的复原 | c10udlnk_Log

走流程解包,得到python源码。

看到这种混淆变量名,果断替换成ida style变量名(。

放一下源码:

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
# uncompyle6 version 3.7.4
# Python bytecode 3.8 (3413)
# Decompiled from: Python 2.7.18 (v2.7.18:8d21aa21f2, Apr 20 2020, 13:25:05) [MSC v.1500 64 bit (AMD64)]
# Warning: this version of Python has problems handling the Python 3 "byte" type in constants properly.

# Embedded file name: main.py
# Compiled at: 1995-09-28 00:18:56
# Size of source mod 2**32: 257 bytes
import random, codecs, sys, time, pygame
from pygame.locals import *
from collections import deque
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 480
SIZE = 20
LINE_WIDTH = 1
flag = 'flag{this is a fake flag}'
SCOPE_X = (0, SCREEN_WIDTH // SIZE - 1)
SCOPE_Y = (2, SCREEN_HEIGHT // SIZE - 1)
FOOD_STYLE_LIST = [(10, (255, 100, 100)), (20, (100, 255, 100)), (30, (100, 100, 255))]
LIGHT = (100, 100, 100)
DARK = (200, 200, 200)
BLACK = (0, 0, 0)
RED = (200, 30, 30)
BGCOLOR = (40, 40, 60)

def print_text(v1, v2, v3, v4, v5, fcolor=(255, 255, 255)):
v6 = v2.render(v5, True, fcolor)
v1.blit(v6, (v3, v4))


def init_snake():
v7 = deque()
v7.append((2, SCOPE_Y[0]))
v7.append((1, SCOPE_Y[0]))
v7.append((0, SCOPE_Y[0]))
return v7


def create_food(v8):
v9 = random.randint(SCOPE_X[0], SCOPE_X[1])
v10 = random.randint(SCOPE_Y[0], SCOPE_Y[1])
while (v9, v10) in v8:
v9 = random.randint(SCOPE_X[0], SCOPE_X[1])
v10 = random.randint(SCOPE_Y[0], SCOPE_Y[1])

return (
v9, v10)


def get_food_style():
return FOOD_STYLE_LIST[random.randint(0, 2)]


DEFAULT_KEY = u'Y\xf3\x02\xc3%\x9a\x820\x0b\xbb%\x7f~;\xd2\xdc'

def rc4(v11, key=DEFAULT_KEY, skip=1024):
v12 = 0
v13 = bytearray([v14 for v14 in range(256)])
v12 = 0
for v15 in range(256):
v12 = (v12 + v13[v15] + ord(key[(v15 % len(key))])) % 256
v16 = v13[v15]
v17 = v13[v12]
v13[v15] = v13[v12]
v13[v12] = v16
else:
v12 = 0
v18 = 0
v19 = []
if skip > 0:
for v15 in range(skip):
v12 = (v12 + 1) % 256
v18 = (v18 + v13[v12]) % 256
v13[v12], v13[v18] = v13[v18], v13[v12]

for v20 in v11:
v12 = (v12 + 1) % 256
v18 = (v18 + v13[v12]) % 256
v13[v12], v13[v18] = v13[v18], v13[v12]
v21 = v13[((v13[v12] + v13[v18]) % 256)]
v19.append(chr(ord(v20) ^ v21))
else:
return ''.join(v19)


def func(v22):
v23 = rc4(v22)
if v23.encode('utf-8').hex() == '275b39c381c28b701ac3972338456022c2ba06c3b04f5501471c47c38ac380c29b72c3b5c38a7ec2a5c2a0':
return 'YOU WIN'
return 'YOU LOSE'


def main():
pygame.init()
v24 = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption(u'\u8d2a\u5403\u86c7')
v25 = pygame.font.SysFont('SimHei', 24)
v26 = pygame.font.Font(None, 72)
v27, v28 = v26.size('GAME OVER')
v29 = True
v30 = init_snake()
v31 = create_food(v30)
v32 = get_food_style()
v33 = (1, 0)
v34 = True
v35 = False
v36 = 0
v37 = 0.5
v38 = v37
v39 = None
v41 = False
for v40 in pygame.event.get():
if v40.type == QUIT:
sys.exit()
elif v40.type == KEYDOWN:
if v40.key == K_RETURN:
if v34:
v35 = True
v34 = False
v29 = True
v30 = init_snake()
v31 = create_food(v30)
v32 = get_food_style()
v33 = (1, 0)
v36 = 0
v39 = time.time()
elif v40.key == K_SPACE:
if not v34:
v41 = not v41
elif v40.key in (K_w, K_UP):
if v29:
v33 = v33[1] or (0, -1)
v29 = False
elif v40.key in (K_s, K_DOWN):
if v29:
v33 = v33[1] or (0, 1)
v29 = False
elif v40.key in (K_a, K_LEFT):
if v29:
if not v33[0]:
v33 = (-1, 0)
v29 = False
elif v40.key in (K_d, K_RIGHT):
if v29:
if not v33[0]:
v33 = (1, 0)
v29 = False
else:
v24.fill(BGCOLOR)
for v42 in range(SIZE, SCREEN_WIDTH, SIZE):
pygame.draw.line(v24, BLACK, (v42, SCOPE_Y[0] * SIZE), (v42, SCREEN_HEIGHT), LINE_WIDTH)
else:
for v43 in range(SCOPE_Y[0] * SIZE, SCREEN_HEIGHT, SIZE):
pygame.draw.line(v24, BLACK, (0, v43), (SCREEN_WIDTH, v43), LINE_WIDTH)
else:
v44 = v34 or time.time()

if v44 - v39 > v38 and not v41:
v29 = True
v39 = v44
v45 = (v30[0][0] + v33[0], v30[0][1] + v33[1])
if v45 == v31:
v30.appendleft(v45)
v36 += v32[0]
v38 = v37 - 0.03 * (v36 // 100)
v31 = create_food(v30)
v32 = get_food_style()
else:
if SCOPE_X[0] <= v45[0] <= SCOPE_X[1]:
if SCOPE_Y[0] <= v45[1] <= SCOPE_Y[1]:
if v45 not in v30:
v30.appendleft(v45)
v30.pop()
else:
v34 = True
if not v34:
pygame.draw.rect(v24, v32[1], (v31[0] * SIZE, v31[1] * SIZE, SIZE, SIZE), 0)
for v46 in v30:
pygame.draw.rect(v24, DARK, (v46[0] * SIZE + LINE_WIDTH, v46[1] * SIZE + LINE_WIDTH, SIZE - LINE_WIDTH * 2, SIZE - LINE_WIDTH * 2), 0)
else:
print_text(v24, v25, 30, 7, f"speed: {v36 // 100}")
print_text(v24, v25, 450, 7, f"score: {v36}")
if v36 >= 5192296858534827628530496329220096:
v47 = flag
print_text(v24, v26, (SCREEN_WIDTH - v27) // 2, (SCREEN_HEIGHT - v28) // 2, func(v47), RED)
if v34:
if v35:
print_text(v24, v26, (SCREEN_WIDTH - v27) // 2, (SCREEN_HEIGHT - v28) // 2, 'GAME OVER', RED)
pygame.display.update()


if __name__ == '__main__':
main()
# okay decompiling main.pyc

可以看到最后getflag这里(func())的程序逻辑就一个rc4加密,由rc4的特性可知加密和解密流程相同,故复用程序中的rc4()来得到flag。

uncompyle反编译出来的源码是python3,但是题目本身的源码是python2,注意编码问题。

关于编码问题,可以看:

Unicode之痛 — PyCoder’s Weelky CN

关于python2中的unicode和str以及python3中的str和bytes - 明王不动心 - 博客园

这里因为反编译做了转换成python3的处理,所以脚本用python3写。

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
DEFAULT_KEY = u'Y\xf3\x02\xc3%\x9a\x820\x0b\xbb%\x7f~;\xd2\xdc'
def rc4(v11, key=DEFAULT_KEY, skip=1024):
v12 = 0
v13 = bytearray([v14 for v14 in range(256)])
v12 = 0
for v15 in range(256):
v12 = (v12 + v13[v15] + ord(key[(v15 % len(key))])) % 256
v16 = v13[v15]
v17 = v13[v12]
v13[v15] = v13[v12]
v13[v12] = v16
else:
v12 = 0
v18 = 0
v19 = []
if skip > 0:
for v15 in range(skip):
v12 = (v12 + 1) % 256
v18 = (v18 + v13[v12]) % 256
v13[v12], v13[v18] = v13[v18], v13[v12]

for v20 in v11:
v12 = (v12 + 1) % 256
v18 = (v18 + v13[v12]) % 256
v13[v12], v13[v18] = v13[v18], v13[v12]
v21 = v13[((v13[v12] + v13[v18]) % 256)]
v19.append(chr(ord(v20) ^ v21))
else:
return ''.join(v19)
# def func(v22):
# v23 = rc4(v22)
# if v23.encode('utf-8').hex() == '275b39c381c28b701ac3972338456022c2ba06c3b04f5501471c47c38ac380c29b72c3b5c38a7ec2a5c2a0':
# return 'YOU WIN'
# return 'YOU LOSE'

# -=-=-=以上所有为源码中原函数-=-=-=

cipher='275b39c381c28b701ac3972338456022c2ba06c3b04f5501471c47c38ac380c29b72c3b5c38a7ec2a5c2a0'
flag=bytes.fromhex(cipher).decode('utf-8')
print(rc4(flag))

flag{snake_bao_is_really_lucky}

print

官方wp说的是给了一个brainfuck引擎,跟我赛中攻击格式化字符串漏洞的思路完全不一样(。),一开始还以为我想得太偏了XD,然而在复现的时候发现这个brainfuck解释器的原理就是攻击格式化字符串漏洞,学到了学到了。

以下试图从头开始复现。

通过在DuckDuckGo上搜索setup()中的特殊字符串"%1$.*1$d %2$hn"查到一个github项目HexHive/printbf: Brainfuck interpreter inside printf,发现其余的字符串与题目中的十分相似,并且pbf_pre.c跟程序逻辑几乎一样,推测是题目的来源。

所以先通过动态调试,把断点设在前面所有的赋值语句之后,用idapython的get_bytes(0x5577FCB50000,16000)提取出progn的内容。

通过printbf项目中的pbf_pre.c源码可以知道progn是int *,故将提取出来的byte对象进行处理,然后按照token.py的逻辑逆向写出由progn反向生成的brainfuck程序。

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
# token.py from "HexHive/printbf: Brainfuck interpreter inside printf"
#!/usr/bin/python
# -*- coding: utf-8 -*-

import itertools
import sys
from argparse import ArgumentParser

__author__ = "Nicholas Carlini <npc@berkeley.edu> and Mathias Payer <mathias.payer@nebelwelt.net>"
__description__ = "Script to tokenize a BF script into a printf interpreter."
__version__ = filter(str.isdigit, "$Revision: 1 $")

parser = ArgumentParser(description=__description__)
parser.add_argument('-v', '--version', action='version', version='%(prog)s {:s}'.format(__version__))
parser.add_argument('-t', '--template', type=str, metavar='template filename', help='Filename for the template to use.', required=False, default='bf_pre.c')
parser.add_argument('-bf', '--brainfuck', type=str, metavar='bf file', help='BF program', required=True)
parser.add_argument('-i', '--input', type=str, metavar='input to bf', help='BF input', required=False, default='')
args = parser.parse_args()

prog = open(args.brainfuck).read()
prog = "".join([x for x in prog if x in "<>+-.,[]"])
prog = prog.replace("[-]", "Z")

remap = {'<':[1],
'>':[2],
'+':[3],
'-':[4],
'.':[5,6],
',':[7,8],
'[':[9,10,11],
']':[12],
'Z':[13],
'F':[14],
'B':[15],#,16,17],
'A':[18],
}

newprog = []
progiter = iter(prog)
while len(prog):
e = prog[0]
if e in '<>+-':
res = itertools.takewhile(lambda x: x == e, prog)
count = len(list(res))
if e == '>':
newprog.append(('F',count))
elif e == '<':
assert count < 256
newprog.append(('B',65536-count))
elif e == '+':
newprog.append(('A',count))
elif e == '-':
newprog.append(('A',256-count))
prog = prog[count:]
else:
newprog.append(e)
prog = prog[1:]

stack = []
index = 2

txt = open(args.template).read()
txt = txt.replace('###INPUT###', args.input)
print txt[0:txt.find('###TOKER###')],

for e in newprog:
count = 0
if type(e) == type(tuple()):
count = e[1]
e = e[0]
for i,insn in enumerate(remap[e]):
print ' progn[%d] = %d;'%(index+i*2,insn)

if count != 0:
assert i == 0
print ' progn[%d] = %d;'%(index+1,count)

if e == '[': # this is a cgoto
stack.append(index)
elif e == ']':
backto = stack.pop()
print ' progn[%d] = %d;'%(backto+1,index*4-4)
print ' progn[%d] = %d;'%(backto+3,index*4-4)
print ' progn[%d] = %d;'%(backto+5,index*4-4)

print ' progn[%d] = %d;'%(index+1,(backto-2)*4) # we always increment PC by 1

index += 2*len(remap[e])

print txt[txt.find('###TOKER###')+11:],

可以看到实际上就是从progn[2*k]这里看到brainfuck的每一步操作符,而对于"±><"这四种操作符的数量由progn[2*k+1]给出,故从这些位置即可提取出bf程序的操作符,得到bf程序:

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
p=b'' # 这里是动态调试提取出的progn字节对象,太长了这里不贴
progn=[]
for i in range(0,16000,4):
progn.append(int.from_bytes(p[i:i+4],byteorder='little'))
# 转化成printbf中的int* progn
i=2
bfCode=""
while i<len(progn) and progn[i]!=0:
if progn[i]==5:
bfCode+='.'
i+=4
elif progn[i]==7:
bfCode+=','
i+=4
elif progn[i]==9:
bfCode+='['
i+=6
elif progn[i]==12:
bfCode+=']'
i+=2
elif progn[i]==13:
bfCode+='[-]'
i+=2
elif progn[i]==14:
bfCode+='>'*progn[i+1]
i+=2
elif progn[i]==15:
bfCode+='<'*(65536-progn[i+1])
i+=2
elif progn[i]==18:
# 其实这里+和-没法准确区分,且对于无符号char型数据来说-n和+(256-n)得到的结果是一样的,这里选择字符数更少的操作符
if progn[i+1]>0x7F:
bfCode+='-'*(256-progn[i+1])
else:
bfCode+='+'*progn[i+1]
i+=2
else:
print("[-] UknCode "+str(progn[i])+" in progn["+str(i)+"]")
i+=2
print(bfCode)

得到:

1
>,>,>,>,>,>,<<<<<[->>>>>>+<<<<<<<+>]<[->+<]>[->>>>>>+<<<<<<<+>]<[->+<]>>[->>>>>>+<<<<<<<<+>>]<<[->>+<<]>>[->>>>>>+<<<<<<<<+>>]<<[->>+<<]>>>[->>>>>>+<<<<<<<<<+>>>]<<<[->>>+<<<]>>>[->>>>>>+<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>[->>>>>>+<<<<<<<<<<+>>>>]<<<<[->>>>+<<<<]>>>>[->>>>>>+<<<<<<<<<<+>>>>]<<<<[->>>>+<<<<]>>>>>[->>>>>>+<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>[->>>>>>+<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>>[->>>>>>+<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>>>>>[->>>>>>+<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>>>>>>[->>>>>>+<<<<<<<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<<]>[->>>>>>>>>>>>+<<<<<<<<<<<<<+>]<[->+<]>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+<<<<<<<<]>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<+>>]<<[->>+<<]>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<+>>>>>>>>>]<<<<<<<<<[->>>>>>>>>+<<<<<<<<<]>>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<+>>>>>>>>>>]<<<<<<<<<<[->>>>>>>>>>+<<<<<<<<<<]>>>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<<<+>>>>]<<<<[->>>>+<<<<]>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<+>>>>>>>>>>>]<<<<<<<<<<<[->>>>>>>>>>>+<<<<<<<<<<<]>>>>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>]<<<<<<<<<<<<[->>>>>>>>>>>>+<<<<<<<<<<<<]>>>>>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>]<<<<<<<<<<<<<[->>>>>>>>>>>>>+<<<<<<<<<<<<<]>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<+>]<[->+<]>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>]<<<<<<<<<<<<<<[->>>>>>>>>>>>>>+<<<<<<<<<<<<<<]>>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<+>>]<<[->>+<<]>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<]>>>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<]>>>>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<+>>>>]<<<<[->>>>+<<<<]>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<]>>>>>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<]>>>>>>[->>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<]>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<+>]<[->+<]>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<]>>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<+>>]<<[->>+<<]>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<]>>>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<]>>>>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>]<<<<[->>>>+<<<<]>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<]>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>]<<[->>+<<]>>>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>]<<<<<<<<<[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<]>>>>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>]<<<<<<<<<<[->>>>>>>>>>+<<<<<<<<<<]>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>]<<<<<[->>>>>+<<<<<]>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>]<<<<<<<<<<<<<[->>>>>>>>>>>>>+<<<<<<<<<<<<<]>>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+<<<<<<<<]>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>>>>>>>>>>>>>>>>>>>[->>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<<]>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>]<<[->>+<<]>>>>>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<]>>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>]<<<<<<<<[->>>>>>>>+<<<<<<<<]>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>]<<<<<<[->>>>>>+<<<<<<]>>>>>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>]<<<<<<<[->>>>>>>+<<<<<<<]>>>>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<]>>>[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>]<<<[->>>+<<<]>>>>>>>>>>>>>>>>>[->>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]---------------------------->[-]+++++>[-]-------------------------------------------------------------------------------------------------->[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<<<<<<<<<<<[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[-]++++++++++++++++++++++++++++++++++++++++++++++++<<<<<<[>>>>>>+<<<<<<[-]]>[>>>>>+<<<<<[-]]>[>>>>+<<<<[-]]>[>>>+<<<[-]]>[>>+<<[-]]>[>+<[-]]>>,>,>,>,>,>,>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++>[-]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++>[-]+++++++++++++++++++++++++++++++++++++++++++++++++++<<<<<<<<<<<[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[->>>>>>-<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<+>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<[->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>[<<<<<<<+>>>>>>>[-]]>[<<<<<<<<+>>>>>>>>[-]]>[<<<<<<<<<+>>>>>>>>>[-]]>[<<<<<<<<<<+>>>>>>>>>>[-]]>[<<<<<<<<<<<+>>>>>>>>>>>[-]]>[<<<<<<<<<<<<+>>>>>>>>>>>>[-]]<<<<<<<<<<<<.

进一步的,为了方便甚至可以直接转化成c程序,最后的处理脚本为:

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
p=b'' #依旧是太长不贴(
progn=[]
for i in range(0,16000,4):
progn.append(int.from_bytes(p[i:i+4],byteorder='little'))
# 转化成printbf中的int* progn
i=2
indent=0
ptr=0
print(' '*indent+"#include <stdio.h>")
print(' '*indent+"int main(){")
indent+=4
print(' '*indent+"char mem[1000000]={0};")
while i<len(progn) and progn[i]!=0:
if progn[i]==5:
print(' '*indent+"printf(\"%c\",mem["+str(ptr)+"]);")
i+=4
elif progn[i]==7:
print(' '*indent+"scanf(\"%c\",&mem["+str(ptr)+"]);")
i+=4
elif progn[i]==9:
print(' '*indent+"while(mem["+str(ptr)+"]){")
indent+=4
i+=6
elif progn[i]==12:
i+=2
indent-=4
print(' '*indent+"}")
elif progn[i]==13:
print(' '*indent+"mem["+str(ptr)+"]=0;")
i+=2
elif progn[i]==14:
ptr+=progn[i+1]
i+=2
elif progn[i]==15:
ptr-=(65536-progn[i+1])
i+=2
elif progn[i]==18:
# 其实这里+和-没法准确区分,且对于无符号char型数据来说-n和+(256-n)得到的结果是一样的,这里选择字符数更少的操作符
if progn[i+1]>0x7F:
print(' '*indent+"mem["+str(ptr)+"]-="+str(256-progn[i+1])+";")
else:
print(' '*indent+"mem["+str(ptr)+"]+="+str(progn[i+1])+";")
i+=2
else:
print("[-] UknCode "+str(progn[i])+" in progn["+str(i)+"]")
i+=2
print(' '*indent+"return 0;")
indent-=4
print(' '*indent+"}")
# 直接转化成C程序

得到C程序:

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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
#include <stdio.h>
int main(){
char mem[1000000]={0};
scanf("%c",&mem[1]);
scanf("%c",&mem[2]);
scanf("%c",&mem[3]);
scanf("%c",&mem[4]);
scanf("%c",&mem[5]);
scanf("%c",&mem[6]);
while(mem[1]){
mem[1]-=1;
mem[7]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[1]+=1;
}
while(mem[1]){
mem[1]-=1;
mem[7]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[1]+=1;
}
while(mem[2]){
mem[2]-=1;
mem[8]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[2]+=1;
}
while(mem[2]){
mem[2]-=1;
mem[8]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[2]+=1;
}
while(mem[3]){
mem[3]-=1;
mem[9]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[3]+=1;
}
while(mem[3]){
mem[3]-=1;
mem[9]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[3]+=1;
}
while(mem[4]){
mem[4]-=1;
mem[10]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[4]+=1;
}
while(mem[4]){
mem[4]-=1;
mem[10]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[4]+=1;
}
while(mem[5]){
mem[5]-=1;
mem[11]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[5]+=1;
}
while(mem[5]){
mem[5]-=1;
mem[11]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[5]+=1;
}
while(mem[6]){
mem[6]-=1;
mem[12]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[6]+=1;
}
while(mem[6]){
mem[6]-=1;
mem[12]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[6]+=1;
}
while(mem[7]){
mem[7]-=1;
mem[13]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[7]+=1;
}
while(mem[1]){
mem[1]-=1;
mem[13]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[1]+=1;
}
while(mem[8]){
mem[8]-=1;
mem[14]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[8]+=1;
}
while(mem[2]){
mem[2]-=1;
mem[14]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[2]+=1;
}
while(mem[9]){
mem[9]-=1;
mem[15]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[9]+=1;
}
while(mem[3]){
mem[3]-=1;
mem[15]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[3]+=1;
}
while(mem[10]){
mem[10]-=1;
mem[16]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[10]+=1;
}
while(mem[4]){
mem[4]-=1;
mem[16]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[4]+=1;
}
while(mem[11]){
mem[11]-=1;
mem[17]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[11]+=1;
}
while(mem[5]){
mem[5]-=1;
mem[17]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[5]+=1;
}
while(mem[12]){
mem[12]-=1;
mem[18]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[12]+=1;
}
while(mem[6]){
mem[6]-=1;
mem[18]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[6]+=1;
}
while(mem[13]){
mem[13]-=1;
mem[19]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[13]+=1;
}
while(mem[1]){
mem[1]-=1;
mem[19]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[1]+=1;
}
while(mem[14]){
mem[14]-=1;
mem[20]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[14]+=1;
}
while(mem[2]){
mem[2]-=1;
mem[20]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[2]+=1;
}
while(mem[15]){
mem[15]-=1;
mem[21]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[15]+=1;
}
while(mem[3]){
mem[3]-=1;
mem[21]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[3]+=1;
}
while(mem[16]){
mem[16]-=1;
mem[22]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[16]+=1;
}
while(mem[4]){
mem[4]-=1;
mem[22]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[4]+=1;
}
while(mem[17]){
mem[17]-=1;
mem[23]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[17]+=1;
}
while(mem[5]){
mem[5]-=1;
mem[23]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[5]+=1;
}
while(mem[18]){
mem[18]-=1;
mem[24]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[18]+=1;
}
while(mem[6]){
mem[6]-=1;
mem[24]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[6]+=1;
}
while(mem[19]){
mem[19]-=1;
mem[25]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[19]+=1;
}
while(mem[1]){
mem[1]-=1;
mem[25]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[1]+=1;
}
while(mem[20]){
mem[20]-=1;
mem[26]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[20]+=1;
}
while(mem[2]){
mem[2]-=1;
mem[26]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[2]+=1;
}
while(mem[21]){
mem[21]-=1;
mem[27]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[21]+=1;
}
while(mem[3]){
mem[3]-=1;
mem[27]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[3]+=1;
}
while(mem[22]){
mem[22]-=1;
mem[28]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[22]+=1;
}
while(mem[4]){
mem[4]-=1;
mem[28]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[4]+=1;
}
while(mem[23]){
mem[23]-=1;
mem[29]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[23]+=1;
}
while(mem[5]){
mem[5]-=1;
mem[29]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[5]+=1;
}
while(mem[24]){
mem[24]-=1;
mem[30]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[24]+=1;
}
while(mem[6]){
mem[6]-=1;
mem[30]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[6]+=1;
}
while(mem[2]){
mem[2]-=1;
mem[31]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[2]+=1;
}
while(mem[9]){
mem[9]-=1;
mem[31]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[9]+=1;
}
while(mem[16]){
mem[16]-=1;
mem[31]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[16]+=1;
}
while(mem[23]){
mem[23]-=1;
mem[31]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[23]+=1;
}
while(mem[30]){
mem[30]-=1;
mem[31]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[30]+=1;
}
while(mem[25]){
mem[25]-=1;
mem[32]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[25]+=1;
}
while(mem[20]){
mem[20]-=1;
mem[32]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[20]+=1;
}
while(mem[15]){
mem[15]-=1;
mem[32]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[15]+=1;
}
while(mem[10]){
mem[10]-=1;
mem[32]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[10]+=1;
}
while(mem[5]){
mem[5]-=1;
mem[32]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[5]+=1;
}
while(mem[13]){
mem[13]-=1;
mem[33]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[13]+=1;
}
while(mem[8]){
mem[8]-=1;
mem[33]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[8]+=1;
}
while(mem[3]){
mem[3]-=1;
mem[33]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[3]+=1;
}
while(mem[22]){
mem[22]-=1;
mem[33]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[22]+=1;
}
while(mem[30]){
mem[30]-=1;
mem[33]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[30]+=1;
}
while(mem[7]){
mem[7]-=1;
mem[34]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[7]+=1;
}
while(mem[2]){
mem[2]-=1;
mem[34]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[2]+=1;
}
while(mem[21]){
mem[21]-=1;
mem[34]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[21]+=1;
}
while(mem[28]){
mem[28]-=1;
mem[34]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[28]+=1;
}
while(mem[17]){
mem[17]-=1;
mem[34]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[17]+=1;
}
while(mem[8]){
mem[8]-=1;
mem[35]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[8]+=1;
}
while(mem[15]){
mem[15]-=1;
mem[35]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[15]+=1;
}
while(mem[22]){
mem[22]-=1;
mem[35]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[22]+=1;
}
while(mem[29]){
mem[29]-=1;
mem[35]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[29]+=1;
}
while(mem[6]){
mem[6]-=1;
mem[35]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[6]+=1;
}
while(mem[7]){
mem[7]-=1;
mem[36]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[7]+=1;
}
while(mem[20]){
mem[20]-=1;
mem[36]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[20]+=1;
}
while(mem[3]){
mem[3]-=1;
mem[36]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[3]+=1;
}
while(mem[17]){
mem[17]-=1;
mem[36]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[17]+=1;
}
while(mem[30]){
mem[30]-=1;
mem[36]+=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[30]+=1;
}
mem[37]=0;
mem[37]+=53;
mem[38]=0;
mem[38]-=28;
mem[39]=0;
mem[39]+=5;
mem[40]=0;
mem[40]-=98;
mem[41]=0;
mem[41]+=73;
mem[42]=0;
mem[42]+=123;
while(mem[31]){
mem[31]-=1;
mem[37]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[31]+=1;
}
while(mem[32]){
mem[32]-=1;
mem[38]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[32]+=1;
}
while(mem[33]){
mem[33]-=1;
mem[39]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[33]+=1;
}
while(mem[34]){
mem[34]-=1;
mem[40]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[34]+=1;
}
while(mem[35]){
mem[35]-=1;
mem[41]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[35]+=1;
}
while(mem[36]){
mem[36]-=1;
mem[42]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[36]+=1;
}
mem[43]=0;
mem[43]+=48;
while(mem[37]){
mem[43]+=1;
mem[37]=0;
}
while(mem[38]){
mem[43]+=1;
mem[38]=0;
}
while(mem[39]){
mem[43]+=1;
mem[39]=0;
}
while(mem[40]){
mem[43]+=1;
mem[40]=0;
}
while(mem[41]){
mem[43]+=1;
mem[41]=0;
}
while(mem[42]){
mem[43]+=1;
mem[42]=0;
}
scanf("%c",&mem[44]);
scanf("%c",&mem[45]);
scanf("%c",&mem[46]);
scanf("%c",&mem[47]);
scanf("%c",&mem[48]);
scanf("%c",&mem[49]);
mem[50]=0;
mem[50]+=101;
mem[51]=0;
mem[51]+=95;
mem[52]=0;
mem[52]+=67;
mem[53]=0;
mem[53]+=48;
mem[54]=0;
mem[54]+=100;
mem[55]=0;
mem[55]+=51;
while(mem[44]){
mem[44]-=1;
mem[50]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[44]+=1;
}
while(mem[45]){
mem[45]-=1;
mem[51]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[45]+=1;
}
while(mem[46]){
mem[46]-=1;
mem[52]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[46]+=1;
}
while(mem[47]){
mem[47]-=1;
mem[53]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[47]+=1;
}
while(mem[48]){
mem[48]-=1;
mem[54]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[48]+=1;
}
while(mem[49]){
mem[49]-=1;
mem[55]-=1;
mem[0]+=1;
}
while(mem[0]){
mem[0]-=1;
mem[49]+=1;
}
while(mem[50]){
mem[43]+=1;
mem[50]=0;
}
while(mem[51]){
mem[43]+=1;
mem[51]=0;
}
while(mem[52]){
mem[43]+=1;
mem[52]=0;
}
while(mem[53]){
mem[43]+=1;
mem[53]=0;
}
while(mem[54]){
mem[43]+=1;
mem[54]=0;
}
while(mem[55]){
mem[43]+=1;
mem[55]=0;
}
printf("%c",mem[43]);
return 0;
}

于是现在的任务就成了分两次输入6个字符,经过处理后若mem[43]==48(原二进制文件中的判断逻辑)就能得到flag。

这个C程序还是比较冗杂(毕竟九百多行),所以对其进行找规律分析化简。

先拿出前四个while即[line 10, line 27]进行分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
while(mem[1]){
mem[1]-=1;
mem[7]+=1;
mem[0]+=1;
} // mem[7]+=mem[1];mem[0]+=mem[1];mem[1]=0;
while(mem[0]){
mem[0]-=1;
mem[1]+=1;
} // mem[1]+=mem[0];mem[0]=0;
while(mem[1]){
mem[1]-=1;
mem[7]+=1;
mem[0]+=1;
} // mem[7]+=mem[1];mem[0]+=mem[1];mem[1]=0;
while(mem[0]){
mem[0]-=1;
mem[1]+=1;
} // mem[1]+=mem[0];mem[0]=0;

也就是说整段等同于:

1
2
mem[7]+=mem[1]*2;
// mem[0]和mem[1]没变

所以接下来的(6-1)*4个循环也做相同处理,即[line 10, line 117]可以简化为:

1
2
3
4
5
6
mem[7]+=mem[1]*2;
mem[8]+=mem[2]*2;
mem[9]+=mem[3]*2;
mem[10]+=mem[4]*2;
mem[11]+=mem[5]*2;
mem[12]+=mem[6]*2;

然后再拿出四个while即[line 118, line 135]进行分析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
while(mem[7]){
mem[7]-=1;
mem[13]+=1;
mem[0]+=1;
} // mem[13]+=mem[7];mem[0]+=mem[7];mem[7]=0;
while(mem[0]){
mem[0]-=1;
mem[7]+=1;
} // mem[7]+=mem[0];mem[0]=0;
while(mem[1]){
mem[1]-=1;
mem[13]+=1;
mem[0]+=1;
} // mem[13]+=mem[1];mem[0]+=mem[1];mem[1]=0;
while(mem[0]){
mem[0]-=1;
mem[1]+=1;
} // mem[1]+=mem[0];mem[0]=0;

所以整段等同于:

1
mem[13]=mem[1]+mem[7];

所以[line 118, line 225]简化为:

1
2
3
4
5
6
mem[13]=mem[1]+mem[7];
mem[14]=mem[2]+mem[8];
mem[15]=mem[3]+mem[9];
mem[16]=mem[4]+mem[10];
mem[17]=mem[5]+mem[11];
mem[18]=mem[6]+mem[12];

最后[line 10, line 803]可简化为

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
mem[7]+=mem[1]*2;
mem[8]+=mem[2]*2;
mem[9]+=mem[3]*2;
mem[10]+=mem[4]*2;
mem[11]+=mem[5]*2;
mem[12]+=mem[6]*2;
// [line 10, line 117]
mem[13]=mem[1]+mem[7];
mem[14]=mem[2]+mem[8];
mem[15]=mem[3]+mem[9];
mem[16]=mem[4]+mem[10];
mem[17]=mem[5]+mem[11];
mem[18]=mem[6]+mem[12];
// [line 118, line 225]
mem[19]=mem[1]+mem[13];
mem[20]=mem[2]+mem[14];
mem[21]=mem[3]+mem[15];
mem[22]=mem[4]+mem[16];
mem[23]=mem[5]+mem[17];
mem[24]=mem[6]+mem[18];
// [line 226, line 333]
mem[25]=mem[1]+mem[19];
mem[26]=mem[2]+mem[20];
mem[27]=mem[3]+mem[21];
mem[28]=mem[4]+mem[22];
mem[29]=mem[5]+mem[23];
mem[30]=mem[6]+mem[24];
// [line 334, line 441]
mem[31]=mem[2]+mem[9]+mem[16]+mem[23]+mem[30];
mem[32]=mem[25]+mem[20]+mem[15]+mem[10]+mem[5];
mem[33]=mem[13]+mem[8]+mem[3]+mem[22]+mem[30];
mem[34]=mem[7]+mem[2]+mem[21]+mem[28]+mem[17];
mem[35]=mem[8]+mem[15]+mem[22]+mem[29]+mem[6];
mem[36]=mem[7]+mem[20]+mem[3]+mem[17]+mem[30];
// [line 442, line 711]
mem[37]=53;
mem[38]=-28;
mem[39]=5;
mem[40]=-98;
mem[41]=73;
mem[42]=123;
// [line 712, line 723]
mem[37]-=mem[31];
mem[38]-=mem[32];
mem[39]-=mem[33];
mem[40]-=mem[34];
mem[41]-=mem[35];
mem[42]-=mem[36];
// [line 724, line 777]
mem[43]=48;
// [line 778, line 779]
for(int i=37;i<=42;i++){
if(mem[i]) mem[43]+=1;
mem[i]=0;
}
// [line 780, line 803]

后半段输入后,处理的简化程序为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mem[50]=101;
mem[51]=95;
mem[52]=67;
mem[53]=48;
mem[54]=100;
mem[55]=51;
// [line 810, line 821]
mem[50]-=mem[44];
mem[51]-=mem[45];
mem[52]-=mem[46];
mem[53]-=mem[47];
mem[54]-=mem[48];
mem[55]-=mem[49];
// [line 822, line 875]
for(int i=50;i<=55;i++){
if(mem[i]) mem[43]+=1;
mem[i]=0;
}
// [line 876, line 899]

至此分析完成。

而mem[43]一开始赋值就是48,也就是说两个for循环都不能进到if里。令s[12]为所求字符串,则可以得出方程组:

1
2
3
4
5
6
7
8
9
10
11
12
s[1]+s[2]*2+s[3]*3+s[4]*4+s[5]*5==53;//mem[2]+mem[9]+mem[16]+mem[23]+mem[30]==53;
s[0]*5+s[1]*4+s[2]*3+s[3]*2+s[4]*1==-28;//mem[25]+mem[20]+mem[15]+mem[10]+mem[5]==-28;
s[0]*3+s[1]*2+s[2]*1+s[3]*4+s[5]*5==5;//mem[13]+mem[8]+mem[3]+mem[22]+mem[30]==5;
s[0]*2+s[1]*1+s[2]*4+s[3]*5+s[4]*3==-98;//mem[7]+mem[2]+mem[21]+mem[28]+mem[17]==-98;
s[1]*2+s[2]*3+s[3]*4+s[4]*5+s[5]*1==73;//mem[8]+mem[15]+mem[22]+mem[29]+mem[6]==73;
s[0]*2+s[1]*4+s[2]*1+s[4]*3+s[5]*5==123;//mem[7]+mem[20]+mem[3]+mem[17]+mem[30]==123;
s[6]==101;
s[7]==95;
s[8]==67;
s[9]==48;
s[10]==100;
s[11]==51;

因为涉及到char型数据解方程的问题,s[0]-s[5]不好直接出,爆破又太慢了,所以决定构造一个程序用angr破:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// useAngr.c
#include <stdio.h>
int main(){
char s[6]={0};
scanf("%6s",s);
char x1=s[1]+s[2]*2+s[3]*3+s[4]*4+s[5]*5;
char x2=s[0]*5+s[1]*4+s[2]*3+s[3]*2+s[4]*1;
char x3=s[0]*3+s[1]*2+s[2]*1+s[3]*4+s[5]*5;
char x4=s[0]*2+s[1]*1+s[2]*4+s[3]*5+s[4]*3;
char x5=s[1]*2+s[2]*3+s[3]*4+s[4]*5+s[5]*1;
char x6=s[0]*2+s[1]*4+s[2]*1+s[4]*3+s[5]*5;
if(x1==53&&x2==-28&&x3==5&&x4==-98&&x5==73&&x6==123)
printf("Congratulations!!!!!!");
return 0;
}

useAngr_exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import time
import angr
import sys

t=time.clock()
path_to_binary='./useAngr'
project=angr.Project(path_to_binary,auto_load_libs=False)
initial_state=project.factory.entry_state()
simulation=project.factory.simgr(initial_state)

find_address=0x4008CC
simulation.one_active.options.add(angr.options.LAZY_SOLVES)
simulation.explore(find=find_address)

print('time:',round(time.clock()-t,2),'s')
if simulation.found:
solution_state=simulation.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()))
else:
raise Exception('Could not find the solution. _0x0_')

解得:

有不可见字符,不过可以通过-0x80得到可见字符(估计是前面判定+和-的地方有点出入,问题不大)。

最后整理得到字符串:

1
2
3
4
5
6
7
8
9
key1=b'\xd23v\xe5\xd2\xd3'
for c in key1:
if c>0x7f:
print(chr(c-0x80),end='')
else:
print(chr(c),end='')
key2=[101,95,67,48,100,51]
for x in key2:
print(chr(x),end='')

由于要连远程拿flag,所以复现的时候只有本地打通没有flag,打通撒花~

R3veRSe_C0d3


[12.27] HarmonyOS和HMS专场

难得有一场ak逆向了!(虽然有大佬带着

有三道题都是卡着四血交,实惨TAT

re123

用file命令可以看到是MS Windows HtmlHelp Data文件(即.chm),查看文件头也可以知道。

所以添加后缀名.chm。

关于chm文件有一个常用的反编译器ChmDecompiler,可以释放CHM里面的全部源文件(包括网页、文本、图片、CHM、ZIP、EXE等全部源文件),并且完美地恢复源文件的全部目录结构 (摘抄的简介

所以用ChmDecompiler打开re.chm,解压缩,可以看到目录下出现一个包含四个文件的文件夹(其实源文件只有三个,.hhp是ChmDecompiler自动生成的)。

一个一个翻可以看到doc.htm里有一段奇怪的Item1。

大概可以看到是powershell的语法?(感觉像win后门,这么多no的参数

查了一下其实就是把后面那大段进行base64解码而已,用wsl解一下base64有

然后得到了一段.NET代码(白字)。

通过查微软文档可以知道,这里是把base64解码以后的字符进行Deflate解压的过程,所以用脚本把中间那段base64解码,并整理输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
import base64
import zlib

def deflate(data):
try:
return zlib.decompress(data, -zlib.MAX_WBITS)
except zlib.error:
return zlib.decompress(data)

code='TY5BC4IwGIbvgv9hjB2McJhEhNChJMGTkN2qg7qvFHQT/bL575vpoV2/53n2skJJBInkQG5xwqOqhkcQXCATx7q+gkaHsvYj7kIVvCgburItVgm9MTxbVB5LATp5OlQvb6IMV0LdQvdPpu+8x66SL2eOrMl+Ck7naUA69ggND5UcoEOzI+pUc8p62G3TRZubv34K6IbLespADoGR27vv+R7HpqXzt8Q9y0IJI5N8RLCtLw=='
de_code=deflate(base64.b64decode(code)).decode()
for x in de_code.split('\r\n'):
print(x)

很明显的逻辑了,把doc.chm(应该是原来的re.chm)中"xxxxxxxx"后面的部分提取出来,还是用base64解码得到文件。

把这后面的内容手动复制出来到cont.txt里,进行base64解码,最后存在theFile中。

1
base64 -d cont.txt > theFile

查看theFile可以猜测是exe(毕竟最开始给的就是有powershell指令的base64),把文件头补上,并改后缀名(即theFile.exe)。

用ida打开,通过FindCrypt插件可以看到AES,跟过去能看到AES加密时的S盒(其实这里前两个都是S盒,第三个是逆S盒),猜测用到了AES加密。

往上回溯找到主函数

显然,这里是AES加密过程,sub_180001100()是密钥拓展过程,sub_1800015B0()是AES加密。

关于逆向中各种常见密码的记录,指路:对称加密算法&&Hash算法 文档 | feng’s blog

看了一下感觉是原装无魔改的AES,密文密钥都给了,那就直接写脚本解密。

注意这里是以整数形式给出的,别忘了小端序。

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Cipher import AES
from binascii import *

arr=[0x16157E2B,0xA6D2AE28,0x8815F7AB,0x3C4FCF09]
key=""
for i in range(4):
key=hex(arr[i])[2:]+key
key=unhexlify(key)[::-1] #注意小端序的问题
tmp=0x46C42084AA2A1B56E799D643453FF4B5
cipher=unhexlify(hex(tmp)[2:])[::-1]
enc=AES.new(key,AES.MODE_ECB)
print(enc.decrypt(cipher))

flag{youcangues}

puzzle

mips架构。

加载进ida以后,通过字符串回溯找到主函数。

可以看到很明显的sub_401134()这个check,先往这里面看。

看到是一个疑似maze的逻辑(

不过sub_400FA8()点进去以后可以看到是swap的功能

所以应该不是maze,是一个以交换为主的逻辑。

至于dword_4A0010,可以看到是一个九个数的数组。

v4和v5的出处在switch逻辑上面一点

可以看到最后(v4,v5)其实表示了数组里0的位置,且数组实际可以看成是3*3。

即:

1
2
3
4 0 3
7 2 6
8 1 5

最后sub_400FFC()的检查逻辑:

实际上就是要让这个3*3等于

1
2
3
1 2 3
4 5 6
7 8 0

把0看成空位的话,很容易就想到3*3的华容道了。

(或者玩算法的小伙伴可能对八数码问题这个名字更熟悉?

有本事下次出数织啊!20*20我都给你火速解出来(来自数织爱好者的吐槽)

这里实际上是求最短能得到的路径(15步),懒得想了,直接去网上抓了个现成代码下来改了改。

八数码问题的代码见:八数码问题-A*(AStar)算法实现_Broken Geeker-CSDN博客

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
#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
#define maxState 10000
#define N 3
using namespace std;

bool isEqual(int a[N][N][maxState],int b[N][N],int n){
for(int i = 0;i < N;i ++){
for(int j = 0;j < N;j ++){
if(a[i][j][n] != b[i][j]) return false;
}
}
return true;
}
bool isEqual(int a[N][N],int b[N][N]){
for(int i = 0;i < N;i ++){
for(int j = 0;j < N;j ++){
if(a[i][j] != b[i][j]) return false;
}
}
return true;
}
int evalute(int state[N][N],int target[N][N]){
int num = 0;
for(int i = 0;i < N;i ++){
for(int j = 0;j < N;j ++)
if(state[i][j] != target[i][j]) num ++;
}
return num;
}
void findBrack(int a[N][N],int x,int y){
for(int i = 0;i < N;i ++){
for(int j = 0;j < N;j ++){
if(a[i][j] == 0) {
x = i;y = j;return;
}
}
}
}
bool move(int a[N][N],int b[N][N],int dir){
//1 up 2 down 3 left 4 right
int x = 0,y = 0;
for(int i = 0;i < N;i ++){
for(int j = 0;j < N;j ++){
b[i][j] = a[i][j];
if(a[i][j] == 0) {
x = i;y = j;
}
}
}
if(x == 0 && dir == 1) return false;
if(x == N-1 && dir == 2) return false;
if(y == 0 && dir == 3) return false;
if(y == N-1 && dir == 4) return false;
if(dir == 1){b[x-1][y] = 0;b[x][y] = a[x-1][y];}
else if(dir == 2){b[x+1][y] = 0;b[x][y] = a[x+1][y];}
else if(dir == 3){b[x][y-1] = 0;b[x][y] = a[x][y-1];}
else if(dir == 4){b[x][y+1] = 0;b[x][y] = a[x][y+1];}
else return false;
return true;
}
void statecpy(int a[N][N][maxState],int b[N][N],int n){
for(int i = 0;i < N;i ++){
for(int j = 0;j < N;j ++){
a[i][j][n] = b[i][j];
}
}
}
void getState(int a[N][N][maxState],int b[N][N],int n){
for(int i = 0;i < N;i ++){
for(int j = 0;j < N;j ++){
b[i][j] = a[i][j][n];
}
}
}
void statecpy(int a[N][N],int b[N][N]){
for(int i = 0;i < N;i++){
for(int j = 0;j < N;j++)
a[i][j] = b[i][j];
}
}
int checkAdd(int a[N][N][maxState],int b[N][N],int n){
for(int i = 0;i < n;i ++){
if(isEqual(a,b,i)) return i;
}
return -1;
}
int Astar(int a[N][N][maxState],int start[N][N],int target[N][N],int path[maxState]){
bool visited[maxState] = {false};
int fitness[maxState] = {0};
int passLen[maxState] = {0};
int curpos[N][N];
statecpy(curpos,start);
int id = 0,Curid = 0;
fitness[id] = evalute(curpos,target);
statecpy(a,start,id++);
while(!isEqual(curpos,target)){
for(int i = 1;i < 5;i ++){//向四周找方向
int tmp[N][N] = {0};
if(move(curpos,tmp,i)){
int state = checkAdd(a,tmp,id);
if(state == -1){//not add
path[id] = Curid;
passLen[id] = passLen[Curid] + 1;
fitness[id] = evalute(tmp,target) + passLen[id];
statecpy(a,tmp,id++);
}else{//add
int len = passLen[Curid] + 1,fit = evalute(tmp,target) + len;
if(fit < fitness[state]){
path[state] = Curid;
passLen[state] = len;
fitness[state] = fit;
visited[state] = false;
}
}
}
}
visited[Curid] = true;
//找到适应度最小的最为下一个带搜索节点
int minCur = -1;
for(int i = 0;i < id;i ++)
if(!visited[i] && (minCur == -1 || fitness[i] < fitness[minCur])) minCur = i;
Curid = minCur;
getState(a,curpos,Curid);
if(id == maxState) return -1;
}
return Curid;
}
void show(int a[N][N][maxState],int n){
cout << "-------------------------------\n";
for(int i = 0;i < N;i ++){
for(int j =0;j < N;j ++){
cout << a[i][j][n] << " ";
}
cout << endl;
}
cout << "-------------------------------\n";
}
int calDe(int a[N][N]){
int sum = 0;
for(int i = 0;i < N*N;i ++){
for(int j = i+1;j < N*N;j ++){
int m,n,c,d;
m = i/N;n = i%N;
c = j/N;d = j%N;
if(a[c][d] == 0) continue;
if(a[m][n] > a[c][d]) sum ++;
}
}
return sum;
}
void autoGenerate(int a[N][N]){
int maxMove = 50;
srand((unsigned)time(NULL));
int tmp[N][N];
while(maxMove --){
int dir = rand()%4 + 1;
if(move(a,tmp,dir)) statecpy(a,tmp);
}
}
int main(){
int a[N][N][maxState] = {0};
// int start[N][N] = {1,2,3,4,5,6,7,8,0};
// autoGenerate(start);
// cout << start[0][0] << start[1][1];
int start[N][N] = {4,0,3,7,2,6,8,1,5};
int target[N][N] = {1,2,3,4,5,6,7,8,0};
if(!(calDe(start)%2 == calDe(target)%2)){
cout << "无解\n";
return 0;
}
int path[maxState] = {0};
int res = Astar(a,start,target,path);
if(res == -1){
cout << "达到最大搜索能力\n";
return 0;
}
int shortest[maxState] = {0},j = 0;
while(res != 0){
shortest[j++] = res;
res = path[res];
}
cout << "第 0 步\n";
show(a,0);
for(int i = j - 1;i >= 0;i --){
cout << "第 " << j-i << " 步\n";
show(a,shortest[i]);
}
return 0;
}

得到每一步的情况,进而根据switch写出路径。

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
第 0 步
-------------------------------
4 0 3
7 2 6
8 1 5
-------------------------------
第 1 步
-------------------------------
4 2 3
7 0 6
8 1 5
-------------------------------
第 2 步
-------------------------------
4 2 3
7 1 6
8 0 5
-------------------------------
第 3 步
-------------------------------
4 2 3
7 1 6
8 5 0
-------------------------------
第 4 步
-------------------------------
4 2 3
7 1 0
8 5 6
-------------------------------
第 5 步
-------------------------------
4 2 0
7 1 3
8 5 6
-------------------------------
第 6 步
-------------------------------
4 0 2
7 1 3
8 5 6
-------------------------------
第 7 步
-------------------------------
4 1 2
7 0 3
8 5 6
-------------------------------
第 8 步
-------------------------------
4 1 2
7 5 3
8 0 6
-------------------------------
第 9 步
-------------------------------
4 1 2
7 5 3
0 8 6
-------------------------------
第 10 步
-------------------------------
4 1 2
0 5 3
7 8 6
-------------------------------
第 11 步
-------------------------------
0 1 2
4 5 3
7 8 6
-------------------------------
第 12 步
-------------------------------
1 0 2
4 5 3
7 8 6
-------------------------------
第 13 步
-------------------------------
1 2 0
4 5 3
7 8 6
-------------------------------
第 14 步
-------------------------------
1 2 3
4 5 0
7 8 6
-------------------------------
第 15 步
-------------------------------
1 2 3
4 5 6
7 8 0
-------------------------------

6 左
2 上
4 右
8 下
// 884226886224488

路径为“884226886224488”。

接下来看主函数里check上面的部分,看到sub_409070()实际上是一个scanf,而dword_4A1B60是我们的输入,也就是最后的flag,中间对输入进行处理以后才得到“884226886224488”这个字符串。

在里面翻可以翻到一个sub_400B58(),猜测是base64换表编码。

于是尝试写脚本编码。

1
2
3
4
5
6
7
8
9
import base64
b64table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
mytable=""
offset=-18
for i in range(len(b64table)):
mytable+=b64table[(i+offset)%len(b64table)]
text="884226886224488".encode()
cipher=base64.b64encode(text).decode().translate(str.maketrans(b64table,mytable))
print(cipher)

试试能不能过check。

wsl运行:(要装qemu才能执行,毕竟特殊架构。

1
2
cp $(which qemu-mips) .
./qemu-mips -L . ./puzzle

执行mips程序,输入脚本中解出的字符串,发现成功了,get flag。

flag{8xOi6R2k8xOk6R2i7xOm}

aRm

arm架构。

照例通过字符串回溯找到主函数。

v1是key,v9是输入的flag,对输入的限制就是长度为42且头尾是“flag{”和“}”。

动态调一下可以发现,sub_27770()这个函数实际上是把unk_723A0数组里的42个数据复制到v8里。

1
./qemu-arm -L ./ -g 12345 ./aRm

(Debugger选Remote GDB debugger,把端口号填上就好,其余配置具体见RE套路 - 关于使用IDA 7.0前端进行的动态调试 | c10udlnk_Log中调试elf部分。

现在我们未知的数就剩v5和v6了,v5要看sub_1714C()的输出,v6这里相当于是42条42元一次方程组(输入未知的情况下)。

而sub_105B4()是输出42个结果,于是可以知道只要输出了output.txt里的42个数就是正确的flag了。

由于前面有一个sub_169AC(key),这边又是一个无参的sub_1714C()+1,于是猜测是srand(seed)和rand()。

为了证明猜测,多次运行程序输入同一个key和相同/不同的flag,发现每一次的v5是一样的,结合rand()的伪随机性,确定这就是随机函数。

由于key只有一字节(0~255),干脆直接爆破。把output.txt的数据读入,用sympy库解方程,只要第一个解x0等于ord('f')^v8[0]=102^0xA0=198,就说明这个key有极大可能性是正确的key。

当然,在此之前,我们得先知道每一次的v5(即方程的系数)是多少。

于是hook函数,在v5生成之后复用程序原来就有的print函数及格式符,把每次生成的v5都打印出来。

还记得有个函数是可以输出八位十六进制数的吧,就是那个sub_105B4(),我们可以用这里面的printf,然后把调用这个函数的地方nop掉(目标要明确,现在是为了爆破key,没必要管程序的正常性hahah)。

本来是想自己堆个调用printf出来的,不知道为什么keypatch对LDR R0, =a08x解释不了,于是只好绕个小路了。

转到汇编窗口,记一下这里的loc,等会要跳过来的。

看回去原来二重循环里出v5那个地方

这几条语句的意思就是f5里面的那行v5 = (unsigned __int8)(sub_1714C() + 1);,我们从再下一行开始改。

注意可以改的范围在蓝框这里,这是我们不需要的v6[j] += (unsigned __int8)v9[k] * v5;,在这个范围里可以尽情修改,剩下的nop掉。

用keypatch直接输入汇编,patch后面的语句为

(其实就是改了一行B loc_105D4,剩下的直接Fill with NOPs就好)

接下来去往loc_105D4,改造一下。

我们知道,现在R3寄存器里实际上存的是v5的值,我们调用printf直接输出R3的值就能达成目标。

在ARM汇编里,函数传参用R0、R1……所以我们这里给R1一个R3的值就好。

这里本来就是MOV R1, R3不用改,所以直接把前面nop掉。

因为v5那里是取(unsigned __int8),所以把这里改一下,把"%08x"改成"%02x",就是出来的v5。

别忘了后面还要跳回去,找到地址:

patch:

记得把调用sub_105B4()的地方也nop掉。

最后把patch的字节保存一下。

运行测试一下,有:

ok,hook成功,开始爆破。

用pexpect进行批量的自动化交互见:【wp】2020ChaMd5圣诞题 | c10udlnk_Log

多亏了周五做的那个题,才有了这个题的爆破脚本(Doge。

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
import pexpect
from sympy import *

data=[]
with open('output.txt','r') as f:
tmp=f.read().split('\r\n')
data=[int(x,16) for x in tmp]
src=[0xA0, 0xE4, 0xBA, 0xFB, 0x10, 0xDD, 0xAC, 0x65, 0x8D, 0x0B, 0x57, 0x1A, 0xE4, 0x28, 0x96, 0xB3, 0x0C, 0x79, 0x4D, 0x80, 0x90, 0x99, 0x58, 0xFE, 0x50, 0xD3, 0xF9, 0x3C, 0x0F, 0xC1, 0xE3, 0xA6, 0x39, 0xC3, 0x28, 0x75, 0xF8, 0xC9, 0xC8, 0xCD, 0x78, 0x26]
flag='flag{000000000000000000000000000000000000}'

var=[]
for num in range(42):
exec("x"+str(num)+"=Symbol('x'+str(num))")
var.append("x"+str(num)) #创建42个变量x0~x41
for i in range(256):
r=pexpect.spawn('./qemu-arm -L ./ ./aRm_getRand')
r.sendline(str(i))
r.sendline(flag)
r.readline()
r.readline()
rand=[]
for j in range(42*42):
s=r.readline()
rand.append(int(str(s)[2:-5],16))
r.wait()
exper=[]
for j in range(42):
anEx=""
for k in range(42):
anEx+=str(rand[j*42+k])+"*"+var[k]+"+"
anEx=anEx[:-1]+"-"+str(data[j])
exper.append(anEx)
res=solve(exper,var)
print(str(i)+": ")
print(res.values())

爆破得到:

可知key是82,而v9在xor以后的数组也爆出来了,简单xor得flag:

1
2
3
4
5
6
arr=[0xA0, 0xE4, 0xBA, 0xFB, 0x10, 0xDD, 0xAC, 0x65, 0x8D, 0x0B, 0x57, 0x1A, 0xE4, 0x28, 0x96, 0xB3, 0x0C, 0x79, 0x4D, 0x80, 0x90, 0x99, 0x58, 0xFE, 0x50, 0xD3, 0xF9, 0x3C, 0x0F, 0xC1, 0xE3, 0xA6, 0x39, 0xC3, 0x28, 0x75, 0xF8, 0xC9, 0xC8, 0xCD, 0x78, 0x26]
x=[198, 136, 219, 156, 107, 228, 152, 7, 239, 63, 97, 127, 134, 5, 247, 131, 109, 75, 96, 180, 241, 173, 57, 211, 49, 224, 157, 9, 34, 243, 129, 199, 1, 244, 31, 17, 157, 171, 252, 249, 64, 91]
flag=""
for i in range(42):
flag+=chr(x[i]^arr[i])
print(flag)

flag{94bb46eb-a0a2-4a4a-a3d5-2ba877deb448}

pe

arm架构,没环境调不动,只能硬看了XD。这题有好多奇怪的函数,而且通过伪代码跟的话就能看到函数套函数套函数……所以基本靠猜出来的(

继续通过字符串回溯找主函数。

根据参数猜测,sub_1400023C8()是strcmp()的作用,我们需要让v9=“KIMLXDWRZXTHXTHQTXTXHZWC”。

再往上走,sub_1400015B0这个函数调用了v9,于是跟进去看功能。

感觉是某种加密,以相邻的两字符为一组,对这两个字符做相同的操作,再做后续处理。

跟进sub_1400012B8()里看,可以看到大概是一个搜索的过程

如果不等于-1就说明在表中找到了这个元素,然后返回一个索引(?

再往下看好像就看不太懂了,然后就是玄学的猜猜猜= =

回去看string可以看到一个这个,猜测是密钥表之类的?

往上回溯也看不到什么线索,不过可以发现这25个数字刚好没有相同的。

现在总结一下这个古典加密算法的特点,大概是两个为一组处理+已定义的密钥表(即不是通过输入生成的)5*5+处理时用到索引。

很久很久以前想写某对cp的AU同人时想把ctf元素混进去,就看了很多简单又奇奇怪怪的编码/古典密码(现代密码太学术了XD),没想到现在有用武之地了(手动狗头。

安利一个编码/古典密码的集合:CTF中那些脑洞大开的编码和加密 - jack_Meng - 博客园

然后翻到了一个符合这个特点的密码,Playfair Cipher:

不同的是密码表是直接给出的,不过加密流程再对回ida里的反编译感觉挺像的,于是果断试试。

按照Playfair Cipher的加解密流程写出脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def getIndex(c):
for i in range(len(key)):
if key[i].find(c)!=-1:
return i,key[i].find(c)
letter_list="ABCDEFGHJKLMNOPQRSTUVWXYZ"
key=["CREIH","TQGNU","AOVXL","DZKYM","PBWFS"]
cipher="KIMLXDWRZXTHXTHQTXTXHZWC"
text=""
for i in range(0,len(cipher),2):
j=i+1
x1,y1=getIndex(cipher[i])
x2,y2=getIndex(cipher[j])
if x1==x2:
text+=key[x1][(y1+1)%5]+key[x2][(y2+1)%5]
elif y1==y2:
text+=key[(x1+1)%5][y1]+key[(x2+1)%5][y2]
else:
text+=key[x1][y2]+key[x2][y1]
print(text)

走一遍脚本解密可以得到:

YES MAYBE YOU CAN RUN AN ARM PE

No, I can’t 😦

看起来能读的通,成功get flag。

flag{YESMAYBEYOUCANRUNANARMPE}

crash

用binwalk分析给的crash文件

可以看到0x1000处有一个ELF, 32-bit LSB executable,先把这个可执行文件用dd分离出来。

其中if=输入文件名(被分离的),of=输出文件名(分离出来的),skip=跳过的块(bs指定字节)数,count=块长度,最后得到file_0x1000文件。

用ida打开如果出现这个warning,可以用 Reinose/Useful-IDAScripts 的方法解决。

老样子,通过shift+F12寻找字符串,通过交叉引用找到主函数。

先通过一些参数和常用逻辑的猜测给系统函数命名,方便后续分析。

然后就剩sub_8048737()sub_80487E7这两个加密函数了。

第一个就很简单啦,简单xor。

第二个点进sub_80489E9()可以看到md5经典常数,说明这一系列大概是个md5加密(当然md5的实现还是要看看的。

退回去,看到0x804B060这里,很像数组的始地址。

于是转到IDA View,按G跳到这个地址,可以看到一堆可见字符,按A转成字符串。

再对回去逻辑就很明显了,就是依次取四个字符进行md5加密,然后跟这八个字符串比。

于是exp就可以写啦,爆破md5:

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
# python2
from hashlib import md5

dst=['bf2b36d56f5757c13cad80494b385e78','3fe9dbae5dc4408350500affa20074aa','1fa6770eca6b57e47a042ffe52eca8ff','1aad6b7da1122b4b5a53bf5a4d3b11b0','e7b77d9e0ab19fc9ea98154f994fccc5','75d9128cfeb61b8949664f6a067f6469','d8b0a52c64d6075017b7346140550c46','306529c7cdedfb06e27b39f7b2babf4d']
res=[]
for a in range(33,127):
for b in range(33,127):
for c in range(33,127):
for d in range(33,127):
l=[a^0x17,b^0x17,c^0x17,d^0x17]
tmp=''.join(list(map(chr,l)))
tmpmd5=md5(tmp).hexdigest()
if tmpmd5 in dst:
s=''.join(list(map(chr,[a,b,c,d])))
res.append((dst.index(tmpmd5),s))
if len(res)==8:
break
else:
continue
break
else:
continue
break
else:
continue
break
res.sort(key=(lambda x:x[0]))
flag=''.join([x[1] for x in res])
print(flag)

得到flag:

flag{ux1cy1x4iltkahbc9nu1nk00d9akpp7w}

文章作者: c10udlnk
文章链接: https://c10udlnk.top/2020/12/29/wpFor-2020XCTF/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 c10udlnk_Log