在实际开发中,Python作为解释型语言,在实际的代码分发过程中,有比较多的格式定义:.pyc\.pyd\.pyo\.pyz。一直想对它们理一下,东拼西凑的整理了下面的内容,主要也回答了自己的一些问题。
当您导入一个模块时,类型为.pyc的文件将由解释器自动生成,这将加速该模块未来的导入。因此,这些文件仅在由另一个.py文件或模块导入时从.py文件创建。
注意,使用.pyc文件只会加快程序的加载速度,而不会加快程序的实际执行速度。这意味着您可以通过在一个模块中编写主程序来提高启动时间,这个模块由另一个更小的模块导入。
pyc主要写入三个内容:
1. Magic num
2. Pyc创建时间
3. PyCodeObject.(python/marshal.c)
Python在不同的版本,pyc的头部长度和内容是不同的:
l PEP 3147:
.pyc文件包含两个32位大端数字,后面跟的序列化的PyCodeObject。32位数字表示一个Magic Num和Timestamp。每当Python改变字节码格式时,Magic Num会改变,例如,通过向其虚拟机添加新的字节码。这确保为以前版本的VM构建的pyc文件不会造成问题。Timestamp用于确保pyc文件与用于创建它的py文件匹配。当Magic Num或Timestamp不匹配时,将重新编译py文件并写入新的pyc文件。
l PEP 552:
pyc头文件目前由3个32位的字组成。我们将把它扩大到4个。第一个单词将继续是magic number,对字节码和pyc格式进行版本控制。第二个4byte新增加的字段,将是一个位字段(bit field),对报头其余部分的解释和pyc的失效行为取决于位字段的内容。
如果位字段(bit field)为0,则pyc是传统的基于时间戳的pyc。即第三个和第四个4字节内容分别是时间戳和文件大小,通过比较源文件的元数据和头文件中的元数据来进行无效判断。
如果位字段的最低位被设置,则pyc是基于哈希的pyc。我们将第二个最低位称为check_source标志。位字段之后是源文件的64位散列。我们将使用带有源文件内容硬编码密钥。另一个类似MD5或BLAKE2的快速散列也可以。我们选择SipHash是因为Python已经从PEP 456中获得了它的内置实现,尽管允许选择SipHash键的接口必须公开给Python。散列的安全性不是一个问题,尽管我们传递完全破碎的散列(如MD5)来简化在受控环境中对Python的审计。
Python version
Magic Num
Before 2.5
cafebabe
Python 2.5
B3F20D0A
-
03F30D0A
Python 3.2
9E0C0D0A
Python 3.3+
160D0D0A
Python 3.5
420D0D0A
已经知的一些Magic例子
1970.01.01到产生pyc时候的秒数.
这个对象比较复杂,不展开,可参考一个整理进行了解,不同的版本还会有变化。
python27参考
.pyo文件类型也是由解释器在导入模块时创建的。但是,.pyo文件是在启用优化设置时运行解释器的结果。
当我们调用Python解释器时,通过添加“-O”标志来启用优化器。下面是一个代码示例,演示如何使用优化。首先,我们有一个定义lambda的模块。在Python中,lambda就像一个函数,但是定义得更简洁。
.pyd文件类型是特定于Windows操作系统类平台的。因此,在个人版和企业版的Windows 10、Windows 8、Windows 7和其他版本中可能经常遇到这种情况。
在Windows生态系统中,.pyd文件是一个包含Python代码的库文件,可以被其他Python应用程序调用和使用。为了使这个库对其他Python程序可用,它被打包为一个动态链接库。
动态链接库(dll)是在运行时链接到调用程序的Windows代码库。在像dll这样的运行时链接到库的主要优点是,它促进了代码重用、模块化架构和更快的程序启动。因此,dll提供了许多与Windows操作系统相关的功能。
.pyd文件是一个动态链接库,它包含一个Python模块,或一组模块,由其他Python代码调用。要创建.pyd文件,需要创建一个名为example.pyd的模块。在这个模块中,您将需要创建一个名为PyInit_example()的函数。当程序调用这个库时,它们需要调用import foo, PyInit_example()函数将运行。
具体内容参见:ZlibArchive
打包文件是包含其他文件的文件,例如.tar文件、.jar文件或.zip文件。PyInstaller中使用了两种存档。一个是ZlibArchive,它允许高效地存储Python模块,并通过一些导入钩子直接导入。另一个是CArchive,类似于.zip文件,这是一种打包(或压缩)任意数据块的通用方法。它的名字来源于这样一个事实,即它可以很容易地从C和Python中操作。这两个类都来自一个公共基类,这使得创建新类型的归档变得相当容易。
ZlibArchive包含压缩的.pyc或.pyo文件。spec文件中的PYZ类调用创建了一个ZlibArchive。
ZlibArchive中的目录是一个Python字典,它的Key(import语句中给定的成员名)与ZlibArchive中的查找位置和长度相关联。ZlibArchive的所有部分都以编组格式存储,因此与平台无关。
ZlibArchive在运行时用于导入绑定的python模块。即使使用最大压缩,这也比正常导入快。而不是搜索系统。路径,在字典里有一个查找。没有目录操作,也没有要打开的文件(该文件已经打开)。只有一次搜索,一次读取和一次解压。
Python错误跟踪将指向创建归档条目的源文件(.pyc编译、捕获并保存到归档时的_file__属性)。这不会告诉您的用户任何有用的东西,但是如果他们向您发送Python错误跟踪,您可以理解它。
ZlibArchive文档结构
CArchive可以包含任何类型的文件。它很像一个.zip文件。它们很容易用Python创建,也很容易从C代码中解包。CArchive可以附加到另一个文件,比如ELF和COFF可执行文件。为了实现这一点,存档是在文件的末尾用它的目录创建的,后面只跟一个cookie,它告诉目录从哪里开始以及存档本身从哪里开始。
CArchive可以嵌入到另一个CArchive中。内部存档可以在适当的地方打开和使用,而不必提取它。
每个目录条目都有可变的长度。条目中的第一个字段给出了条目的长度。最后一个字段是相应打包文件的名称。名称以空结尾。压缩对于每个成员都是可选的。
还有一个与每个成员相关联的类型代码。类型代码由自提取的可执行程序使用。如果使用CArchive作为.zip文件,则不必担心代码。
ELF可执行格式(Windows、GNU/Linux和其他一些格式)允许将任意数据连接到可执行文件的末尾,而不影响其功能。因此,CArchive的目录在归档的最后。可执行文件可以以二进制文件的形式打开自己,查找到最后并“打开”CArchive。
Carchive结构
自解压执行文件结构
可以检查和提取任何使用PyInstaller (PYZ或PKG)构建的归档文件的内容,或任何可执行文件(.exe文件或ELF或COFF二进制文件)的内容。
简单点说是:支持对pyinstaller打包的exe和pyz文件进行内容查看和提取。需要说明:提取出来的pyc文件缺少头部信息,需要自己添加上,具体的头部信息需要根据上面提到的头部版本进行添加。
支持对.exe文件的内容进行分析和提取(因为作者没有更新了,功能支持有限,其实它也支持pyz的提取,只是没有单独列出来)。
功能强大的在线工具:https://python-decompiler.com/ ,比较之下比uncompile6和Easy python Decompiler 要兼容的情况多。
https://kdr2.com/tech/python/pyc-format.html
https://www.python.org/dev/peps/pep-0552/
https://www.python.org/dev/peps/pep-3147/
https://docs.python.org/3/library/zipapp.html
https://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html
http://code.activestate.com/recipes/577880-inspect-a-pyc-file/
https://qiita.com/amedama/items/698a7c4dbdd34b03b427
https://pyinstaller.readthedocs.io/en/stable/advanced-topics.html#the-bootstrap-process-in-detail
[进行中]2019 KCTF总决赛 | 巅峰对决,谁与争锋!(感谢第五空间和安恒信息对活动的支持!)
最后于 3小时前 被nevinhappy编辑 ,原因: 上传附件。