1. 项目概述:当CTF遇上Python逆向
在CTF(Capture The Flag)夺旗赛中,逆向工程一直是充满挑战与乐趣的领域。当目标不再是传统的C/C++二进制程序,而是Python这类解释型语言时,解题思路和工具链会发生根本性的变化。很多刚接触逆向的选手,面对一个 .pyc 文件或者一段混淆的Python字节码时,常常感到无从下手,觉得比分析汇编指令还要抽象。实际上,Python逆向有其独特的“套路”,一旦掌握,解题效率会大幅提升。这篇文章,我就结合自己多次在CTF中解决Python逆向题的经验,从最基础的 .pyc 文件结构讲起,一直深入到手工分析Python字节码,手把手带你搭建一套完整的Python逆向实战技能树。无论你是想入门CTF逆向,还是已经有一定基础想深化Python方向的技能,这些从实战中踩坑总结出来的方法,都能让你少走弯路。
2. 核心原理:Python代码的执行与存储形式
要逆向,首先得知道正向是怎么跑的。Python代码的执行并非直接翻译成机器码,这为我们逆向分析留下了清晰的“中间层”。
2.1 从源码到字节码:编译过程揭秘
当我们执行 python script.py 时,解释器并非直接读取你的 .py 文本文件。它内部有一个编译过程:
- 词法分析与语法分析 :解释器首先将源代码文本解析成一颗“抽象语法树”(AST)。这棵树代表了代码的结构化表示。
- 生成字节码 :解释器遍历AST,将其转换为一系列低级的、平台无关的指令,这就是Python字节码。这些指令是Python虚拟机(PVM)能够直接理解和执行的。
- 存储为pyc文件 :为了提高后续加载速度,Python通常会将编译好的字节码序列化后保存到
.pyc文件中。.pyc文件本质上是一个二进制文件,包含了序列化后的字节码、常量、变量名等信息。
注意 :默认情况下,导入模块(
import)时会生成.pyc文件,存放在__pycache__目录下。直接运行脚本(python script.py)通常不会生成,但可以通过python -m py_compile script.py或python -O -m py_compile script.py(生成优化后的.pyc)来手动编译。
2.2 Pyc文件结构解析
一个 .pyc 文件不是一团乱麻的二进制数据,它有固定的结构。理解这个结构是手动修复损坏pyc或深入分析的基础。以Python 3.7+为例,一个典型的pyc文件头包含:
- 魔数(Magic Number) :前4个字节。用于标识该pyc文件是由哪个特定版本的Python编译器生成的。Python版本升级,魔数通常会变。如果魔数不对,反编译工具会报错。
- 位域(Bit Field) :接下来4个字节,包含时间戳等信息。
- 序列化后的Code Object :文件剩余的大部分内容,是使用
marshal模块序列化后的一个code对象。这个对象才是核心,它包含了执行所需的所有信息。
code 对象本身又包含了许多属性,这些属性在逆向时至关重要:
-
co_consts: 一个元组,存放代码中使用的所有字面量常量,比如数字、字符串、None等。 -
co_names: 一个元组,存放代码中使用的所有变量名、函数名等名称。 -
co_code: 一个字节串(bytes),这就是真正的字节码指令序列。我们常说的分析字节码,主要就是分析它。 -
co_varnames: 一个元组,存放局部变量名。 -
co_filename: 源代码文件名。
在CTF题目中,出题人有时会故意修改或损坏pyc文件的魔数,导致通用反编译工具失效。这时候,如果你了解结构,就可以用十六进制编辑器(如 010 Editor ,它有pyc模板)手动修复魔数,这是解题的关键一步。
3. 逆向工具链:从反编译到动态调试
工欲善其事,必先利其器。Python逆向有一系列成熟或小众的工具,各有适用场景。
3.1 反编译工具:一键还原源码
对于完整的、未加密的pyc文件,反编译是最高效的手段。
-
uncompyle6 / decompyle3 :这是目前最主流、最强大的Python反编译器。它能将Python 2.7和3.8以下版本的字节码高质量地还原为源代码。安装和使用非常简单:
pip install uncompyle6 uncompyle6 -o . target.pyc # 将反编译结果输出到当前目录很多时候,直接使用
uncompyle6就能拿到近乎原始的源码,题目直接告破。但出题人也会针对此进行防护。 -
pycdc / pycdas



被折叠的 条评论
为什么被折叠?



