浮生半日:探究Python字节码
好吧!“人生苦短,请用Python”,作为python爱好者以及安全从业者,而且最近也碰到了一些这方面的问题,懂点python字节码还是很有必要的。
Python是一门解释性语言,它的具体工作流程如下:
1:编译,形成.pyc或.pyo后缀的语言
2:放入解释器,解释器执行字节流(opecode)
和java字节码一样,他们都是基于栈进行解释的。首先,先来看对pyc文件进行一个直观的理解:
一:直面pyc文件
pyc文件的生成一般用于加快Python的解释速度,运行时,如果pyc的编译时间晚于py的修改时间,会直接运行pyc文件。所以一般需要以module形式加载时,才会直接生成,生成pyc文件脚本如下:
- import imp
- import sys
- def generate_pyc(name):
- fp, pathname, description = imp.find_module(name)
- try:
- imp.load_module(name, fp, pathname, description)
- finally:
- if fp:
- fp.close()
- if __name__ == '__main__':
- t = raw_input()
- generate_pyc(t)
通过load_module形式进行加载,来间接获取pyc文件。
二:分析pyc文件
pyc文件的格式分为两种
3.4以前前四个字节为magic,这个和python版本相关,然后四个字节为编译时间,后面为code_type(PycodeObject)。而在3.4及以后则在编译时间之后添加一个filesize。下面是CPython中对code_type数据结构的描述
- #define OFF(x) offsetof(PyCodeObject, x)
- static PyMemberDef code_memberlist[] = {
- {"co_argcount", T_INT, OFF(co_argcount), READONLY},
- {"co_nlocals", T_INT, OFF(co_nlocals), READONLY},
- {"co_stacksize",T_INT, OFF(co_stacksize), READONLY},
- {"co_flags", T_INT, OFF(co_flags), READONLY},
- {"co_code", T_OBJECT, OFF(co_code), READONLY},
- {"co_consts", T_OBJECT, OFF(co_consts), READONLY},
- {"co_names", T_OBJECT, OFF(co_names), READONLY},
- {"co_varnames", T_OBJECT, OFF(co_varnames), READONLY},
- {"co_freevars", T_OBJECT, OFF(co_freevars), READONLY},
- {"co_cellvars", T_OBJECT, OFF(co_cellvars), READONLY},
- {"co_filename", T_OBJECT, OFF(co_filename), READONLY},
- {"co_name", T_OBJECT, OFF(co_name), READONLY},
- {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY},
- {"co_lnotab", T_OBJECT, OFF(co_lnotab), READONLY},
- {NULL} /* Sentinel */
- };
下面是一个基于python2.7的pyc文件例子的部分截图
解析pyc文件可以得到,如下:
- magic 03f30d0a
- moddate aa813e59 (Mon Jun :: )
- code
- argcount
- nlocals
- stacksize
- flags
- code
- consts
- 'hello world!'
- None
- names ()
- varnames ()
- freevars ()
- cellvars ()
- filename 'C:\\Users\\Administrator\\Desktop\\test3.py'
- name '<module>'
- firstlineno
- lnotab
本文着重讲解的是co_code,也就是opcode。
三:解读opcode
opcode代码在理解上还是很简单的,想要得到某个函数或者module的话可以直接使用dis.dis()或者dis.disassemble()函数,这里先使用dis.dis()函数,直接观察函数的opcode。下面是一个简单的python脚本:
- import dis
- def test1():
- a = "hello"
- b = " "
- c = "world"
- d = a +b+c
- print d
- print dis.dis(test1)
可以得到test1函数的opcode代码
- LOAD_CONST ('hello') //'hello'压栈
- STORE_FAST (a) //'hell0'出栈,同时local['a'] = 'hello'
- LOAD_CONST (' ')
- STORE_FAST (b)
- LOAD_CONST ('world')
- STORE_FAST (c)
- LOAD_FAST (a) //将local['a']压栈
- LOAD_FAST (b) //将local['b']压栈
- BINARY_ADD //栈中a,b相加,结果压栈
- LOAD_FAST (c)
- BINARY_ADD
- STORE_FAST (d)
- LOAD_FAST (d)
- PRINT_ITEM
- PRINT_NEWLINE
- LOAD_CONST (None)
- RETURN_VALUE
- None
源码和opcode对照并结合注释,理解起来还是很方便的。第一列是源代码的行数,第二列是字节相对于第一个字节的偏移,第三个则是命令,第四个是命令参数。opcode的格式如下:
想要彻底理解上面的代码,必须先理解python基于栈的运行机制,python的运行是单纯模拟cpu运行的机制,看一下它的堆结构
- typedef struct _frame {
- PyObject_VAR_HEAD
- struct _frame *f_back; /* 调用者的帧 */
- PyCodeObject *f_code; /* 帧对应的字节码对象 */
- PyObject *f_builtins; /* 内置名字空间 */
- PyObject *f_globals; /* 全局名字空间 */
- PyObject *f_locals; /* 本地名字空间 */
- PyObject **f_valuestack; /* 运行时栈底 */
- PyObject **f_stacktop; /* 运行时栈顶 */
- …….
可以利用sys.getFrame来得到运行时的堆栈状态
- {'a': 'hello', 'c': 'world', 'frame': <frame object at 0x0000000002DEA3A8>, 'b': ' ', 'd': 'hello '}
- {'test1': <function test1 at 0x00000000032B9908>, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'C:\\Users\\Administrator\\Desktop\\test3.py', '__package__': None, 'sys': <module 'sys' (built-in)>, '__name__': '__main__', '__doc__': None, 'dis': <module 'dis' from 'C:\Python27\lib\dis.pyc'>}
- {'test1': <function test1 at 0x00000000032B9908>, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'C:\\Users\\Administrator\\Desktop\\test3.py', '__package__': None, 'sys':
四:常见语句以及对应opcode
1.判断语句
- def test2(t):if t > :
- print "OK!"
对应
- LOAD_FAST (t)
- LOAD_CONST ()
- COMPARE_OP (>)
- POP_JUMP_IF_FALSE
- LOAD_CONST ('OK!')
- PRINT_ITEM
- PRINT_NEWLINE
- JUMP_FORWARD (to )
- >> LOAD_CONST (None)
- RETURN_VALUE
- None
2.循环语句
- def test3():
- i =
- while t < :
- t += i
- i+=
对应
- LOAD_CONST ()
- STORE_FAST (i)
- SETUP_LOOP (to )
- >> LOAD_FAST (t)
- LOAD_CONST ()
- COMPARE_OP (<)
- POP_JUMP_IF_FALSE
- LOAD_FAST (t)
- LOAD_FAST (i)
- INPLACE_ADD
- STORE_FAST (t)
- LOAD_FAST (i)
- LOAD_CONST ()
- INPLACE_ADD
- STORE_FAST (i)
- JUMP_ABSOLUTE
- >> POP_BLOCK
- >> LOAD_CONST (None)
- RETURN_VALUE
3.调用操作
- def test4(a,b):
- print a+b
- def test3():
- test4(,)
对应
- LOAD_GLOBAL (test4)
- LOAD_CONST ()
- LOAD_CONST ()
- CALL_FUNCTION
- POP_TOP
- LOAD_CONST (None)
- RETURN_VALUE
浮生半日:探究Python字节码的更多相关文章
- Python 字节码是什么
了解 Python 字节码是什么,Python 如何使用它来执行你的代码,以及知道它是如何帮到你的. 如果你曾经编写过 Python,或者只是使用过 Python,你或许经常会看到 Python 源代 ...
- Python逆向(五)—— Python字节码解读
一.前言 前些章节我们对python编译.反汇编的原理及相关模块已经做了解读.读者应该初步掌握了通过反汇编获取python程序可读字节码的能力.python逆向或者反汇编的目的就是在没有源码的基础上, ...
- Python 字节码bytecode
字节码bytecode python把源码文件编译成字节码文件,存放在__pycahe子目录内,用.pyc结尾.之后如果不再修改源码文件,运行时则使用*.pyc文件编译成机器码,这样不但运行速度快,而 ...
- Python字节码与解释器学习
参考:http://blog.jobbole.com/55327/ http://blog.jobbole.com/56300/ http://blog.jobbole.com/56761/ 1. 在 ...
- Python字节码介绍
了解 Python 字节码是什么,Python 如何使用它来执行你的代码,以及知道它是如何帮到你的.如果你曾经编写过 Python,或者只是使用过 Python,你或许经常会看到 Python 源代码 ...
- 使用uncompyle2直接反编译python字节码文件pyo/pyc
update:在Mac OS X版的September 10, 2014版(5.0.9-1)中发现安装目录中的src.zip已更换位置至WingIDE.app/Contents/Resources/b ...
- python 字节码死磕
前言: 如果你跟我一样,对python的字节码感兴趣,想了解python的代码在内存中到底是怎么去运行的,那么你可以继续往下看,如果你是python新手,我建议你移步它处,本文适合有点基础的pyth ...
- python字节码,java字节码,十六进制相互转换
下面是互相转换的代码: 有想要了解更多关于python知识的请在下方评论或私信小编
- python反编译之字节码
如果你曾经写过或者用过 Python,你可能已经习惯了看到 Python 源代码文件:它们的名称以.Py 结尾.你可能还见过另一种类型的文件是 .pyc 结尾的,它们就是 Python "字 ...
随机推荐
- struts2之OGNL用法
浅析OGNL OGNL是Object-GraphNavigation Language的缩写,是一种功能强大的表达式语言 通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对 ...
- Linux 文件编码问题及iconv命令
iconv命令是运行于linux/unix平台的文件编码装换工具.当我们在linux/unix系统shell查看文本文件时,常常会发现文件的中文是乱码的,这是由于文本文件的编码与当前操作系统设置的编码 ...
- UIToolBar的半透明属性设置
UIToolBar的半透明属性设置style:Translucent(Ps:长得很像翻译translation) https://www.evernote.com/shard/s227/sh/ ...
- Js如何动态声明变量名
做个笔记~ var a = 5; for (var i = 1; i <= a; i++) { eval("var a" + i + "=" + i); ...
- bzoj 2733 平衡树启发式合并
首先对于一个连通块中,询问我们可以直接用平衡树来求出排名,那么我们可以用并查集来维护各个块中的连通情况,对于合并两个平衡树,我们可以暴力的将size小的平衡树中的所有节点删掉,然后加入大的平衡树中,因 ...
- Spring cloud 实战读书笔记
基础知识 Spring cloud 版本说明 Brixton.SR5 :Brixton 的第5个Release版本 SRX:service releases 简称SRX版本,X版本号 Spring b ...
- 【转】debian下的update-rc.d的使用
在Linux系统下,一个Services的启动.停止以及重启通常是通过/etc/init.d目录下的脚本来控制的.然而,在启动或改变运行级别时, 是在/etc/rcX.d中来搜索脚本.其中X是运行级别 ...
- python之计算器
开发一个简单的python计算器 1.实现加减乘除及拓号优先级解析 2.用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * ...
- C++学习之路(二):引用
(1)引用是变量的别名 引用的基本定义格式:类型 &引用名 = 变量名 例如:int a = 1; int &b = a,这里b是a的别名,b与a都指向了同一块内存单元. 对于引用而言 ...
- 再思linux内核在中断路径内不能睡眠/调度的原因(2010)【转】
转自:http://blog.csdn.net/maray/article/details/5770889 Linux内核中断路径中不能睡眠,为什么? 这里就行了很深入的讨论,值得一看:http:// ...