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)

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐