首页 技术 正文
技术 2022年11月18日
0 收藏 755 点赞 2,873 浏览 3913 个字

 好吧!“人生苦短,请用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文件例子的部分截图

浮生半日:探究Python字节码

  解析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 dprint 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基于栈的运行机制,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+bdef test3():
test4(,)

  对应

             LOAD_GLOBAL               (test4)
LOAD_CONST ()
LOAD_CONST ()
CALL_FUNCTION
POP_TOP
LOAD_CONST (None)
RETURN_VALUE
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,494
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,907
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,740
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,495
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,133
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,297