python 逆向浅析
1.编译过程简介python是一种解释性的语言,其原理是将代码块按照需求边运行边翻译给机器执行。python运行原理就是首先把python源文件解释成pyc二进制文件,然后再将pyc文件交由python虚拟机直接运行。但是有时候我们在运行的过程中并没有pyc文件。通常在默认编译的情况下,只有被调用库文件会生成pyc文件保证代码重用,主文件不会生成pyc文件。我们可以通过使用-m参数生成pyc文件2
1.编译过程简介
python是一种解释性的语言,其原理是将代码块按照需求边运行边翻译给机器执行。python运行原理就是首先把python源文件解释成pyc二进制文件,然后再将pyc文件交由python虚拟机直接运行。但是有时候我们在运行的过程中并没有pyc文件。通常在默认编译的情况下,只有被调用库文件会生成pyc文件保证代码重用,主文件不会生成pyc文件。我们可以通过使用-m参数生成pyc文件
2.python逆向
在这里我们不详述pyc文件格式。由于在pyc文件中对字节码做了处理。我们使用库对pyc文件逆向,代码如下
import dis
import marshal
f=open("printname.pyc","rb")
b_data=f.read()
f.close()
PyCodeObjectData=b_data[8:]
Pyobj=marshal.loads(PyCodeObjectData)
dis.dis(Pyobj)
其中dis是一个python分析二进制代码的一个重要的库。我们也可以时候dis库将源码换为字节码
import dis
def a(c,d):
return c+d
dis.dis(a)
3.使用一道题详解python字节码阅读
我们先看题目的字节码
Disassembly of a:
3 0 LOAD_CONST 1 (0)
2 BUILD_LIST 1
4 LOAD_GLOBAL 0 (len)
6 LOAD_FAST 0 (s)
8 CALL_FUNCTION 1
10 BINARY_MULTIPLY
12 STORE_FAST 1 (o)
4 14 LOAD_GLOBAL 1 (enumerate)
16 LOAD_FAST 0 (s)
18 CALL_FUNCTION 1
20 GET_ITER
>> 22 FOR_ITER 24 (to 48)
24 UNPACK_SEQUENCE 2
26 STORE_FAST 2 (i)
28 STORE_FAST 3 (c)
5 30 LOAD_FAST 3 (c)
32 LOAD_CONST 2 (2)
34 BINARY_MULTIPLY
36 LOAD_CONST 3 (60)
38 BINARY_SUBTRACT
40 LOAD_FAST 1 (o)
42 LOAD_FAST 2 (i)
44 STORE_SUBSCR
46 JUMP_ABSOLUTE 22
6 >> 48 LOAD_FAST 1 (o)
50 RETURN_VALUE
Disassembly of b:
9 0 LOAD_GLOBAL 0 (zip)
2 LOAD_FAST 0 (s)
4 LOAD_FAST 1 (t)
6 CALL_FUNCTION 2
8 GET_ITER
>> 10 FOR_ITER 22 (to 34)
12 UNPACK_SEQUENCE 2
14 STORE_FAST 2 (x)
16 STORE_FAST 3 (y)
10 18 LOAD_FAST 2 (x)
20 LOAD_FAST 3 (y)
22 BINARY_ADD
24 LOAD_CONST 1 (50)
26 BINARY_SUBTRACT
28 YIELD_VALUE
30 POP_TOP
32 JUMP_ABSOLUTE 10
>> 34 LOAD_CONST 0 (None)
36 RETURN_VALUE
Disassembly of c:
13 0 LOAD_CONST 1 (<code object <listcomp> at 0x7ff31a16f0e0, file "vuln.py", line 13>)
2 LOAD_CONST 2 ('c.<locals>.<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_FAST 0 (s)
8 GET_ITER
10 CALL_FUNCTION 1
12 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x7ff31a16f0e0, file "vuln.py", line 13>:
13 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 12 (to 18)
6 STORE_FAST 1 (c)
8 LOAD_FAST 1 (c)
10 LOAD_CONST 0 (5)
12 BINARY_ADD
14 LIST_APPEND 2
16 JUMP_ABSOLUTE 4
>> 18 RETURN_VALUE
Disassembly of e:
16 0 LOAD_CONST 1 (<code object <listcomp> at 0x7ff31a16f240, file "vuln.py", line 16>)
2 LOAD_CONST 2 ('e.<locals>.<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_FAST 0 (s)
8 GET_ITER
10 CALL_FUNCTION 1
12 STORE_FAST 0 (s)
17 14 LOAD_CONST 3 (<code object <listcomp> at 0x7ff31a16f2f0, file "vuln.py", line 17>)
16 LOAD_CONST 2 ('e.<locals>.<listcomp>')
18 MAKE_FUNCTION 0
20 LOAD_GLOBAL 0 (b)
22 LOAD_GLOBAL 1 (a)
24 LOAD_FAST 0 (s)
26 CALL_FUNCTION 1
28 LOAD_GLOBAL 2 (c)
30 LOAD_FAST 0 (s)
32 CALL_FUNCTION 1
34 CALL_FUNCTION 2
36 GET_ITER
38 CALL_FUNCTION 1
40 STORE_FAST 1 (o)
18 42 LOAD_GLOBAL 3 (bytes)
44 LOAD_FAST 1 (o)
46 CALL_FUNCTION 1
48 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x7ff31a16f240, file "vuln.py", line 16>:
16 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 12 (to 18)
6 STORE_FAST 1 (c)
8 LOAD_GLOBAL 0 (ord)
10 LOAD_FAST 1 (c)
12 CALL_FUNCTION 1
14 LIST_APPEND 2
16 JUMP_ABSOLUTE 4
>> 18 RETURN_VALUE
Disassembly of <code object <listcomp> at 0x7ff31a16f2f0, file "vuln.py", line 17>:
17 0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
>> 4 FOR_ITER 16 (to 22)
6 STORE_FAST 1 (c)
8 LOAD_FAST 1 (c)
10 LOAD_CONST 0 (5)
12 BINARY_XOR
14 LOAD_CONST 1 (30)
16 BINARY_SUBTRACT
18 LIST_APPEND 2
20 JUMP_ABSOLUTE 4
>> 22 RETURN_VALUE
Disassembly of main:
21 0 LOAD_GLOBAL 0 (input)
2 LOAD_CONST 1 ('Guess?')
4 CALL_FUNCTION 1
6 STORE_FAST 0 (s)
22 8 LOAD_CONST 2 (b'\xae\xc0\xa1\xab\xef\x15\xd8\xca\x18\xc6\xab\x17\x93\xa8\x11\xd7\x18\x15\xd7\x17\xbd\x9a\xc0\xe9\x93\x11\xa7\x04\xa1\x1c\x1c\xed')
10 STORE_FAST 1 (o)
23 12 LOAD_GLOBAL 1 (e)
14 LOAD_FAST 0 (s)
16 CALL_FUNCTION 1
18 LOAD_FAST 1 (o)
20 COMPARE_OP 2 (==)
22 POP_JUMP_IF_FALSE 34
24 24 LOAD_GLOBAL 2 (print)
26 LOAD_CONST 3 ('Correct!')
28 CALL_FUNCTION 1
30 POP_TOP
32 JUMP_FORWARD 8 (to 42)
26 >> 34 LOAD_GLOBAL 2 (print)
36 LOAD_CONST 4 ('Wrong...')
38 CALL_FUNCTION 1
40 POP_TOP
>> 42 LOAD_CONST 0 (None)
44 RETURN_VALUE
很明显主函数在结尾,所以我们倒着来看。先看主函数。首先最前面的数字应该是代码的行号,在同一数字之下的字节码应该是一行代码翻译出来的。然后在同一行字节码我们可以倒着看。看第21行,最后一行“STORE_FAST 0 (s)”表明结果是存放在临时变量s,向上看一行是“CALL_FUNCTION 1”表明调用了一个函数,函数有一个参数。再向上看一行“LOAD_CONST 1 ('Guess?')”表明函数参数是“Guess”。第一行“LOAD_GLOBAL 0 (input)”表明这里调用了一个函数input。综上所述这一句应该是s=input('Guess?')。第22行是将一串字符付给o,即o=b'\xae\xc0\xa1\xab\xef\x15\xd8\xca\x18\xc6\xab\x17\x93\xa8\x11\xd7\x18\x15\xd7\x17\xbd\x9a\xc0\xe9\x93\x11\xa7\x04\xa1\x1c\x1c\xed'。第23行里最后一句“POP_JUMP_IF_FALSE 34”表明这是一个条件语句,当条件成立时向下执行,条件不成立将调香34。这里的34不是行数,而是字节码之前的标记。倒着向上看首先是一个"==",再往上是一个变量o,在运算符的右侧,再往上是一个函数调用,且有一个参数——e(s)。所以这一句是if e(s)==0:。第24行最后一句是“JUMP_FORWARD 8 (to 42)”,就是if结束后跳到42处执行。剩下的可以看出这一句是print("Correct!")。第26行最后两行是“LOAD_CONST 0 (None)”和“RETURN_VALUE”。表明该函数return None,即不返回。之上可以易知是"else: print('Wrong...')"。所以mian函数反编译结果如下:
s=input("Guess!")
o=b'\xae\xc0\xa1\xab\xef\x15\xd8\xca\x18\xc6\xab\x17\x93\xa8\x11\xd7\x18\x15\xd7\x17\xbd\x9a\xc0\xe9\x93\x11\xa7\x04\xa1\x1c\x1c\xed'
if e(s)==o:
print('Correct!')
else:
print('Wrong...')
倒上去看函数e的第16行,首先我们发现结果存在s中,然后将一个函数带着参数s返回。结合看函数,发现这是做一个循环,将每一个元素c转换为ord(c)返回。所以我们猜测这里应该是s=[ord(c) for c in s]。第17行类似于上一行,不同的是函数里的参数,首先第一个函数b有两个参数,每个参数又在调用一个有一个参数s的函数a,c。所以这里应该是o=[(c^5)-30 for c in b(a(s),c(s))]。第27行是return bytes(o)。所以函数e的反编译结果如下
def e(s):
s=[ord(c) for c in s]
o=[(c^5)-30 for c in b(a(s),c(s))]
return bytes(o)
向上看c函数,根据前面所述,这里反汇编比较容易,,结果如下:
def c(s):
return [(c+5) for c in s]
再向上看b函数,先看第九行后几句“STORE_FAST 2 (x) STORE_FAST 3 (y) UNPACK_SEQUENCE 2“表明最后结果存在(x,y),向上看”FOR_ITER 22 (to 34)说明在22-34有一个循环。再向上看“LOAD_GLOBAL 0 (zip) LOAD_FAST 0 (s) LOAD_FAST 1 (t) CALL_FUNCTION 2 GET_ITER”说明从zip(s,t)中取元素,所以第9行的反汇编结果是“for (x,y) in zip(s,t):”在看第10行。先看最后两句,说明这个函数没有return返回。在向上看进入循环体——向上看”YIELD_VALUE",说明使用yield返回,之上是一个表达式——x+y-50。所以b函数逆向结果如下:
def b(s,t):
for (x,y) in zip(s,t):
yield x+y-50
看最后一个函数a,可以利用前面的经验,很轻松对函数a反汇编,结果如下:
def a(s):
o=[0]*len(s)
for i,c in enumerate(s):
o[i]=c*2-60
return o
所以完整的反汇编结果如下:
def a(s):
o=[0]*len(s)
for i,c in enumerate(s):
o[i]=c*2-60
return o
def b(s,t):
for (x,y) in zip(s,t):
yield x+y-50
def c(s):
return [(c+5) for c in s]
def e(s):
s=[ord(c) for c in s]
o=[(c^5)-30 for c in b(a(s),c(s))]
return bytes(o)
s=input("Guess!")
o=b'\xae\xc0\xa1\xab\xef\x15\xd8\xca\x18\xc6\xab\x17\x93\xa8\x11\xd7\x18\x15\xd7\x17\xbd\x9a\xc0\xe9\x93\x11\xa7\x04\xa1\x1c\x1c\xed'
if e(s)==o:
print('Correct!')
else:
print('Wrong...')
我们对该反汇编代码逆向,代码如下:
o=b'\xae\xc0\xa1\xab\xef\x15\xd8\xca\x18\xc6\xab\x17\x93\xa8\x11\xd7\x18\x15\xd7\x17\xbd\x9a\xc0\xe9\x93\x11\xa7\x04\xa1\x1c\x1c\xed'
ll=[]
for i in o:
ll.append((((int(i.encode("hex"),16)+30)^5)+50+55)//3)
m=""
for ii in ll:
m=m+chr(ii)
print(m)
更多推荐
所有评论(0)