HGAME2019-python-bytecode

题目给了一个pyc文件,但是不能用uncompyle2反编译成py文件。很难受,给了hint:bytecode。网上🏄发现了python自带的dis模块,可以解析一下pyc文件。

1
2
3
4
5
6
7
import dis
import marshal
f=open("1.pyc",'rb')
magic=f.read(4)
mtime=f.read(4)
code=marshal.load(f)
dis.dis(code)

然而什么都不做直接使用dis会出现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  2           0 JUMP_ABSOLUTE            3
>> 3 JUMP_ABSOLUTE 9
6 LOAD_CONST 15 ("You're Wrong! ")
>> 9 JUMP_ABSOLUTE 14

3 12 PRINT_ITEM
13 LOAD_CONST 100
Traceback (most recent call last):
File "2.py", line 6, in <module>
dis.dis(code)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/dis.py", line 43, in dis
disassemble(x)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/dis.py", line 95, in disassemble
print '(' + repr(co.co_consts[oparg]) + ')',
IndexError: tuple index out of range

继续网上🏄,了解到结合上下文修改出错的地方的字节码就OK了。没有头绪地弄了半天,最终改掉了LOAD_CONST 100中的100,十六进制表示为0x64,改成00就可以先凑合看着了。

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
 2           0 JUMP_ABSOLUTE            3
>> 3 JUMP_ABSOLUTE 9
6 LOAD_CONST 15 ("You're Wrong! ")
>> 9 JUMP_ABSOLUTE 14

3 12 PRINT_ITEM
13 PRINT_NEWLINE
>> 14 LOAD_CONST 0 (-1)
17 LOAD_CONST 1 (None)
20 IMPORT_NAME 0 (string)
23 STORE_NAME 0 (string)
26 LOAD_NAME 1 (list)
29 LOAD_NAME 0 (string)
32 LOAD_ATTR 2 (letters)
35 CALL_FUNCTION 1
38 LOAD_NAME 1 (list)
41 LOAD_NAME 0 (string)
44 LOAD_ATTR 3 (digits)
47 CALL_FUNCTION 1

5 50 BINARY_ADD
51 LOAD_CONST 2 ('+')
54 LOAD_CONST 3 ('/')
57 BUILD_LIST 2
60 BINARY_ADD
61 STORE_NAME 2 (letters)
64 LOAD_CONST 4 ('FcjTCgD1EffEm2rPC3bTyL5Wu2bKBI9KAZrwFgrUygHN')
67 STORE_NAME 4 (dec)

35 70 LOAD_CONST 5 (<code object encode at 0x101a328b0, file "third.py", line 7>)
73 MAKE_FUNCTION 0
76 STORE_NAME 5 (encode)
79 LOAD_CONST 6 ("Welcome to Processor's Python Classroom Part 3&4!\n")
82 PRINT_ITEM
83 PRINT_NEWLINE
84 LOAD_CONST 7 ('qi shi wo jiu shi lan cai ba liang dao ti fang zai yi qi.')
87 PRINT_ITEM
88 PRINT_NEWLINE
89 LOAD_CONST 8 ("Now let's start the origin of Python!\n")
92 PRINT_ITEM
93 PRINT_NEWLINE

40 94 LOAD_CONST 9 ('Plz Input Your Flag:\n')
97 PRINT_ITEM
98 PRINT_NEWLINE
99 LOAD_NAME 6 (raw_input)
102 CALL_FUNCTION 0
105 STORE_NAME 7 (enc)
108 LOAD_NAME 1 (list)
111 LOAD_NAME 7 (enc)
114 CALL_FUNCTION 1
117 STORE_NAME 8 (lst)
120 LOAD_NAME 8 (lst)
123 LOAD_ATTR 9 (reverse)
126 CALL_FUNCTION 0
129 POP_TOP
130 LOAD_NAME 10 (len)
133 LOAD_NAME 8 (lst)
136 CALL_FUNCTION 1
139 STORE_NAME 11 (llen)
142 SETUP_LOOP 99 (to 244)
145 LOAD_NAME 12 (range)
148 LOAD_NAME 11 (llen)
151 CALL_FUNCTION 1
154 GET_ITER
155 FOR_ITER 85 (to 243)
158 STORE_NAME 13 (i)
161 LOAD_NAME 13 (i)
164 LOAD_CONST 10 (2)
167 BINARY_MODULO
168 LOAD_CONST 11 (0)
171 COMPARE_OP 2 (==)
174 POP_JUMP_IF_FALSE 196
177 LOAD_NAME 14 (chr)
180 LOAD_NAME 15 (ord)
183 LOAD_NAME 8 (lst)
186 LOAD_NAME 13 (i)
189 BINARY_SUBSCR
190 CALL_FUNCTION 1
193 LOAD_CONST 10 (2)

46 >> 196 BINARY_SUBTRACT
197 CALL_FUNCTION 1
200 LOAD_NAME 8 (lst)
203 LOAD_NAME 13 (i)
206 STORE_SUBSCR
207 JUMP_FORWARD 0 (to 210)
>> 210 LOAD_NAME 14 (chr)
213 LOAD_NAME 15 (ord)
216 LOAD_NAME 8 (lst)
219 LOAD_NAME 13 (i)
222 BINARY_SUBSCR
223 CALL_FUNCTION 1
226 LOAD_CONST 12 (1)
229 BINARY_ADD

48 230 CALL_FUNCTION 1
233 LOAD_NAME 8 (lst)

49 236 LOAD_NAME 13 (i)
239 STORE_SUBSCR
240 JUMP_ABSOLUTE 141
>> 243 POP_BLOCK
>> 244 LOAD_CONST 13 ('')
247 STORE_NAME 16 (enc2)
250 LOAD_NAME 16 (enc2)
253 LOAD_ATTR 17 (join)
256 LOAD_NAME 8 (lst)
259 CALL_FUNCTION 1
262 STORE_NAME 16 (enc2)
265 LOAD_NAME 5 (encode)
268 LOAD_NAME 16 (enc2)
271 CALL_FUNCTION 1
274 STORE_NAME 18 (enc3)
277 LOAD_NAME 18 (enc3)
280 LOAD_NAME 4 (dec)

55 >> 283 COMPARE_OP 2 (==)
286 POP_JUMP_IF_FALSE 283
289 LOAD_CONST 14 ("You're right! ")
292 PRINT_ITEM
293 PRINT_NEWLINE
294 JUMP_FORWARD 5 (to 302)
297 LOAD_CONST 15 ("You're Wrong! ")
300 PRINT_ITEM
301 PRINT_NEWLINE
>> 302 LOAD_CONST 1 (None)
305 RETURN_VALUE

还是比较好看的,只是一开始把MAKE_FUNCTION 0当成了定义一个无参函数,造成了很多不必要的麻烦。后来通过自己写py然后编译pyc然后用dis看的方法,大致是还原出了py源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import string
letters=list(string.letters)+list(string.digits)+['+','/']
dec="FcjTCgD1EffEm2rPC3bTyL5Wu2bKBI9KAZrwFgrUygHN"
def encode(str):
'换表base64'
return str
enc=raw_input()
lst=list(enc)
lst.reverse()
for i in range(len(lst)):
if i % 2 == 0:
lst[i]=chr(ord(lst[i])-1)
continue
lst[i]=chr(ord(lst[i])+1)
enc2=''
enc2=enc2.join(lst)
enc3=encode(enc2)
if enc3==dec:
print "You're right! "
else:
print "You're Wrong! "
print letters

虽然定义的函数的内容dis模块是不会解析出来的,但是看见熟悉的string.letters+string.digits+'+'+'/',我不经意间想起了base64,只是换了个表而已。dec字符串中的大小写互换,然后base64解密,之后通过逆运算得到flag。

逆运算脚本(换表base64就不自己手写了):

1
2
3
4
5
6
7
8
9
dec='|"mpguxQ^3dispmb^pS`dn/dk4V|dn`hg'
lst=list(dec)
for i in range(len(lst)):
if i % 2 == 0:
lst[i]=chr(ord(lst[i])+1)
if i % 2 == 1:
lst[i]=chr(ord(lst[i])-1)
lst.reverse()
print ''.join(lst)
文章目录
|