坐牢就完事儿了☠
感觉这次的初赛难度不低…
看到题面写着字节码 Python3.7,我心里就咯噔一声,暗道大事不妙,因为这比赛是不能联网的,下面直接上题解,具体的字节码知识,补充在了我的 Python3 字节码详解 博文中了;
1 0 LOAD_CONST 0 (<code object keyinit at 0x0000028C1CC11D20, file "crackPYC.py", line 1>)
2 LOAD_CONST 1 ('keyinit')
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (keyinit)
8 8 LOAD_NAME 1 (__name__)
10 LOAD_CONST 2 ('__main__')
12 COMPARE_OP 2 (==)
14 POP_JUMP_IF_FALSE 250
9 16 LOAD_NAME 2 (print)
18 LOAD_CONST 3 ('Can you crack pyc?')
20 CALL_FUNCTION 1
22 POP_TOP
10 24 LOAD_NAME 3 (input)
26 LOAD_CONST 4 ('Plz give me your flag:')
28 CALL_FUNCTION 1
30 STORE_NAME 4 (str)
11 32 LOAD_CONST 5 (108)
34 LOAD_CONST 6 (17)
36 LOAD_CONST 7 (42)
38 LOAD_CONST 8 (226)
40 LOAD_CONST 9 (158)
42 LOAD_CONST 10 (180)
44 LOAD_CONST 11 (96)
46 LOAD_CONST 12 (115)
48 LOAD_CONST 13 (64)
50 LOAD_CONST 14 (24)
52 LOAD_CONST 15 (38)
54 LOAD_CONST 16 (236)
56 LOAD_CONST 17 (179)
58 LOAD_CONST 18 (173)
60 LOAD_CONST 19 (34)
62 LOAD_CONST 20 (22)
64 LOAD_CONST 21 (81)
66 LOAD_CONST 22 (113)
68 LOAD_CONST 15 (38)
70 LOAD_CONST 23 (215)
72 LOAD_CONST 24 (165)
74 LOAD_CONST 25 (135)
76 LOAD_CONST 26 (68)
78 LOAD_CONST 27 (7)
12 80 LOAD_CONST 28 (119)
82 LOAD_CONST 29 (97)
84 LOAD_CONST 30 (45)
86 LOAD_CONST 31 (254)
88 LOAD_CONST 32 (250)
90 LOAD_CONST 33 (172)
92 LOAD_CONST 34 (43)
94 LOAD_CONST 35 (62)
96 BUILD_LIST 32
98 STORE_NAME 5 (text)
13 100 LOAD_NAME 6 (len)
102 LOAD_NAME 4 (str)
104 CALL_FUNCTION 1
106 LOAD_CONST 36 (32)
108 COMPARE_OP 3 (!=)
110 POP_JUMP_IF_TRUE 140
112 LOAD_NAME 4 (str)
114 LOAD_CONST 37 (0)
116 LOAD_CONST 27 (7)
118 BUILD_SLICE 2
120 BINARY_SUBSCR
122 LOAD_CONST 38 ('DASCTF{')
124 COMPARE_OP 3 (!=)
126 POP_JUMP_IF_TRUE 140
128 LOAD_NAME 4 (str)
130 LOAD_CONST 39 (31)
132 BINARY_SUBSCR
134 LOAD_CONST 40 ('}')
136 COMPARE_OP 3 (!=)
138 POP_JUMP_IF_FALSE 154
14 >> 140 LOAD_NAME 2 (print)
142 LOAD_CONST 41 ('Bye bye~~')
144 CALL_FUNCTION 1
146 POP_TOP
15 148 LOAD_NAME 7 (exit)
150 CALL_FUNCTION 0
152 POP_TOP
16 >> 154 LOAD_NAME 8 (list)
156 LOAD_NAME 4 (str)
158 CALL_FUNCTION 1
160 STORE_NAME 9 (st)
17 162 BUILD_LIST 0
164 STORE_NAME 10 (key)
18 166 LOAD_NAME 0 (keyinit)
168 LOAD_NAME 10 (key)
170 CALL_FUNCTION 1
172 POP_TOP
19 174 SETUP_LOOP 48 (to 224)
176 LOAD_NAME 11 (range)
178 LOAD_CONST 36 (32)
180 CALL_FUNCTION 1
182 GET_ITER
>> 184 FOR_ITER 36 (to 222)
186 STORE_NAME 12 (i)
20 188 LOAD_NAME 13 (ord)
190 LOAD_NAME 4 (str)
192 LOAD_NAME 12 (i)
194 BINARY_SUBSCR
196 CALL_FUNCTION 1
198 LOAD_NAME 10 (key)
200 LOAD_NAME 12 (i)
202 LOAD_NAME 6 (len)
204 LOAD_NAME 10 (key)
206 CALL_FUNCTION 1
208 BINARY_MODULO
210 BINARY_SUBSCR
212 BINARY_XOR
214 LOAD_NAME 9 (st)
216 LOAD_NAME 12 (i)
218 STORE_SUBSCR
220 JUMP_ABSOLUTE 184
>> 222 POP_BLOCK
21 >> 224 LOAD_NAME 9 (st)
226 LOAD_NAME 5 (text)
228 COMPARE_OP 2 (==)
230 POP_JUMP_IF_FALSE 242
22 232 LOAD_NAME 2 (print)
234 LOAD_CONST 42 ('Congratulations and you are good at PYC!')
236 CALL_FUNCTION 1
238 POP_TOP
240 JUMP_FORWARD 8 (to 250)
24 >> 242 LOAD_NAME 2 (print)
244 LOAD_CONST 43 ('Sorry,plz learn more about pyc.')
246 CALL_FUNCTION 1
248 POP_TOP
>> 250 LOAD_CONST 44 (None)
252 RETURN_VALUE
Disassembly of <code object keyinit at 0x0000028C1CC11D20, file "crackPYC.py", line 1>:
2 0 LOAD_CONST 1 (0)
2 STORE_FAST 1 (num)
3 4 SETUP_LOOP 42 (to 48)
6 LOAD_GLOBAL 0 (range)
8 LOAD_CONST 2 (8)
10 CALL_FUNCTION 1
12 GET_ITER
>> 14 FOR_ITER 30 (to 46)
16 STORE_FAST 2 (i)
4 18 LOAD_FAST 1 (num)
20 LOAD_CONST 3 (7508399208111569251)
22 BINARY_SUBTRACT
24 LOAD_CONST 4 (4294967295)
26 BINARY_MODULO
28 STORE_FAST 1 (num)
5 30 LOAD_FAST 0 (key)
32 LOAD_METHOD 1 (append)
34 LOAD_FAST 1 (num)
36 LOAD_CONST 5 (24)
38 BINARY_RSHIFT
40 CALL_METHOD 1
42 POP_TOP
44 JUMP_ABSOLUTE 14
>> 46 POP_BLOCK
>> 48 LOAD_CONST 0 (None)
50 RETURN_VALUE
上边字节码的大致内容就是输入一串字符串 str
,然后与 key
进行异或,最后与 text
中的内容逐个比较,其中 key
是通过函数 keyinit
生成,直接上 poc 脚本:
num = 0
key = []
text = [108,17,42,226,158,180,96,115,64,24,38,236,179,173,34,22,81,113,38,215,165,135,68,7,119,97,45,254,250,172,43,62]
flag = ''
for i in range(8):
num = (num - 7508399208111569251) % 4294967295
key.append((num >> 24))
for i in range(len(text)):
flag += chr(text[i] ^ key[i % len(key)])
print(flag)
# DASCTF{0hH_My_9Uy!_vou_D_1T_0^0}
直接抄大佬的 WP:
原题应该来自于今年 GoogleCTF reverse 的 CPP,考点是 C++ 的宏定义:
clang-format --style='{IndentPPDirectives: AfterHash}' preprocess.c > loveCTF.c
宏定义处:
#define _COM(a0, a1, a2) (a0<<2 | a1<<1 | a2)
#define COM(a0, a1, a2, a3, a4, a5) (_COM(a3, a1, a5) << 3 | _COM(a0, a4, a2)) #define A COM(A7, A6, A5, A4, A3, A2)
#define B COM(A1, A0, B7, B6, B5, B4)
#define C COM(B3, B2, B1, B0, C7, C6)
#define D COM(C5, C4, C3, C2, C1, C0)
#define _CP(x, y) INPUT_ ## x ## _ ## y
#define CP(x, y) _CP(x, y) #define _LK(l0, l1, l2, l3, l4, l5, l6, l7) l0 ## l1 ## l2 ## l3 ## l4 ## l5 ## l6 ## l7
#define LK(l0, l1, l2, l3, l4, l5, l6, l7) _LK(l0, l1, l2, l3, l4, l5, l6, l7)
#define L LK(l0, l1, l2, l3, l4, l5, l6, l7)
#define SUM (S0 | S1 | S2 | S3) // 0
#define F (R0 | R1 | R2 | R3 | R4 | R5 | R6 | R7 | R8 | R9) // 0
这边的 10~17 其实是索引:
#ifdefl0
#undefl0
#endif
#definel00
#ifdefl1
#undefl1
#endif
#definel10
#ifdefl2
#undefl2
#endif
#definel20
#ifdefl3
#undefl3
#endif
#definel30
#ifdefl4
#undefl4
#endif
#definel40
#ifdefl5
#undefl5
#endif
#definel50
#ifdefl6
#undefl6
#endif
#definel60
#ifdefl7
#undefl7
#endif
#definel70
#ifdefA0
#undefA0
#endif
通过 CP(L, 0) 得到 INPUT_00000000_0 ,如果值为 1 则定义 A0 为 0 ,其实就是一个取反的过程,照此处理3个字节得到24位,再使用了 A,B,C,D 宏定义处理了二进制位即 4*6,简单位移之后与对应数异或;
数值归零算作成功,结尾使用 F 宏定义check,意味着10组24位二进制字串经处理后都为0;
#if F
#error Failed to execute program
#else
#warning Check completed #endif
先用脚本跑出异或表:
text = """
// Please fill in your flag below
// run `gcc preprocess.c -o preprocess.out`
// Have fun~
#define FLAG_0 CHAR_Z
#define FLAG_1 CHAR_J
#define FLAG_2 CHAR_C
#define FLAG_3 CHAR_T
……省略了太长了,就是源文件内容
"""
data = text.splitlines()
arrs = []
tm = 0
ts = []
for i, v in enumerate(data):
index = data[i].find(" ^ ")
if index != -1:
ts.append(eval(data[i][index + 2:]))
if data[i].startswith("#if D ^ "):
arrs.append(ts.copy())
ts = []
print(arrs)
使用 z3 进行条件约束,求解:
# -*- coding:utf-8 -*-
import z3
def com(a0, a1, a2, a3, a4, a5):
return (a3 << 2 | a1 << 1 | a5) << 3 | (a0 << 2 | a4 << 1 | a2)
# 01011010 01001010 01000011
# 101001
# 010110
arr = [[13, 27, 50, 53], [7, 31, 38, 32], [14, 9, 35, 50], [38, 9, 51, 11], [36, 21, 19, 4], [46, 13, 35, 11], [38, 5, 54, 19], [38, 23, 2, 61], [46, 5, 22, 19], [22, 49, 39, 2]]
tflag = ""
for i in range(0, 30, 3):
flag = [z3.BitVec("f" + str(i), 8) for i in range(3)]
slo = z3.Solver()
tmp = arr[i // 3]
a7 = flag[0] >> 7 & 1
a6 = flag[0] >> 6 & 1
a5 = flag[0] >> 5 & 1
a4 = flag[0] >> 4 & 1
a3 = flag[0] >> 3 & 1
a2 = flag[0] >> 2 & 1
a1 = flag[0] >> 1 & 1
a0 = flag[0] >> 0 & 1
slo.add(com(a7 ^ 1, a6 ^ 1, a5 ^ 1, a4 ^ 1, a3 ^ 1, a2 ^ 1) ^ tmp[0] == 0)
b7 = flag[1] >> 7 & 1
b6 = flag[1] >> 6 & 1
b5 = flag[1] >> 5 & 1
b4 = flag[1] >> 4 & 1
b3 = flag[1] >> 3 & 1
b2 = flag[1] >> 2 & 1
b1 = flag[1] >> 1 & 1
b0 = flag[1] >> 0 & 1
slo.add(com(a1 ^ 1, a0 ^ 1, b7 ^ 1, b6 ^ 1, b5 ^ 1, b4 ^ 1) ^ tmp[1] == 0)
c7 = flag[2 ] >> 7 & 1
c6 = flag[2 ] >> 6 & 1
c5 = flag[2 ] >> 5 & 1
c4 = flag[2] >> 4 & 1
c3 = flag[2] >> 3 & 1
c2 = flag[2] >> 2 & 1
c1 = flag[2] >> 1 & 1
c0 = flag[2] >> 0 & 1
slo.add(com(b3 ^ 1, b2 ^ 1, b1 ^ 1, b0 ^ 1, c7 ^ 1, c6 ^ 1) ^ tmp[2] == 0)
slo.add(com(c5 ^ 1, c4 ^ 1, c3 ^ 1, c2 ^ 1, c1 ^ 1, c0 ^ 1) ^ tmp[3] == 0)
# slo.add(122 >= flag[0])
# slo.add(122 >= flag[1])
# slo.add(122 >= flag[2])
assert slo.check() == z3.sat
solov = slo.model()
tflag += "".join([chr(solov.eval(flag[i]).as_long()) for i in range(3)])
print(tflag)
print(((0 << 2 | 0 << 1 | 1) << 3 | (1 << 2 | 0 << 1 | 1)))
com(1, 0, 1, 0, 0, 1)
# ((0 << 2 | 0 << 1 | 1) << 3 | (1 << 2 | 0 << 1 | 1))
真·最简单逆向
str = [118, 115, 133, 117, 134, 120, 173, 107, 151, 104,
152, 103, 100, 100, 98, 151, 104, 152, 107, 107,
150, 103, 98, 105, 149, 150, 101, 150, 106, 105,
105, 101, 102, 151, 104, 152, 106, 149, 104, 175]
for i in str:
print(chr(i-50),end="")
三段加密,以 _
分隔:
第一段是简单的字符 ASCII 变换;
第二段就是标准 sm4;
第三段是个迷宫;
# -*- coding:utf-8 -*-
import pysm4
flag1 = [i for i in "E0gy3"]
maps = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"
for i, v in enumerate(flag1):
if ord("A") <= ord(v) <= ord("Z"):
flag1[i] = chr(maps.index(v) + 0x41)
elif ord("a") <= ord(v) <= ord("z"):
flag1[i] = chr(maps.index(v) + 0x47)
else:pass
flag1 = "".join(flag1)
encdata = int.from_bytes(bytes.fromhex("F27352FB8DF41D6DC233B5A5EEC160DA"), 'big')
key = 0x0123456789abcdeffedcba9876543210
flag2 = pysm4.decrypt(encdata, key)
flag2 = flag2.to_bytes(16, 'big')
print(flag2)
arrs = [
[0x0077DF9E, 0x0077DFB1, 0x0077DF84, 0x0077DF84, 0x0077E2D6, 0x0077DF84, 0x0077DFAD],
[0x0077DF84, 0x0077DFB2, 0x0077DF84, 0x0077DF84, 0x0077DF84, 0x0077DF98, 0x0077DF84],
[0x0077DF84, 0x0077DFBC, 0x0077DF84, 0x0077DF84, 0x0077E06A, 0x0077DF84, 0x0077DF84],
[0x0077DF84, 0x0077DFE4, 0x0077E017, 0x0077E066, 0x0077E069, 0x0077DF84, 0x0077DF84],
[0x0077E06B, 0x0077DF84, 0x0077DF84, 0x0077DF84, 0x0077E0D9, 0x0077DF84, 0x0077DF84],
[0x0077DF84, 0x0077DF84, 0x0077DFE8, 0x0077DF84, 0x0077E1B5, 0x0077E21A, 0x0077E291],
[0x0077DF84, 0x0077DF84, 0x0077DF84, 0x0077DF84, 0x0077DF84, 0x0077DF84, 0x0077E36C]
]
mik = arrs.copy()
for i, v in enumerate(arrs):
for j, v2 in enumerate(v):
if v2 <= 0x0077DF9E:
mik[i][j] = 1
else:
mik[i][j] = 0
print(mik)
# [s, 0, 1, 1, 0, 1, 0],
# [1, 0, 1, 1, 1, 1, 1],
# [1, 0, 1, 1, 0, 1, 1],
# [1, 0, 0, 0, 0, 1, 1],
# [0, 1, 1, 1, 0, 1, 1],
# [1, 1, 0, 1, 0, 0, 0],
# [1, 1, 1, 1, 1, 1, e]
flag3 = ""
awsd = [1, 7, -1, -7]
adds = -127
for i in "dsssdddssdds":
if i == "d":
adds = awsd.index(1)
elif i == 'a':
adds = awsd.index(-1)
elif i == 's':
adds = awsd.index(7)
elif i == "w":
adds = awsd.index(-7)
flag3 += chr(adds + ord('a'))
print(flag3)
print(flag1+flag2+flag3)
# ZJCTF{R0tl3_Sm34@and_abbbaaabbaab}
有一说一,首尾都解出来了,中间有思路,但不会操作,纯手爆,笑死;
我的方便面没有调料包,
二等奖,
只能说很幸运,但也挺可惜的,跟上一个就差5分,就是手速慢了点,不然就是省一了;
不过我的重心还是在开发上,双修还是时间太少了,没法精通,又或者说关于安全的资源太少了,没有成型的体系,自己摸索太消耗时间了,到头来一事无成就得不偿失了,所以打 CTF 也就是图一乐吧;
衷心感谢我的队友 y3s
woodwhale
,么么哒!!!