死磕python字节码-⼿⼯还原python源码
0x1.前⾔
Python 代码先被编译为字节码后,再由Python虚拟机来执⾏字节码, Python的字节码是⼀种类似汇编指令的中间语⾔, ⼀个Python语句会对应若⼲字节码指令,虚拟机⼀条⼀条执⾏字节码指令, 从⽽完成程序执⾏。
Python dis 模块⽀持对Python代码进⾏反汇编, ⽣成字节码指令。
dis.dis()将CPython字节码转为可读的伪代码(类似于汇编代码)。结构如下:
7          0 LOAD_CONST              1 (0)
3 STORE_FAST              1 (local1)
8          6 LOAD_CONST              2 (101)
9 STORE_GLOBAL            0 (global1)
9          12 LOAD_FAST                1 (local1)
重新启动linux系统命令
15 PRINT_ITEM
16 LOAD_FAST                0 (arg1)
19 PRINT_ITEM
20 LOAD_GLOBAL              0 (global1)
23 PRINT_ITEM
24 PRINT_NEWLINE
25 LOAD_CONST              0 (None)
28 RETURN_VALUE
其实就是这样的结构:
python基础代码实例
源码⾏号 | 指令在函数中的偏移 | 指令符号 | 指令参数 | 实际参数值
0x2.变量
LOAD_CONST加载const变量,⽐如数值、字符串等等,⼀般⽤于传给函数的参数
55      12 LOAD_GLOBAL              1 (test)
15 LOAD_FAST                0 (2) #读取2
18 LOAD_CONST              1 ('output')
21 CALL_FUNCTION            2
转为python代码就是:
test(2, 'output')
2.局部变量
LOAD_FAST⼀般加载局部变量的值,也就是读取值,⽤于计算或者函数调⽤传参等。
STORE_FAST⼀般⽤于保存值到局部变量。
61          77 LOAD_FAST                0 (n)
80 LOAD_FAST                3 (p)
83 INPLACE_DIVIDE
84 STORE_FAST              0 (n)
这段bytecode转为python就是:
n = n / p
函数的形参也是局部变量,如何区分出是函数形参还是其他局部变量呢?
形参没有初始化,也就是从函数开始到LOAD_FAST该变量的位置,如果没有看到STORE_FAST,那么该变量就是函数形参。
⽽其他局部变量在使⽤之前肯定会使⽤STORE_FAST进⾏初始化。
具体看下⾯的实例:
4          0 LOAD_CONST              1 (0)
3 STORE_FAST              1 (local1)
5          6 LOAD_FAST                1 (local1)
9 PRINT_ITEM
10 LOAD_FAST                0 (arg1)
13 PRINT_ITEM
14 PRINT_NEWLINE
15 LOAD_CONST              0 (None)
18 RETURN_VALUE
对应的python代码如下,对⽐⼀下就⼀⽬了然。
def test(arg1):
local1 = 0
print local1, arg1
3.全局变量
LOAD_GLOBAL⽤来加载全局变量,包括指定函数名,类名,模块名等全局符号。STORE_GLOBAL⽤来给全局变量赋值。
8          6 LOAD_CONST              2 (101)
9 STORE_GLOBAL            0 (global1)
20 LOAD_GLOBAL              0 (global1)
23 PRINT_ITEM
对应的python代码
def test():
global global1
global1 = 101
print global
0x3.常⽤数据类型
1.list
BUILD_LIST⽤于创建⼀个list结构。
13          0 LOAD_CONST              1 (1)
3 LOAD_CONST              2 (2)
6 BUILD_LIST              2
9 STORE_FAST              0 (k)
对应python代码是:
k = [1, 2]
另外再看看⼀种常见的创建list的⽅式如下:
[x for x in xlist if x!=0 ]
⼀个实例bytecode如下:
22        235 BUILD_LIST              0 //创建list,为赋值给某变量,这种时候⼀般都是语法糖结构了
238 LOAD_FAST                3 (sieve)
241 GET_ITER
>>  242 FOR_ITER                24 (to 269)
245 STORE_FAST              4 (x)
248 LOAD_FAST                4 (x)
251 LOAD_CONST              2 (0)
254 COMPARE_OP              3 (!=)
257 POP_JUMP_IF_FALSE      242 //不满⾜条件contine
260 LOAD_FAST                4 (x)//读取满⾜条件的x
263 LIST_APPEND              2 //把每个满⾜条件的x存⼊list
266 JUMP_ABSOLUTE          242
>>  269 RETURN_VALUE
转为python代码是:
[for x in sieve if x != 0]
2.dict
table manners in france
BUILD_MAP⽤于创建⼀个空的dict。STORE_MAP⽤于初始化dict的内容。
13          0 BUILD_MAP                1
3 LOAD_CONST              1 (1)
6 LOAD_CONST              2 ('a')
9 STORE_MAP
10 STORE_FAST              0 (k)
对应的python代码是:
k = {'a': 1}
再看看修改dict的bytecode:
14          13 LOAD_CONST              3 (2)
16 LOAD_FAST                0 (k)
19 LOAD_CONST              4 ('b')
22 STORE_SUBSCR
对应的python代码是:
k['b'] = 2
3.slice
BUILD_SLICE⽤于创建slice。对于list、元组、字符串都可以使⽤slice的⽅式进⾏访问。
但是要注意BUILD_SLICE⽤于[x:y:z]这种类型的slice,结合BINARY_SUBSCR读取slice的值,结合STORE_SUBSCR⽤于修改slice的值。另外SLICE+n⽤于[a:b]类型的访问,STORE_SLICE+n⽤于[a:b]类型的修改,其中n表⽰如下:
SLICE+0()
Implements TOS = TOS[:].
SLICE+1()
Implements TOS = TOS1[TOS:].
SLICE+2()
Implements TOS = TOS1[:TOS].
SLICE+3()
Implements TOS = TOS2[TOS1:TOS].
tablefield下⾯看具体实例:
13          0 LOAD_CONST              1 (1)
3 LOAD_CONST              2 (2)
6 LOAD_CONST              3 (3)
9 BUILD_LIST              3
12 STORE_FAST              0 (k1) //k1 = [1, 2, 3]
14          15 LOAD_CONST              4 (10)
18 BUILD_LIST              1
21 LOAD_FAST                0 (k1)
24 LOAD_CONST              5 (0)
27 LOAD_CONST              1 (1)
30 LOAD_CONST              1 (1)
33 BUILD_SLICE              3
36 STORE_SUBSCR                    //k1[0:1:1] = [10]
15          37 LOAD_CONST              6 (11)
40 BUILD_LIST              1
43 LOAD_FAST                0 (k1)
46 LOAD_CONST              1 (1)
49 LOAD_CONST              2 (2)
52 STORE_SLICE+3                  //k1[1:2] = [11]
16          53 LOAD_FAST                0 (k1)
56 LOAD_CONST              1 (1)
59 LOAD_CONST              2 (2)
62 SLICE+3
63 STORE_FAST              1 (a)  //a = k1[1:2]
17          66 LOAD_FAST                0 (k1)
69 LOAD_CONST              5 (0)
72 LOAD_CONST              1 (1)
75 LOAD_CONST              1 (1)
78 BUILD_SLICE              3
81 BINARY_SUBSCR
82 STORE_FAST              2 (b) //b = k1[0:1:1]
0x4.循环
SETUP_LOOP⽤于开始⼀个循环。SETUP_LOOP 26 (to 35)中35表⽰循环退出点。while循环
23          0 LOAD_CONST              1 (0)
3 STORE_FAST              0 (i) // i=0
24          6 SETUP_LOOP              26 (to 35)
>>    9 LOAD_FAST                0 (i) //循环起点
12 LOAD_CONST              2 (10)
15 COMPARE_OP              0 (<)
18 POP_JUMP_IF_FALSE      34    //while i < 10:
25          21 LOAD_FAST                0 (i)
24 LOAD_CONST              3 (1)linux下载telnet命令
27 INPLACE_ADD
28 STORE_FAST              0 (i) // i += 1
31 JUMP_ABSOLUTE            9    // 回到循环起点
>>  34 POP_BLOCK
>>  35 LOAD_CONST              0 (None)
对应python代码是:
i = 0
while i < 10:
i += 1
for in结构
238 LOAD_FAST                3 (sieve)#sieve是个list
241 GET_ITER                    //开始迭代sieve
>>  242 FOR_ITER                24 (to 269) //继续iter下⼀个x
245 STORE_FAST              4 (x)
...
266 JUMP_ABSOLUTE          242 //循环
这是典型的for+in结构,转为python代码就是:
for x in sieve:
0x5.if
POP_JUMP_IF_FALSE和JUMP_FORWARD⼀般⽤于分⽀判断跳转。POP_JUMP_IF_FALSE表⽰条件结果为FALSE就跳转到⽬标偏移指令。JUMP_FORWARD直接跳转到⽬标偏移指令。
23          0 LOAD_CONST              1 (0)
3 STORE_FAST              0 (i) //i=0
24          6 LOAD_FAST                0 (i)
9 LOAD_CONST              2 (5)
12 COMPARE_OP              0 (<)
15 POP_JUMP_IF_FALSE      26
25          18 LOAD_CONST              3 ('i < 5')
21 PRINT_ITEM
22 PRINT_NEWLINE
23 JUMP_FORWARD            25 (to 51)
26    >>  26 LOAD_FAST                0 (i)
29 LOAD_CONST              2 (5)
32 COMPARE_OP              4 (>)
35 POP_JUMP_IF_FALSE      46
站长统计27          38 LOAD_CONST              4 ('i > 5')
41 PRINT_ITEM
42 PRINT_NEWLINE
43 JUMP_FORWARD            5 (to 51)
29    >>  46 LOAD_CONST              5 ('i = 5')
49 PRINT_ITEM
50 PRINT_NEWLINE
>>  51 LOAD_CONST              0 (None)
转为python代码是:
i = 0
if i < 5:
print 'i < 5'
elif i > 5:
print 'i > 5'
else:
print 'i = 5'
0x6.分辨函数
1.函数范围
前⾯介绍第⼆列表⽰指令在函数中的偏移地址,所以看到0就是函数开始,下⼀个0前⼀条指令就是函数结束位置,当然也可以通
过RETURN_VALUE来确定函数结尾

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。