关闭页面特效

Python逆向(二)—— pyc文件结构分析

1|0一、前言


上一节我们知道了pyc文件是python在编译过程中出现的主要中间过程文件。pyc文件是二进制的,可以由python虚拟机直接执行的程序。分析pyc文件的文件结构对于实现python编译与反编译就显得十分重要。

Python代码的编译结果就是PyCodeObject对象。PyCodeObject对象可以由虚拟机加载后直接运行,而pyc文件就是PyCodeObject对象在硬盘上的保存形式。因此我们先分析PyCodeObject对象的结构,随后再涉及pyc文件的二进制结构。

2|0二、PyCodeObject对象结构分析


typedef struct { PyObject_HEAD int co_argcount; /* 位置参数个数 */ int co_nlocals; /* 局部变量个数 */ int co_stacksize; /* 栈大小 */ int co_flags; PyObject *co_code; /* 字节码指令序列 */ PyObject *co_consts; /* 所有常量集合 */ PyObject *co_names; /* 所有符号名称集合 */ PyObject *co_varnames; /* 局部变量名称集合 */ PyObject *co_freevars; /* 闭包用的的变量名集合 */ PyObject *co_cellvars; /* 内部嵌套函数引用的变量名集合 */ /* The rest doesn’t count for hash/cmp */ PyObject *co_filename; /* 代码所在文件名 */ PyObject *co_name; /* 模块名|函数名|类名 */ int co_firstlineno; /* 代码块在文件中的起始行号 */ PyObject *co_lnotab; /* 字节码指令和行号的对应关系 */ void *co_zombieframe; /* for optimization only (see frameobject.c) */ } PyCodeObject;

上面就是PyCodeObject对象一般情况下包含的属性名称及数据类型,每个属性在虚拟机执行pyc文件时都有其作用,随后在编译与反编译的过程中我们会对上述出现的属性一一分析。

3|0三、pyc文件生成


python中使用marshal.dump的方法将PyCodeObject对象转化为对应的二进制文件结构。每个字段在二进制文件中的结构如下图:

byte表示占用1个字节,long表示占用4个字节,bytes表示该字段可能占用1到多个字节。需要说明的是,PyCodeObject对象中的每一个属性及值都会按照一定的顺序表示在二进制文件里。

pyc文件结构主要包括两部分:pyc文件头部表示和PyCodeObject对象部分。上面对PyCodeObject对象的二进制部分已经有了了解,pyc文件头部比较简单,在python2中只占用4个字节包含两个字段magic和mtime,完整的pyc文件结构见下图:

4|0四、实例分析


上面我们对pyc文件结构已经有了理论上的了解,接下来通过一个实例对实际的二进制文件进行分析。
源文件test.py

s = "hello" def func(): a = 3 print s func()

通过执行python2 -m py_compile test.py 可以生成编译好的pyc文件test.pyc。

我们使用二进制编辑器打开test.pyc

  • pyc文件头部
    • 前4个字节:03f3 0d0a,表示python版本
    • 5-8个字节:0e6b 905d,表示pyc文件修改时间
  • PyCodeObject对象二进制编译结果
    • 第9字节:63,TYPE_CODE字段,也就是字符c,值为99,即0x63,表示接下为是一个PyCodeObject对象
  • PyCodeObject对象----全局参数:
    • 然后4个字节是0x00 0000 00,code block的位置参数个数co_argument,这里是0;
    • 再接着4个字节是0x00 0000 00, code block中的局部变量个数co_nlocals,这里是0;
    • 再接着4个字节是0x01 0000 00, code block需要的栈空间co_stacksize,这里是1;
    • 再接着4个字节是0x40 0000 00, co_flags,这里是64;
  • PyCodeObject对象----code block:
    • 1个字节0x73为TYPE_CODE字段, 表示该字段为string格式;
    • 4个字节0x1a00 0000表示code block段的数据部分占用0x1a个字节,即长度为26;
    • 接下来26个字节6400 ...... 6402 0053为该TYPE_CODE字段(数据类型string)部分,也就是pyc文件中包含的字节码指令
  • 再往下的逐个TYPE_CODE字段都是重复结构的,用来表示PyCodeObject对象中的一些其他参数

__EOF__

作  者Blili
出  处https://www.cnblogs.com/blili
关于博主:日出江花红胜火,春来江水绿如蓝
版权声明:本文版权归作者和博客园共有,如有转载,请在文章页面给出原文链接,并以cnblogs短消息方式通知作者,转载不得用于商业用途,否则保留追究法律责任的权利。
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Blili  阅读(9271)  评论(2编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示