首页
社区
课程
招聘
[原创]死磕python字节码-手工还原python源码
发表于: 2018-9-6 11:11 16840

[原创]死磕python字节码-手工还原python源码

2018-9-6 11:11
16840

Python 代码先被编译为字节码后,再由Python虚拟机来执行字节码, Python的字节码是一种类似汇编指令的中间语言, 一个Python语句会对应若干字节码指令,虚拟机一条一条执行字节码指令, 从而完成程序执行。
Python dis 模块支持对Python代码进行反汇编, 生成字节码指令。

dis.dis()将CPython字节码转为可读的伪代码(类似于汇编代码)。结构如下:

其实就是这样的结构:

LOAD_CONST加载const变量,比如数值、字符串等等,一般用于传给函数的参数

转为python代码就是:

LOAD_FAST一般加载局部变量的值,也就是读取值,用于计算或者函数调用传参等。
STORE_FAST一般用于保存值到局部变量。

这段bytecode转为python就是:

函数的形参也是局部变量,如何区分出是函数形参还是其他局部变量呢?

形参没有初始化,也就是从函数开始到LOAD_FAST该变量的位置,如果没有看到STORE_FAST,那么该变量就是函数形参。

而其他局部变量在使用之前肯定会使用STORE_FAST进行初始化。

具体看下面的实例:

对应的python代码如下,对比一下就一目了然。

LOAD_GLOBAL用来加载全局变量,包括指定函数名,类名,模块名等全局符号。

STORE_GLOBAL用来给全局变量赋值。

对应的python代码

BUILD_LIST用于创建一个list结构。

对应python代码是:

另外再看看一种常见的创建list的方式如下:

一个实例bytecode如下:

转为python代码是:

BUILD_MAP用于创建一个空的dict。STORE_MAP用于初始化dict的内容。

对应的python代码是:

再看看修改dict的bytecode:

对应的python代码是:

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表示如下:

下面看具体实例:

SETUP_LOOP用于开始一个循环。SETUP_LOOP 26 (to 35)35表示循环退出点。

对应python代码是:

这是典型的for+in结构,转为python代码就是:

POP_JUMP_IF_FALSEJUMP_FORWARD一般用于分支判断跳转。POP_JUMP_IF_FALSE表示条件结果为FALSE就跳转到目标偏移指令。JUMP_FORWARD直接跳转到目标偏移指令。

转为python代码是:

前面介绍第二列表示指令在函数中的偏移地址,所以看到0就是函数开始,下一个0前一条指令就是函数结束位置,当然也可以通过RETURN_VALUE来确定函数结尾

函数调用类似于push+call的汇编结构,压栈参数从左到右依次压入(当然不是push,而是读取指令LOAD_xxxx来指定参数)。

函数名一般通过LOAD_GLOBAL指令指定,如果是模块函数或者类成员函数通过LOAD_GLOBAL+LOAD_ATTR来指定。

先指定要调用的函数,然后压参数,最后通过CALL_FUNCTION调用。

CALL_FUNCTION后面的值表示有几个参数。

支持嵌套调用:

这段bytecode转换成python代码就是

其他常见指令,一看就明白,就不具体分析了,更多详细内容请看官方文档

基础运算还有一套对应的BINARY_xxxx指令,两者区别很简单。

欢迎有兴趣的朋友关注公众号:汉客儿

 
 
 
 
 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 5
支持
分享
最新回复 (9)
雪    币: 940
活跃值: (1063)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
Mark
2018-9-6 15:40
0
雪    币: 97
活跃值: (86)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
如果opcode没有被改过, 好像有工具直接可以还原出源码
2018-9-6 16:27
0
雪    币: 156
活跃值: (107)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
你博客访问不了
2018-9-6 16:28
0
雪    币: 10072
活跃值: (3008)
能力值: ( LV15,RANK:515 )
在线值:
发帖
回帖
粉丝
5
LiDogEgg 如果opcode没有被改过, 好像有工具直接可以还原出源码
什么工具?
2018-9-7 09:52
0
雪    币: 10072
活跃值: (3008)
能力值: ( LV15,RANK:515 )
在线值:
发帖
回帖
粉丝
6
oopww 你博客访问不了
https://anhkgg.github.io  https://anhkgg.com
2018-9-7 09:53
0
雪    币: 1339
活跃值: (2228)
能力值: ( LV5,RANK:75 )
在线值:
发帖
回帖
粉丝
7
Angelxf 什么工具?
应该指的是uncompyle2,如果opcode没被替换,可以直接反编译出源码,
楼主这篇文档对于理解python汇编和处理opcode替换过的pyc文件比较有用,收藏了
最后于 2018-9-7 14:53 被茅山小僧编辑 ,原因:
2018-9-7 14:53
0
雪    币: 10072
活跃值: (3008)
能力值: ( LV15,RANK:515 )
在线值:
发帖
回帖
粉丝
8
茅山小僧 Angelxf 什么工具? 应该指的是uncompyle2,如果opcode没被替换,可以直接反编译出源码,楼主这篇文档对于理解python汇编和处理o ...
uncompyle2只能用于98 00 11这种bytecode还原,伪代码需手工转为bytecode字节码才能使用uncompyle2
2018-9-7 16:18
0
雪    币: 58
活跃值: (1780)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
写的已经非常的全了。非常好。
2020-9-12 21:48
0
雪    币: 66
活跃值: (2807)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
这和.NET的MSIL一样,已经近似于高级语言了。
2020-9-14 15:07
0
游客
登录 | 注册 方可回帖
返回
//