2022年春秋杯 easy_python

分析

首先打开发现是一个txt的文本,把文本打开,发现这是一道python字节码到源码的逆向

3           0 LOAD_CONST               1 (204)
3 LOAD_CONST 2 (141)
6 LOAD_CONST 3 (44)
9 LOAD_CONST 4 (236)
12 LOAD_CONST 5 (111)
15 LOAD_CONST 6 (140)
18 LOAD_CONST 6 (140)
21 LOAD_CONST 7 (76)
24 LOAD_CONST 3 (44)
27 LOAD_CONST 8 (172)
30 LOAD_CONST 9 (7)
33 LOAD_CONST 9 (7)
36 LOAD_CONST 10 (39)
39 LOAD_CONST 11 (165)
42 LOAD_CONST 12 (70)
45 LOAD_CONST 9 (7)
48 LOAD_CONST 10 (39)
51 LOAD_CONST 13 (166)
54 LOAD_CONST 11 (165)
57 LOAD_CONST 14 (134)
60 LOAD_CONST 14 (134)
63 LOAD_CONST 6 (140)
66 LOAD_CONST 1 (204)
69 LOAD_CONST 11 (165)
72 LOAD_CONST 9 (7)
75 LOAD_CONST 10 (39)
78 LOAD_CONST 15 (230)
81 LOAD_CONST 6 (140)
84 LOAD_CONST 11 (165)
87 LOAD_CONST 12 (70)
90 LOAD_CONST 3 (44)
93 LOAD_CONST 8 (172)
96 LOAD_CONST 16 (102)
99 LOAD_CONST 17 (6)
102 LOAD_CONST 6 (140)
105 LOAD_CONST 1 (204)
108 LOAD_CONST 15 (230)
111 LOAD_CONST 15 (230)
114 LOAD_CONST 7 (76)
117 LOAD_CONST 18 (198)
120 LOAD_CONST 19 (38)
123 LOAD_CONST 20 (175)
126 BUILD_LIST 42
129 STORE_FAST 0 (flag)

4 132 SETUP_LOOP 54 (to 189)
135 LOAD_GLOBAL 0 (range)
138 LOAD_CONST 21 (42)
141 CALL_FUNCTION 1
144 GET_ITER
>> 145 FOR_ITER 40 (to 188)
148 STORE_FAST 1 (i)

5 151 LOAD_FAST 0 (flag)
154 LOAD_FAST 1 (i)
157 BINARY_SUBSCR
158 LOAD_CONST 22 (5)
161 BINARY_RSHIFT
162 LOAD_FAST 0 (flag)
165 LOAD_FAST 1 (i)
168 BINARY_SUBSCR
169 LOAD_CONST 23 (3)
172 BINARY_LSHIFT
173 BINARY_OR
174 LOAD_CONST 24 (255)
177 BINARY_AND
178 LOAD_FAST 0 (flag)
181 LOAD_FAST 1 (i)
184 STORE_SUBSCR
185 JUMP_ABSOLUTE 145
>> 188 POP_BLOCK
>> 189 LOAD_CONST 0 (None)
192 RETURN_VALUE

首先了解一下python字节码结构是什么:

源码行号 指令在函数中的偏移 指令符号 指令参数 实际参数值

(Ps:一般来说指令在函数中的偏移和指令参数在做题中都不是很重要可以忽略不管)

很容易看出本题是分为了3 4 5行这三个部分,这里逐一分析

第三行

这里有三个不同的指令符号

LOAD_CONST 加载常量,通常为整数值

BUILD_LIST 创建一个列表

STORE_FAST 一般用于保存值到局部变量

这里来分析第一个

3 0 LOAD_CONST 1 (204)

这个表示第一个元素204,以此类推这里存了

204, 141, 44, 236, 111, 140, 140, 76, 44, 172, 7, 7, 39, 165, 70, 7, 39, 166, 165, 134, 134, 140, 204, 165, 7, 39, 230, 140, 165, 70, 44, 172, 102, 6, 140, 204, 230, 230, 76, 198, 38, 175

然后看到 126 BUILD_LIST 42

创建一个长度为42的列表

129 STORE_FAST 0 (flag)

将前面的数据保存在flag中

第四行

image-20221225205007201

这里有五个不同的指令符号

SETUP_LOOP,用于开始循环,括号里的189表示循环退出点

LOAD_GLOBAL,用来加载全局变量,包括指定函数名,类名,模块名等全局符号

CALL_FUNCTION,用来表示前面加载全局变量的参数个数

GET_ITER,FOR_ITER ,获取参数,开始迭代。这两个不需要过多理解,属于for-in结构特有的,它们通常同时出现。

整体分析一下第四行就是

for i in range(42):

第五行

image-20221225205022756

这里出现的指令符号

BINARY_SUBSCR ,读取迭代器中某个下标的值

BINARY_RSHIFT,进行右移运算

BINARY_LSHIFT ,左移运算

BINARY_OR,或运算

BINARY_AND ,与运算

STORE_SUBSCR ,修改迭代器中某个下标的值

JUMP_ABSOLUTE ,回到循环起点

RETURN_VALUE ,函数结束标志

POP_BLOCK,特有的,通常和LOAD_CONST 0 (None)一起出现。

可以得出首先进行右移5,然后左移3再或运算为

(flag[i] >> 5) | (flag[i] << 3)

然后与上了255最后存入flag[i]中,然后整体为

flag[i] = (flag[i] >> 5) | (flag[i] << 3) & 255

脚本

flag = [204, 141, 44, 236, 111, 140, 140, 76, 44, 172, 7, 7, 39, 165, 70, 7, 39, 166, 165, 134, 134, 140, 204, 165, 7, 39, 230, 140, 165, 70, 44, 172, 102, 6, 140, 204, 230, 230, 76, 198, 38, 175]

for i in range(42):
flag[i] = (flag[i] >> 5) | (flag[i] << 3) & 255

flag1 = ''
for i in flag:
flag1 += chr(i)
print(flag1)
'''flag{ddbae889-2895-44df-897d-2ae30df77b61}'''