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 的内部工作原理。