虎符ctf三道逆向中最简单的一道,比赛期间我只做出来这一道。题目对我来说挺新颖的,所以在这里也记录一下。刚写完vm的题解,有点累,所以这篇就简单点写。
只给出一个disasm.txt
,部分如下:
查了下,是python的字节码。
pyc可以还原成py源代码,但是这个类似汇编的东西没办法还原出来(如果大佬们知道怎么还原,劳请告诉我一下)
dis模块
同时查到这个文本是怎么来的,python的dis模块。
下面有个小例子:
def f():
s = [90, 100, 87, 109, 86, 108, 86, 105, 90, 104, 88, 102]
b = [90, 100, 87, 109, 86, 108, 86, 105, 90, 104, 88, 102]
#a = s[6:30:3]
#
#s = 'xctf{1231231}'
#a = (((((((ord(s[0])*128+ord(s[1]))*128)+ord(s[2]))*128)+ord(s[3]))*128) + ord(s[4])) * 128 + ord(s[5])
#print(a)
a = map(lambda x:x+1, zip(s, b[2:5]))
import dis
dis.dis(f)
输出:
2 0 LOAD_CONST 1 (90)
2 LOAD_CONST 2 (100)
4 LOAD_CONST 3 (87)
6 LOAD_CONST 4 (109)
8 LOAD_CONST 5 (86)
10 LOAD_CONST 6 (108)
12 LOAD_CONST 5 (86)
14 LOAD_CONST 7 (105)
16 LOAD_CONST 1 (90)
18 LOAD_CONST 8 (104)
20 LOAD_CONST 9 (88)
22 LOAD_CONST 10 (102)
24 BUILD_LIST 12
26 STORE_FAST 0 (s)
3 28 LOAD_CONST 1 (90)
30 LOAD_CONST 2 (100)
32 LOAD_CONST 3 (87)
34 LOAD_CONST 4 (109)
36 LOAD_CONST 5 (86)
38 LOAD_CONST 6 (108)
40 LOAD_CONST 5 (86)
42 LOAD_CONST 7 (105)
44 LOAD_CONST 1 (90)
46 LOAD_CONST 8 (104)
48 LOAD_CONST 9 (88)
50 LOAD_CONST 10 (102)
52 BUILD_LIST 12
54 STORE_FAST 1 (b)
9 56 LOAD_GLOBAL 0 (map)
58 LOAD_CONST 11 (<code object <lambda> at 0x0000019F93CB0270, file "d:\桌面\20200419 虎符ctf\text.py", line 9>)
60 LOAD_CONST 12 ('f.<locals>.<lambda>')
62 MAKE_FUNCTION 0
64 LOAD_GLOBAL 1 (zip)
66 LOAD_FAST 0 (s)
68 LOAD_FAST 1 (b)
70 LOAD_CONST 13 (2)
72 LOAD_CONST 14 (5)
74 BUILD_SLICE 2
76 BINARY_SUBSCR
78 CALL_FUNCTION 2
80 CALL_FUNCTION 2
82 STORE_FAST 2 (a)
84 LOAD_CONST 0 (None)
86 RETURN_VALUE
Disassembly of <code object <lambda> at 0x0000019F93CB0270, file "d:\桌面\20200419 虎符ctf\text.py", line 9>:
9 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (1)
4 BINARY_ADD
6 RETURN_VALUE
上面的输出的代码形式风格和题目给出的只有细微的差异(我用的是python3,而题目是python2,可能是这个原因)
然后可以去试着分析下题目给出的代码了,遇到不懂的点,可以自己写个函数,然后disasm,看看和题目中代码的差异。
分析字节码
首先是arr0,arr1,arr2这三个列表的赋值,很容易看出来。
然后有check0, check1,check2,check3四个检测函数。
check0
# <genexpr> line 6 of game.py
6 0 LOAD_FAST 0 '.0'
3 FOR_ITER 32 'to 38'
6 STORE_FAST 1 'x'
9 LOAD_GLOBAL 0 'ord'
12 LOAD_FAST 1 'x'
15 CALL_FUNCTION_1 1 None
18 LOAD_GLOBAL 1 'range'
21 LOAD_CONST 32
24 LOAD_CONST 128
27 CALL_FUNCTION_2 2 None
30 COMPARE_OP 6 in
33 YIELD_VALUE
34 POP_TOP
35 JUMP_BACK 3 'to 3'
38 LOAD_CONST None
41 RETURN_VALUE
//推测是判断字符ord(x) in (32, 128)
# check0 line 5 of game.py
6 0 LOAD_GLOBAL 0 'all'
3 LOAD_GENEXPR '<code_object <genexpr>>'
6 MAKE_FUNCTION_0 0 None
9 LOAD_FAST 0 's'
12 GET_ITER
13 CALL_FUNCTION_1 1 None
16 CALL_FUNCTION_1 1 None
19 RETURN_VALUE
//check0()判断字符介于(32,128)
配合着我代码中的注释就应该能看懂了吧,我就不多解释了。
check1
# check1 line 8 of game.py
9 0 LOAD_GLOBAL 0 'len'
3 LOAD_FAST 0 's'
6 CALL_FUNCTION_1 1 None
9 LOAD_CONST 100
12 COMPARE_OP 0 <
15 POP_JUMP_IF_FALSE 58 'to 58'
//if len(s)<100
18 LOAD_GLOBAL 0 'len'
21 LOAD_FAST 0 's'
24 CALL_FUNCTION_1 1 None
27 LOAD_GLOBAL 0 'len'
30 LOAD_FAST 0 's'
33 CALL_FUNCTION_1 1 None
36 BINARY_MULTIPLY //乘
//len(s) * len(s)
37 LOAD_CONST 777
40 BINARY_MODULO //取模
//(len(s) * len(s))%777
41 LOAD_CONST 233
44 BINARY_XOR //异或
//((len(s) * len(s))%777) ^ 233
45 LOAD_CONST 513
48 COMPARE_OP 2 ==
//if ((len(s) * len(s))%777) ^ 233 == 513
51_0 COME_FROM 15 '15'
51 POP_JUMP_IF_FALSE 58 'to 58'
10 54 LOAD_GLOBAL 1 'True'
57 RETURN_END_IF
58_0 COME_FROM 51 '51'
12 58 LOAD_GLOBAL 2 'False'
61 RETURN_VALUE
62 LOAD_CONST None
65 RETURN_VALUE
//check1()判断flag长度,经爆破得知len(flag) = 39
check1检测flag长度是否符合条件( if ((len(s) * len(s))%777) ^ 233 == 513
)
爆破可以求得长度为39
check2
# check2 line 14 of game.py
15 0 LOAD_GLOBAL 0 'ord'
3 LOAD_FAST 0 's'
6 LOAD_CONST 0
9 BINARY_SUBSCR //TOS = TOS1[TOS]
10 CALL_FUNCTION_1 1 None
13 LOAD_CONST 128
16 BINARY_MULTIPLY
//ord(s[0]) * 128
17 LOAD_GLOBAL 0 'ord'
20 LOAD_FAST 0 's'
23 LOAD_CONST 1
26 BINARY_SUBSCR
27 CALL_FUNCTION_1 1 None
30 BINARY_ADD
31 LOAD_CONST 128
34 BINARY_MULTIPLY
//(ord(s[0])*128+ord(s[1])) * 128
35 LOAD_GLOBAL 0 'ord'
38 LOAD_FAST 0 's'
41 LOAD_CONST 2
44 BINARY_SUBSCR
45 CALL_FUNCTION_1 1 None
48 BINARY_ADD
49 LOAD_CONST 128
52 BINARY_MULTIPLY
//(((ord(s[0])*128+ord(s[1]))*128) + ord(s[2])) * 128
53 LOAD_GLOBAL 0 'ord'
56 LOAD_FAST 0 's'
59 LOAD_CONST 3
62 BINARY_SUBSCR
63 CALL_FUNCTION_1 1 None
66 BINARY_ADD
67 LOAD_CONST 128
70 BINARY_MULTIPLY
//(((((ord(s[0])*128+ord(s[1]))*128)+ord(s[2])) * 128) + ord(s[3])) * 128
71 LOAD_GLOBAL 0 'ord'
74 LOAD_FAST 0 's'
77 LOAD_CONST 4
80 BINARY_SUBSCR
81 CALL_FUNCTION_1 1 None
84 BINARY_ADD
85 LOAD_CONST 128
88 BINARY_MULTIPLY
//(((((((ord(s[0])*128+ord(s[1]))*128)+ord(s[2]))*128)+ord(s[3]))*128) + ord(s[4])) * 128
89 LOAD_GLOBAL 0 'ord'
92 LOAD_FAST 0 's'
95 LOAD_CONST 5
98 BINARY_SUBSCR
99 CALL_FUNCTION_1 1 None
102 BINARY_ADD
//(((((((ord(s[0])*128+ord(s[1]))*128)+ord(s[2]))*128)+ord(s[3]))*128) + ord(s[4])) * 128 + ord(s[5])
103 LOAD_CONST 3533889469877L
106 COMPARE_OP 2 ==
109 POP_JUMP_IF_FALSE 138 'to 138'
112 LOAD_GLOBAL 0 'ord'
115 LOAD_FAST 0 's'
118 LOAD_CONST -1
121 BINARY_SUBSCR
122 CALL_FUNCTION_1 1 None
125 LOAD_CONST 125
128 COMPARE_OP 2 ==
131_0 COME_FROM 109 '109'
131 POP_JUMP_IF_FALSE 138 'to 138'
16 134 LOAD_GLOBAL 1 'True'
137 RETURN_END_IF
138_0 COME_FROM 131 '131'
18 138 LOAD_GLOBAL 2 'False'
141 RETURN_VALUE
142 LOAD_CONST None
145 RETURN_VALUE
/*
if 表达式 == 3533889469877L:
if ord(s[-1]) == 125:
return True
else return false
else return false
*/
//check2()判断flag{}这个框架,以及flag{}主体的第一个字符flag[5]
check2用于判断前6个字符是否满足(((((((ord(s[0])*128+ord(s[1]))*128)+ord(s[2]))*128)+ord(s[3]))*128) + ord(s[4])) * 128 + ord(s[5]) == 3533889469877
,同时判断最后一个字符是否是}
只要学过数学的小学生都知道6元一次等式根本不可能求出六个未知数,所以题目可能是假设了前五个字符是flag{
,由此我们可以求出flag的第6个字符,也就是flag主体部分的第一个字符。
注意,我和炜昊都被这里坑到了,看到了flag[5],以为只有5个字符,恰好是flag{
,忽略了第六个字符。
这个低级的下标错误平常不会错,但是每次到这个综合性题目中总会出错,还是要注意啊。
check3
# check3 line 20 of game.py
21 0 LOAD_GLOBAL 0 'map'
3 LOAD_GLOBAL 1 'ord'
6 LOAD_FAST 0 's'
9 CALL_FUNCTION_2 2 None
12 STORE_FAST 1 'arr'
//e.g: map(function, iterable, ...)
//arr = map(ord, s)
22 15 LOAD_FAST 1 'arr'
18 LOAD_CONST 6
21 LOAD_CONST 30
24 LOAD_CONST 3
27 BUILD_SLICE_3 3 //切片,3个一组
30 BINARY_SUBSCR
31 STORE_FAST 2 'a'
//a = arr[6:30:3]
23 34 SETUP_LOOP 62 'to 99'
37 LOAD_GLOBAL 2 'range'
40 LOAD_GLOBAL 3 'len'
43 LOAD_FAST 2 'a'
46 CALL_FUNCTION_1 1 None
49 CALL_FUNCTION_1 1 None
52 GET_ITER
53 FOR_ITER 42 'to 98'
56 STORE_FAST 3 'i'
//类似for i in range(len(a)) ,不过这里用的是迭代器
24 59 LOAD_FAST 2 'a'
62 LOAD_FAST 3 'i'
65 BINARY_SUBSCR
66 LOAD_CONST 17684
69 BINARY_MULTIPLY
70 LOAD_CONST 372511
73 BINARY_ADD
74 LOAD_CONST 257
77 BINARY_MODULO
78 LOAD_GLOBAL 4 'arr0'
81 LOAD_FAST 3 'i'
84 BINARY_SUBSCR
85 COMPARE_OP 3 !=
88 POP_JUMP_IF_FALSE 53 'to 53'
/*
if (a[i] * 17684 + 372511) % 257 == arr0[i]:(是==,我没搞错(因为jump if false))
goto 53(继续循环)
else return flase
*/
25 91 LOAD_GLOBAL 5 'False'
94 RETURN_END_IF
95_0 COME_FROM 88 '88'
95 JUMP_BACK 53 'to 53'
98 POP_BLOCK
99_0 COME_FROM 34 '34'
26 99 LOAD_FAST 1 'arr'
102 LOAD_CONST -2
105 LOAD_CONST 33
108 LOAD_CONST -1
111 BUILD_SLICE_3 3
114 BINARY_SUBSCR
115 LOAD_CONST 5
118 BINARY_MULTIPLY
119 STORE_FAST 4 'b'
//b = arr[-2:33:-1] * 5
27 122 LOAD_GLOBAL 0 'map'
125 LOAD_LAMBDA '<code_object <lambda>>'
128 MAKE_FUNCTION_0 0 None
131 LOAD_GLOBAL 6 'zip'
134 LOAD_FAST 4 'b'
137 LOAD_FAST 1 'arr'
140 LOAD_CONST 7
143 LOAD_CONST 27
146 SLICE+3
147 CALL_FUNCTION_2 2 None
150 CALL_FUNCTION_2 2 None
153 STORE_FAST 5 'c'
//c = map(lambda x:函数体, zip(b, arr[7,27]))
//即c[i] = b[i] ^ arr[7,27][i]
28 156 LOAD_FAST 5 'c'
159 LOAD_GLOBAL 7 'arr1'
162 COMPARE_OP 3 !=
165 POP_JUMP_IF_FALSE 172 'to 172'
29 168 LOAD_GLOBAL 5 'False'
171 RETURN_END_IF
172_0 COME_FROM 165 '165'
/*
if c == arr1:
goto 172
else return false
*/
30 172 LOAD_CONST 0
175 STORE_FAST 6 'p'
31 178 SETUP_LOOP 105 'to 286'
181 LOAD_GLOBAL 2 'range'
184 LOAD_CONST 28
187 LOAD_CONST 34
190 CALL_FUNCTION_2 2 None
193 GET_ITER
194 FOR_ITER 88 'to 285'
197 STORE_FAST 3 'i'
/*
for i in range(28, 34)
*/
32 200 LOAD_FAST 1 'arr'
203 LOAD_FAST 3 'i'
206 BINARY_SUBSCR
207 LOAD_CONST 107
210 BINARY_ADD
211 LOAD_CONST 16
214 BINARY_DIVIDE
215 LOAD_CONST 77
218 BINARY_ADD
// ((arr[i] + 107)//16)+77 (除法//还是/存疑)
219 LOAD_GLOBAL 8 'arr2'
222 LOAD_FAST 6 'p'
225 BINARY_SUBSCR
226 COMPARE_OP 3 !=
229 POP_JUMP_IF_TRUE 268 'to 268'
/*
if arr2[p] != ((arr[i] + 107)//16)+77 :
return false
*/
232 LOAD_FAST 1 'arr'
235 LOAD_FAST 3 'i'
238 BINARY_SUBSCR
239 LOAD_CONST 117
242 BINARY_ADD
243 LOAD_CONST 16
246 BINARY_MODULO
247 LOAD_CONST 99
250 BINARY_ADD
251 LOAD_GLOBAL 8 'arr2'
254 LOAD_FAST 6 'p'
257 LOAD_CONST 1
260 BINARY_ADD
261 BINARY_SUBSCR
262 COMPARE_OP 3 !=
265_0 COME_FROM 229 '229'
265 POP_JUMP_IF_FALSE 272 'to 272'
/*
if (arr[i]+117)%16+99 == arr2[p+1]:
goto 272
else return false
*/
33 268 LOAD_GLOBAL 9 'false'
271 RETURN_END_IF
272_0 COME_FROM 265 '265'
34 272 LOAD_FAST 6 'p'
275 LOAD_CONST 2
278 INPLACE_ADD //TOS = TOS1 + TOS
279 STORE_FAST 6 'p'
282 JUMP_BACK 194 'to 194'
285 POP_BLOCK
286_0 COME_FROM 178 '178'
//p += 2,然后继续循环
35 286 LOAD_GLOBAL 10 'True'
289 RETURN_VALUE
整理一下,check3的逻辑是这样的
def check3():
arr = map(ord, flag)
a = arr[6:30:3]
for i in range(len(a)):
if (a[i] * 17684 + 372511) % 257 != arr0[i]:
return False
b = arr[-2:33:-1] * 5
c = map(lambda x: x[0]^x[1], zip(b, arr[7,27]))
if c != arr1:
return False
p = 0
for i in range(28, 34):
if arr2[p] != (((arr[i]+107)//16)+77):
return False
if arr2[p+1] != ((arr[i]+117)%16+99):
return False
p += 2
return True
逆向
倒着逆,脚本如下:
arr0 = [249, 91, 149, 113, 16, 91, 53 ,41]
arr1 = [43, 1, 6, 69, 20, 62, 6, 44, 24, 113, 6, 35, 0 , 3, 6, 44, 20, 22, 127, 60]
arr2 = [90, 100, 87, 109, 86, 108, 86, 105, 90, 104, 88, 102]
def check3():
arr = map(ord, flag)
a = arr[6:30:3]
for i in range(len(a)):
if (a[i] * 17684 + 372511) % 257 != arr0[i]:
return False
b = arr[-2:33:-1] * 5
c = map(lambda x: x[0]^x[1], zip(b, arr[7,27]))
if c != arr1:
return False
p = 0
for i in range(28, 34):
if arr2[p] != (((arr[i]+107)//16)+77):
return False
if arr2[p+1] != ((arr[i]+117)%16+99):
return False
p += 2
return True
def solve_check1():
for i in range(100):
if ((i *i )%777) ^ 233 == 513:
print(i)
def solve_5():
for i,n in enumerate('flag{'):
flag[i] = ord(n)
flag[38] = ord('}')
sum = 0
for i in range(5):
sum = (sum + flag[i]) * 128
#print(sum)
for i in range(32,128):
if sum+i==3533889469877:
flag[5] = i
def solve_6_30_3():
for i in range(6, 30, 3):
for j in range(32, 128):
if (j * 17684 + 372511) % 257 == arr0[(i-6)//3]:
flag[i] = j
break
def solve_28_34():
p = 0
for i in range(28,34):
for j in range(32,128):
if (((j+107)//16)+77) == arr2[p] and ((j+117)%16+99) == arr2[p+1]:
flag[i] = j
break
p += 2
def solve_34_38():
flag[34] = flag[18] ^ arr1[11]
flag[35] = flag[9] ^ arr1[2]
flag[36] = flag[12] ^ arr1[5]
flag[37] = flag[15] ^ arr1[8]
def solve_7_27():
b = flag[37:33:-1]*5
for i in range(20):
flag[i+7] = b[i] ^ arr1[i]
def main():
solve_check1()
global flag
flag = [0] * 39
solve_5()
solve_6_30_3()
solve_28_34()
solve_34_38()
solve_7_27()
for i in flag:
print(chr(i),end='')
main()
注意flag[7, 27]的值需要依靠flag[34, 38]求出。
空包单号网全国地址任意发,24小时自助下单,快速免费提供底单www.dydanhw.com
麦片哥给爷爪巴
难道不是应该给他删了吗?