好吧!“人生苦短,请用Python”,作为python爱好者以及安全从业者,而且最近也碰到了一些这方面的问题,懂点python字节码还是很有必要的。

 Python是一门解释性语言,它的具体工作流程如下:

    1:编译,形成.pyc或.pyo后缀的语言

    2:放入解释器,解释器执行字节流(opecode)

 和java字节码一样,他们都是基于栈进行解释的。首先,先来看对pyc文件进行一个直观的理解:

一:直面pyc文件

  pyc文件的生成一般用于加快Python的解释速度,运行时,如果pyc的编译时间晚于py的修改时间,会直接运行pyc文件。所以一般需要以module形式加载时,才会直接生成,生成pyc文件脚本如下:

  1. import imp
  2. import sys
  3. def generate_pyc(name):
  4. fp, pathname, description = imp.find_module(name)
  5. try:
  6. imp.load_module(name, fp, pathname, description)
  7. finally:
  8. if fp:
  9. fp.close()
  10. if __name__ == '__main__':
  11. t = raw_input()
  12. generate_pyc(t)

  通过load_module形式进行加载,来间接获取pyc文件。

二:分析pyc文件

  pyc文件的格式分为两种

  3.4以前前四个字节为magic,这个和python版本相关,然后四个字节为编译时间,后面为code_type(PycodeObject)。而在3.4及以后则在编译时间之后添加一个filesize。下面是CPython中对code_type数据结构的描述

  1.  
  1. #define OFF(x) offsetof(PyCodeObject, x)
  1. static PyMemberDef code_memberlist[] = {
  2. {"co_argcount", T_INT, OFF(co_argcount), READONLY},
  3. {"co_nlocals", T_INT, OFF(co_nlocals), READONLY},
  4. {"co_stacksize",T_INT, OFF(co_stacksize), READONLY},
  5. {"co_flags", T_INT, OFF(co_flags), READONLY},
  6. {"co_code", T_OBJECT, OFF(co_code), READONLY},
  7. {"co_consts", T_OBJECT, OFF(co_consts), READONLY},
  8. {"co_names", T_OBJECT, OFF(co_names), READONLY},
  9. {"co_varnames", T_OBJECT, OFF(co_varnames), READONLY},
  10. {"co_freevars", T_OBJECT, OFF(co_freevars), READONLY},
  11. {"co_cellvars", T_OBJECT, OFF(co_cellvars), READONLY},
  12. {"co_filename", T_OBJECT, OFF(co_filename), READONLY},
  13. {"co_name", T_OBJECT, OFF(co_name), READONLY},
  14. {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY},
  15. {"co_lnotab", T_OBJECT, OFF(co_lnotab), READONLY},
  16. {NULL} /* Sentinel */
  17. };

  下面是一个基于python2.7的pyc文件例子的部分截图

  解析pyc文件可以得到,如下:

  1. magic 03f30d0a
  2. moddate aa813e59 (Mon Jun :: )
  3. code
  4. argcount
  5. nlocals
  6. stacksize
  7. flags
  8. code
  9. consts
  10. 'hello world!'
  11. None
  12. names ()
  13. varnames ()
  14. freevars ()
  15. cellvars ()
  16. filename 'C:\\Users\\Administrator\\Desktop\\test3.py'
  17. name '<module>'
  18. firstlineno
  19. lnotab

  本文着重讲解的是co_code,也就是opcode。

三:解读opcode

  opcode代码在理解上还是很简单的,想要得到某个函数或者module的话可以直接使用dis.dis()或者dis.disassemble()函数,这里先使用dis.dis()函数,直接观察函数的opcode。下面是一个简单的python脚本:

  1. import dis
  2. def test1():
  3. a = "hello"
  4. b = " "
  5. c = "world"
  6. d = a +b+c
  7. print d
  8.  
  9. print dis.dis(test1)

  可以得到test1函数的opcode代码

  1. LOAD_CONST ('hello')   //'hello'压栈
  2. STORE_FAST (a) //'hell0'出栈,同时local['a'] = 'hello'
  3.  
  4. LOAD_CONST (' ')
  5. STORE_FAST (b)
  6.  
  7. LOAD_CONST ('world')
  8. STORE_FAST (c)
  9.  
  10. LOAD_FAST (a) //将local['a']压栈
  11. LOAD_FAST (b) //将local['b']压栈
  12. BINARY_ADD //栈中a,b相加,结果压栈
  13. LOAD_FAST (c)
  14. BINARY_ADD
  15. STORE_FAST (d)
  16.  
  17. LOAD_FAST (d)
  18. PRINT_ITEM
  19. PRINT_NEWLINE
  20. LOAD_CONST (None)
  21. RETURN_VALUE
  22. None

  源码和opcode对照并结合注释,理解起来还是很方便的。第一列是源代码的行数,第二列是字节相对于第一个字节的偏移,第三个则是命令,第四个是命令参数。opcode的格式如下:

   想要彻底理解上面的代码,必须先理解python基于栈的运行机制,python的运行是单纯模拟cpu运行的机制,看一下它的堆结构

  1. typedef struct _frame {
  2. PyObject_VAR_HEAD
  3. struct _frame *f_back; /* 调用者的帧 */
  4. PyCodeObject *f_code; /* 帧对应的字节码对象 */
  5. PyObject *f_builtins; /* 内置名字空间 */
  6. PyObject *f_globals; /* 全局名字空间 */
  7. PyObject *f_locals; /* 本地名字空间 */
  8. PyObject **f_valuestack; /* 运行时栈底 */
  9. PyObject **f_stacktop; /* 运行时栈顶 */
  10. …….

  可以利用sys.getFrame来得到运行时的堆栈状态

  1. {'a': 'hello', 'c': 'world', 'frame': <frame object at 0x0000000002DEA3A8>, 'b': ' ', 'd': 'hello '}
  2. {'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'>}
  3. {'test1': <function test1 at 0x00000000032B9908>, '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'C:\\Users\\Administrator\\Desktop\\test3.py', '__package__': None, 'sys':

四:常见语句以及对应opcode

  1.判断语句

  1. def test2(t):if t > :
  2. print "OK!"

  对应

  1. LOAD_FAST (t)
  2. LOAD_CONST ()
  3. COMPARE_OP (>)
  4. POP_JUMP_IF_FALSE
  5.  
  6. LOAD_CONST ('OK!')
  7. PRINT_ITEM
  8. PRINT_NEWLINE
  9. JUMP_FORWARD (to )
  10. >> LOAD_CONST (None)
  11. RETURN_VALUE
  12. None

  2.循环语句

  1. def test3():
  2. i =
  3. while t < :
  4. t += i
  5. i+=

  对应

  1. LOAD_CONST ()
  2. STORE_FAST (i)
  3.  
  4. SETUP_LOOP (to )
  5. >> LOAD_FAST (t)
  6. LOAD_CONST ()
  7. COMPARE_OP (<)
  8. POP_JUMP_IF_FALSE
  9.  
  10. LOAD_FAST (t)
  11. LOAD_FAST (i)
  12. INPLACE_ADD
  13. STORE_FAST (t)
  14.  
  15. LOAD_FAST (i)
  16. LOAD_CONST ()
  17. INPLACE_ADD
  18. STORE_FAST (i)
  19. JUMP_ABSOLUTE
  20. >> POP_BLOCK
  21. >> LOAD_CONST (None)
  22. RETURN_VALUE

  3.调用操作

  1. def test4(a,b):
  2. print a+b
  3.  
  4. def test3():
  5. test4(,)

  对应

  1. LOAD_GLOBAL (test4)
  2. LOAD_CONST ()
  3. LOAD_CONST ()
  4. CALL_FUNCTION
  5. POP_TOP
  6. LOAD_CONST (None)
  7. RETURN_VALUE

浮生半日:探究Python字节码的更多相关文章

  1. Python 字节码是什么

    了解 Python 字节码是什么,Python 如何使用它来执行你的代码,以及知道它是如何帮到你的. 如果你曾经编写过 Python,或者只是使用过 Python,你或许经常会看到 Python 源代 ...

  2. Python逆向(五)—— Python字节码解读

    一.前言 前些章节我们对python编译.反汇编的原理及相关模块已经做了解读.读者应该初步掌握了通过反汇编获取python程序可读字节码的能力.python逆向或者反汇编的目的就是在没有源码的基础上, ...

  3. Python 字节码bytecode

    字节码bytecode python把源码文件编译成字节码文件,存放在__pycahe子目录内,用.pyc结尾.之后如果不再修改源码文件,运行时则使用*.pyc文件编译成机器码,这样不但运行速度快,而 ...

  4. Python字节码与解释器学习

    参考:http://blog.jobbole.com/55327/ http://blog.jobbole.com/56300/ http://blog.jobbole.com/56761/ 1. 在 ...

  5. Python字节码介绍

    了解 Python 字节码是什么,Python 如何使用它来执行你的代码,以及知道它是如何帮到你的.如果你曾经编写过 Python,或者只是使用过 Python,你或许经常会看到 Python 源代码 ...

  6. 使用uncompyle2直接反编译python字节码文件pyo/pyc

    update:在Mac OS X版的September 10, 2014版(5.0.9-1)中发现安装目录中的src.zip已更换位置至WingIDE.app/Contents/Resources/b ...

  7. python 字节码死磕

    前言:  如果你跟我一样,对python的字节码感兴趣,想了解python的代码在内存中到底是怎么去运行的,那么你可以继续往下看,如果你是python新手,我建议你移步它处,本文适合有点基础的pyth ...

  8. python字节码,java字节码,十六进制相互转换

    下面是互相转换的代码: 有想要了解更多关于python知识的请在下方评论或私信小编

  9. python反编译之字节码

    如果你曾经写过或者用过 Python,你可能已经习惯了看到 Python 源代码文件:它们的名称以.Py 结尾.你可能还见过另一种类型的文件是 .pyc 结尾的,它们就是 Python "字 ...

随机推荐

  1. struts2之OGNL用法

    浅析OGNL OGNL是Object-GraphNavigation Language的缩写,是一种功能强大的表达式语言 通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对 ...

  2. Linux 文件编码问题及iconv命令

    iconv命令是运行于linux/unix平台的文件编码装换工具.当我们在linux/unix系统shell查看文本文件时,常常会发现文件的中文是乱码的,这是由于文本文件的编码与当前操作系统设置的编码 ...

  3. UIToolBar的半透明属性设置

      UIToolBar的半透明属性设置style:Translucent(Ps:长得很像翻译translation)   https://www.evernote.com/shard/s227/sh/ ...

  4. Js如何动态声明变量名

    做个笔记~ var a = 5; for (var i = 1; i <= a; i++) { eval("var a" + i + "=" + i); ...

  5. bzoj 2733 平衡树启发式合并

    首先对于一个连通块中,询问我们可以直接用平衡树来求出排名,那么我们可以用并查集来维护各个块中的连通情况,对于合并两个平衡树,我们可以暴力的将size小的平衡树中的所有节点删掉,然后加入大的平衡树中,因 ...

  6. Spring cloud 实战读书笔记

    基础知识 Spring cloud 版本说明 Brixton.SR5 :Brixton 的第5个Release版本 SRX:service releases 简称SRX版本,X版本号 Spring b ...

  7. 【转】debian下的update-rc.d的使用

    在Linux系统下,一个Services的启动.停止以及重启通常是通过/etc/init.d目录下的脚本来控制的.然而,在启动或改变运行级别时, 是在/etc/rcX.d中来搜索脚本.其中X是运行级别 ...

  8. python之计算器

    开发一个简单的python计算器 1.实现加减乘除及拓号优先级解析 2.用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * ...

  9. C++学习之路(二):引用

    (1)引用是变量的别名 引用的基本定义格式:类型 &引用名 = 变量名 例如:int a = 1; int &b = a,这里b是a的别名,b与a都指向了同一块内存单元. 对于引用而言 ...

  10. 再思linux内核在中断路径内不能睡眠/调度的原因(2010)【转】

    转自:http://blog.csdn.net/maray/article/details/5770889 Linux内核中断路径中不能睡眠,为什么? 这里就行了很深入的讨论,值得一看:http:// ...