python逆向
python
python是一门解释性的语言
源代码—>中间代码—>机器语言
编译器先将源代码编译成虚拟机指令,再由解释器对中间代码进行解释。
python有两种常见的解释器:
CPython:C语言开发,使用最广,默认的解释器
PyPy:采用JIT技术,对python代码进行动态编译,追求执行速度
Python 字节码与字节码混淆 - 简书 (jianshu.com)关于python字节码的知识
深入理解python之Opcode备忘录 - 简书 (jianshu.com)这个博客里有关于字节码指令的详细解释
pyc文件
pyc是编译py后生成的字节码文件,可以用来隐藏源代码。pyc内容跟python
版本有关。
不同版本的python可能运行不了同一个pyc文件。
1 | python -m py_compile test.py |
学习python字节码学习搭配chatGPT,能提高学习效率
CPython使用基于堆栈的虚拟机
在执行python文件时候,第一步: python解释器会将你写的python代码先编译为字节码
第二步: 当你每一次调用函数,或者刚开始运行python的时候,cpython会建立一个新的Frame,然后在这个Frame框架下,cpython会一条一条的执行编译后的ByteCode, 每一条ByteCode在C语言中有相应的代码去执行它。
另外,在每一个Frame里, cpython都会维护一个stack,然后ByteCode会和这个Stack进行交互操作。
python字节码官方文档dis —- Python 字节码反汇编器 — Python 3.12.0 文档
变量指令解析
1 | 源码行号 | 指令偏移量 | 指令符号 | 指令参数 | 实际参数值 |
一下解析均来自chatGPT
FAST
LOAD_FAST
LOAD_FAST 是 Python 字节码的一种指令,用于加载局部变量的值。具体来说,它从当前函数的局部变量数组中加载指定名称的变量的值,并将其推送到堆栈上。
以下是关于 LOAD_FAST 指令的一些细节:
- 指令名称:
LOAD_FAST - Opcode:
0x7C - 参数: 一个整数索引,表示局部变量在局部变量数组中的位置
LOAD_FAST 的作用是加载函数的局部变量的值。局部变量是在函数中定义的变量,只在函数的范围内可见。
GLOBAL
LOAD_GLOBAL
LOAD_GLOBAL 是 Python 字节码的一种指令,用于加载全局变量的值。具体来说,它从当前模块的全局命名空间中加载指定名称的变量的值,并将其推送到堆栈上。
以下是关于 LOAD_GLOBAL 指令的一些细节:
- 指令名称:
LOAD_GLOBAL - Opcode:
0x74 - 参数: 一个整数索引,表示在常量池中的全局变量名称的位置
LOAD_GLOBAL 的作用是加载全局变量的值。全局变量是在模块级别定义的变量,可以在整个模块内被访问。
STORE_NAME
STORE_NAME 是 Python 字节码的一种指令,用于将值存储到局部变量的名称中。具体来说,它从堆栈中弹出一个值,然后将该值存储到当前作用域中具有指定名称的变量中。
以下是关于 STORE_NAME 指令的一些细节:
- 指令名称:
STORE_NAME - Opcode:
0x5 - 参数: 一个表示变量名称的字符串(变量名)
STORE_NAME 的作用是将堆栈顶部的值存储到局部变量中,变量的名称由参数指定。这通常是在赋值操作时使用的指令。
LIST
BUILD_LIST
BUILD_LIST 是 Python 字节码的一种指令,用于创建一个列表对象。具体来说,它从堆栈上弹出指定数量的元素,然后创建一个新的列表并将这些元素放入列表中,最后将列表对象推送回堆栈。
以下是关于 BUILD_LIST 指令的一些细节:
- 指令名称:
BUILD_LIST - Opcode:
0x14 - 参数: 一个整数,表示要从堆栈中弹出的元素数量
BUILD_LIST 的作用是根据堆栈上的元素创建一个新的列表对象。
dict
BUILD_MAP
BUILD_MAP 是 Python 字节码的一种指令,用于创建一个字典对象。具体来说,它从堆栈上弹出两倍于指定元素数量的元素,然后创建一个新的字典并将这些元素添加到字典中,最后将字典对象推送回堆栈。
以下是关于 BUILD_MAP 指令的一些细节:
- 指令名称:
BUILD_MAP - Opcode:
0x15 - 参数: 一个整数,表示要从堆栈中弹出的键值对数量(通常是键值对的数量的两倍)
BUILD_MAP 的作用是根据堆栈上的键值对创建一个新的字典对象。
LOOP
SETUP_LOOP
SETUP_LOOP 是 Python 字节码的一种指令,用于在循环开始的位置设置循环的头部。它指定了循环体的开始位置.
以下是关于 SETUP_LOOP 指令的一些细节:
- 指令名称:
SETUP_LOOP - Opcode:
0x13 - 参数: 两个字节的有符号偏移量(表示循环体的结束位置)
SETUP_LOOP 的作用是标记循环的开始位置,并在堆栈上推送一个块堆栈(block stack)记录循环的开始位置和结束位置。当循环体结束时,POP_BLOCK 指令将从块堆栈中弹出该记录。
示例:
1 | for i in range(5): |
对应的字节码可能包含:
1 | 0 SETUP_LOOP 28 # 设置循环的头部,指定循环体的结束位置 |
在这个例子中,SETUP_LOOP 用于设置循环的头部,POP_BLOCK 将在循环体结束时弹出块堆栈。循环体的开始和结束位置由 FOR_ITER 和 POP_BLOCK 来标记。
POP_BLOCK
POP_BLOCK 是 Python 字节码指令之一,用于在异常处理中管理块的结束。具体来说,它用于弹出当前块的信息,以便程序继续正常执行而不是执行异常处理。在 CPython 的实现中,这通常与异常处理语句(try, except, finally)一起使用。
在 Python 字节码中,POP_BLOCK 通常出现在异常处理块的末尾,它会将与该块相关的信息从块堆栈中弹出。
例如,下面是一个简单的 Python 代码片段和对应的字节码:
Python 代码片段:
1 | pythonCopy codetry: |
对应的字节码:
1 | scssCopy code 1 0 SETUP_EXCEPT 16 (to 19) |
在上述字节码中,POP_BLOCK 出现在 POP_EXCEPT 和 END_FINALLY 之后,用于清理块堆栈。这样,程序可以继续执行正常的代码,而不是进入异常处理块。
BINARY_SUBSCR
BINARY_SUBSCR 是 Python 字节码的一种指令,它用于在可迭代对象(如列表、元组、字典等)上执行下标(索引)操作。具体来说,它从堆栈中弹出两个值,一个是容器对象,另一个是索引值,然后将容器对象中指定索引的元素推送回堆栈。
下面是关于 BINARY_SUBSCR 指令的一些细节:
- 指令名称:
BINARY_SUBSCR - Opcode:
0x25 - 参数: 无
- 作用: 从容器对象中获取指定索引的元素,并将其推送到堆栈上。
STORE_SUBSCR
STORE_SUBSCR 是 Python 字节码的一种指令,用于在容器对象(如列表、字典等)中的指定索引位置存储一个值。具体来说,它从堆栈中弹出三个值:容器对象、索引值和要存储的值,然后将值存储到容器对象的指定索引位置。
以下是关于 STORE_SUBSCR 指令的一些细节:
- 指令名称:
STORE_SUBSCR - Opcode:
0x3A - 参数: 无
- 作用: 从堆栈中弹出容器对象、索引值和值,将值存储到容器对象的指定索引位置。
这个指令通常在对列表、字典等进行索引赋值操作时使用。例如,如果有一个列表 my_list,栈顶有一个整数表示索引,而下面的两个值分别是容器对象和要存储的值,STORE_SUBSCR 将在列表的指定索引位置存储新的值。
跳转指令
POP_JUMP_IF_FALSE
POP_JUMP_IF_FALSE 是 Python 字节码的一种条件跳转指令。它用于在堆栈上弹出一个值,并在该值为假(False)时执行跳转。如果弹出的值为假,指令将跳转到指定的目标地址,否则继续执行下一条指令。
以下是关于 POP_JUMP_IF_FALSE 指令的一些细节:
- 指令名称:
POP_JUMP_IF_FALSE - Opcode:
0x72 - 参数: 两个字节的有符号偏移量(表示跳转目标的相对位置)
POP_JUMP_IF_FALSE 的作用是从堆栈中弹出一个值,如果该值为假(False),则执行跳转到指定的目标地址。否则,继续执行下一条指令。
示例:
1 | x = False |
上述代码的字节码可能包含类似下面的部分:
1 | 2 0 LOAD_GLOBAL 0 (x) |
在这个例子中,POP_JUMP_IF_FALSE 指令用于检查变量 x 的真假,并根据结果选择性地执行不同的分支。
JUMP_FORWARD
JUMP_FORWARD 是 Python 字节码的一种无条件跳转指令。它用于在执行过程中无条件地跳转到指定的相对位置。
以下是关于 JUMP_FORWARD 指令的一些细节:
- 指令名称:
JUMP_FORWARD - Opcode:
0x28 - 参数: 两个字节的有符号偏移量(表示跳转的相对位置)
JUMP_FORWARD 的作用是使程序跳转到当前位置之后的指定位置。它通常用于实现循环、条件语句等结构,其中需要在不同条件下跳转到不同的代码块。
示例:
1 | x = True |
上述代码的字节码可能包含类似下面的部分:
1 | 2 0 LOAD_GLOBAL 0 (x) |
在这个例子中,JUMP_FORWARD 用于在条件判断为真时跳过 else 语句块。在字节码中,JUMP_FORWARD 的目标是跳过指定数量的字节码指令,从而实现程序的流程控制。
JUMP_ABSOLUTE
JUMP_ABSOLUTE 是 Python 字节码的一种无条件跳转指令。它用于无条件地跳转到指定的绝对位置。
以下是关于 JUMP_ABSOLUTE 指令的一些细节:
- 指令名称:
JUMP_ABSOLUTE - Opcode:
0x71 - 参数: 两个字节的有符号偏移量(表示跳转的绝对位置)
JUMP_ABSOLUTE 的作用是使程序跳转到指定的绝对位置。这个位置通常是由 bytecode 的相对位置计算得到的,而不是 Python 源代码中的行号。
示例:
1 | x = 5 |
对应的字节码可能包含类似下面的部分:
1 | 2 0 LOAD_NAME 0 (x) |
在这个例子中,JUMP_ABSOLUTE 指令用于在条件判断为真时跳转到指定的位置。在字节码中,JUMP_ABSOLUTE 的目标是跳转到指定的绝对位置,绕过相应的代码块。
运算指令
- BINARY_ADD: 从堆栈中弹出两个值,相加,并将结果推回堆栈。
- BINARY_SUBTRACT: 从堆栈中弹出两个值,相减,并将结果推回堆栈。
- BINARY_MULTIPLY: 从堆栈中弹出两个值,相乘,并将结果推回堆栈。
- BINARY_DIVIDE: 从堆栈中弹出两个值,相除,并将结果推回堆栈。
- BINARY_MODULO: 从堆栈中弹出两个值,取模,并将结果推回堆栈。
- BINARY_POWER: 从堆栈中弹出两个值,计算幂,并将结果推回堆栈。
- BINARY_LSHIFT: 将堆栈顶部的值左移指定位数,并将结果推回堆栈。
- BINARY_RSHIFT: 将堆栈顶部的值右移指定位数,并将结果推回堆栈。
- BINARY_AND: 从堆栈中弹出两个值,进行按位与操作,并将结果推回堆栈。
- BINARY_OR: 从堆栈中弹出两个值,进行按位或操作,并将结果推回堆栈。
- BINARY_XOR: 从堆栈中弹出两个值,进行按位异或操作,并将结果推回堆栈。
- INPLACE_ADD: 从堆栈中弹出两个值,相加,并将结果就地存储回栈顶的位置。
- INPLACE_SUBTRACT: 从堆栈中弹出两个值,相减,并将结果就地存储回栈顶的位置。
- INPLACE_MULTIPLY: 从堆栈中弹出两个值,相乘,并将结果就地存储回栈顶的位置。
- INPLACE_DIVIDE: 从堆栈中弹出两个值,相除,并将结果就地存储回栈顶的位置。
比较指令COMPARE_OP
COMPARE_OP是 Python 字节码的一种指令,用于执行比较操作。它弹出两个值,执行指定的比较操作,然后将比较结果的布尔值推送回堆栈。以下是关于
COMPARE_OP指令的一些细节:- 指令名称:
COMPARE_OP - Opcode:
0x6E - 参数: 一个表示比较操作类型的整数码(具体的操作由该参数确定)
COMPARE_OP的作用是执行两个值之间的比较操作,比较操作的类型由参数确定。这可以是诸如等于、不等于、小于、大于等操作。比较操作的参数码和对应的比较操作如下:
0:<(小于)1:<=(小于等于)2:==(等于)3:!=(不等于)4:>(大于)5:>=(大于等于)6:in(成员测试)7:not in(不是成员测试)8:is(同一对象测试)9:is not(不是同一对象测试)10:exception match(异常匹配)11:BAD(无效的比较)
示例:
1
2
3
4# 比较操作: a < b
a = 5
b = 10
result = a < b对应的字节码可能包含:
1
2
30 LOAD_CONST 1 (5)
2 LOAD_CONST 2 (10)
4 COMPARE_OP 0 (<) # 执行 a < b 的比较操作在这个例子中,
COMPARE_OP指令执行了小于(<)的比较操作,将结果推送回堆栈。- 指令名称:
用户输入
在 Python 2 中,使用 raw_input 函数来接收用户的输入。raw_input 会将用户输入的内容作为字符串返回,不进行任何解析或转换。
从 Python 3 开始,raw_input 被移除,而 input 函数则获得了与 Python 2 中的 raw_input 类似的行为,即将用户输入的内容作为字符串返回。
yield关键字
首先了解一下python迭代器
python迭代器
迭代器有两个重要函数 iter() 用于创建一个迭代器(所有的序列都可以转化为迭代器)。 next()可以返回迭代器的下一元素。若迭代器数据耗尽时仍然调用next()函数,则会引发StopIteration异常。
1 | import sys |
Python3 迭代器与生成器 | 菜鸟教程 (runoob.com)
yield
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
生成器是一个返回迭代器的函数。
1 |
|
生成器函数的优势是它们可以按需生成值,避免一次性生成大量数据并占用大量内存。此外,生成器还可以与其他迭代工具(如for循环)无缝配合使用,提供简洁和高效的迭代方式。











