PVM字节码
在前面的章节我们提到过,python的运行是将pvm可以识别的字节码进行运行,那么什么是pvm和字节码呢?
PVM
PVM就像一个专门设计用来运行字节码的特殊引擎(有java逆向基础的师傅们可以参考JVM)。它逐条读取字节码指令并执行,从而使你的Python程序运行起来。你可以理解为他是一个为了解决迁移问题的小虚拟机。
字节码
如果把PVM比作虚拟机的话,那么字节码就是汇编了,简而言之,编译过程将你可读的代码转换为PVM能够理解和更高效执行的形式。
查看字节码
Python 提供了一个强大的工具,称为 dis
模块,该模块允许您反汇编 Python 函数或甚至整个脚本,转化为字节码。
我们写这样一段代码
def dDostalker():
print('written by dDostalker')
要查看此函数的字节码,我们使用 dis.dis()
函数:
import dis
dis.dis(dDostalker)
之后便打印出我们想要的字节码了
3 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('written by dDostalker')
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
常见字节码指令
LOAD_CONST
:将一个常量值(如数字、字符串或None
)加载到栈顶。例如,LOAD_CONST 1 ('Hello, ')
将字符串“Hello, ”加载到栈中。LOAD_FAST
:将局部变量的值加载到栈中。示例:LOAD_FAST 0 (x)
将局部变量x
的值加载到栈中。STORE_FAST
:将栈顶的值取出并存储到局部变量中。例如,STORE_FAST 1 (y)
将栈顶的值存储到变量y
中。BINARY_ADD
:从栈中取出顶部的两个值,将它们相加,并将结果推回栈中。例如,在指令序列LOAD_FAST 0 (x)
、LOAD_CONST 1 (5)
、BINARY_ADD
中,x
和 5 的值被相加,结果被放置在栈上。POP_TOP
:从栈中移除顶部的值,有效地丢弃它。RETURN_VALUE
:返回栈顶的值,有效地结束函数的执行。JUMP_IF_FALSE_OR_POP
:如果栈顶的值为假,则此指令跳转到指定的指令。否则,它从栈中弹出该值。JUMP_ABSOLUTE
:无条件跳转到指定的指令。
基本 Python 结构的字节码示例
让我们看看这些指令在基本 Python 结构中的使用:
条件语句(If-Else)
def check_positive(x):
if x > 0:
return "Positive"
else:
return "Non-positive"
字节码:
2 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (0)
4 COMPARE_OP 4 (>)
6 POP_JUMP_IF_FALSE 14
3 8 LOAD_CONST 2 ('Positive')
10 RETURN_VALUE
5 >> 12 LOAD_CONST 3 ('Non-positive')
14 RETURN_VALUE
在上面的字节码中:
LOAD_FAST 0 (x)
:将变量x
加载到栈上。LOAD_CONST 1 (0)
:将常量0
加载到栈上。COMPARE_OP 4 (>)
:比较栈顶的两个值(x > 0
)。POP_JUMP_IF_FALSE 14
:如果比较结果为假,则跳转到指令 14。LOAD_CONST 2 ('Positive')
:如果x > 0
,则将字符串'Positive'
加载到栈上。RETURN_VALUE
:返回栈上的值。LOAD_CONST 3 ('Non-positive')
:如果x <= 0
,则将字符串'Non-positive'
加载到栈上。
循环(For 循环)
def sum_list(numbers):
total = 0
for num in numbers:
total += num
return total
字节码:
2 0 LOAD_CONST 1 (0)
2 STORE_FAST 1 (total)
3 4 LOAD_FAST 0 (numbers)
6 GET_ITER
>> 8 FOR_ITER 12 (到 22)
10 STORE_FAST 2 (num)
4 12 LOAD_FAST 1 (total)
14 LOAD_FAST 2 (num)
16 INPLACE_ADD
18 STORE_FAST 1 (total)
20 JUMP_ABSOLUTE 8
>> 22 LOAD_FAST 1 (total)
24 RETURN_VALUE
现在,让我们来探讨一下字节码中发生了什么:
LOAD_CONST 1 (0)
:将常量0
加载到栈上,以初始化total
。STORE_FAST 1 (total)
:将0
存储在变量total
中。LOAD_FAST 0 (numbers)
:将变量numbers
加载到栈上。GET_ITER
:获取numbers
的迭代器。FOR_ITER 12 (to 22)
:遍历numbers
,完成后跳转到指令 22。STORE_FAST 2 (num)
:将当前项存储在变量num
中。LOAD_FAST 1 (total)
:将total
加载到栈上。LOAD_FAST 2 (num)
:将num
加载到栈上。INPLACE_ADD
:将total
和num
相加(就地操作)。STORE_FAST 1 (total)
:将结果存储回total
中。JUMP_ABSOLUTE 8
:跳回循环的开始处。LOAD_FAST 1 (total)
:将total
加载到栈上。RETURN_VALUE
:返回total
。
理解这些常见指令及其在不同 Python 结构中的使用,可以显著增强您分析字节码的能力,并深入了解 Python 的内部工作原理。