从一道NISACTF赛题看Python逆向:pyc文件修复与RSA加密破解全记录

从一道NISACTF赛题看Python逆向:pyc文件修复与RSA加密破解全记录

最近在NISACTF 2022的赛场上遇到了一道名为“ezpython”的逆向题目,这道题看似简单,却巧妙地将Python打包程序的反编译、pyc文件头修复、以及RSA加密算法的分析融合在一起,成为了一个检验逆向工程师综合能力的典型案例。很多朋友在初次接触这类题目时,往往会在pyc文件修复这一步卡住,或者面对复杂的加密逻辑感到无从下手。今天,我就结合这道赛题的完整解题过程,为大家详细拆解Python逆向中的这两个核心难点,并分享一些实用的工具和技巧。

对于使用PyInstaller打包的Python程序,逆向的第一步通常是从EXE文件中提取出原始的Python字节码文件(.pyc)。这个过程看似简单,但实际操作中会遇到各种意想不到的问题,比如文件头损坏、版本不匹配等。而一旦成功提取并修复了pyc文件,我们就能看到程序的源代码,这时候真正的挑战才刚刚开始——如何理解并逆向分析其中的加密算法逻辑。在“ezpython”这道题中,出题人精心设计了一个包含RSA加密和自定义异或加密的双重保护机制,需要我们对Python的加密库和基本的密码学原理有深入的理解。

1. PyInstaller打包程序的逆向基础与工具链

在Python逆向工程中,PyInstaller是一个常见的打包工具,它能够将Python脚本及其依赖项打包成独立的可执行文件。这种打包方式虽然方便了程序的发布和运行,但也给逆向分析带来了一定的挑战。不过,只要掌握了正确的方法和工具链,我们仍然能够从打包后的EXE文件中还原出原始的Python源代码。

1.1 PyInstaller的工作原理与文件结构

要理解如何逆向PyInstaller打包的程序,首先需要了解它的工作原理。PyInstaller在打包时会将Python解释器、脚本的字节码文件(.pyc)、以及所有依赖的库文件一起打包到一个可执行文件中。当用户运行这个EXE文件时,PyInstaller会在临时目录中解压这些文件,然后启动Python解释器执行主脚本。

PyInstaller打包的EXE文件结构:
├── 可执行文件头部(包含解压逻辑)
├── Python解释器
├── 主脚本的.pyc文件
├── 依赖库的.pyc文件
└── 其他资源文件

了解这个结构后,我们就可以有针对性地进行逆向操作。核心思路是模拟PyInstaller的解压过程,从EXE文件中提取出.pyc文件,然后对这些字节码文件进行反编译。

1.2 核心工具:pyinstxtractor的使用详解

目前最常用的PyInstaller解包工具是pyinstxtractor,这是一个开源的Python脚本,专门用于从PyInstaller打包的EXE文件中提取原始文件。它的使用非常简单,但有几个关键点需要注意。

首先,你需要从GitHub或SourceForge下载pyinstxtractor.py脚本。将下载的脚本与目标EXE文件放在同一目录下,然后在命令行中执行:

python pyinstxtractor.py ez_python.exe

执行成功后,会在当前目录生成一个以“_extracted”结尾的文件夹,里面包含了从EXE文件中提取出的所有文件。对于“ezpython”这道题,提取后的文件夹内容大致如下:

ez_python_extracted/
├── PYZ-00.pyz_extracted/  # 依赖库文件
├── struct                 # 关键的系统模块文件
├── src                    # 主程序文件(无后缀)
└── 其他元数据文件

注意:不同版本的PyInstaller打包的程序,其内部结构可能略有差异。如果遇到解包失败的情况,可以尝试更新pyinstxtractor到最新版本,或者检查EXE文件是否被其他工具加壳保护。

在实际操作中,我遇到过一些特殊情况。比如某些题目可能会对PyInstaller进行修改,或者使用了自定义的打包参数,这时候标准的pyinstxtractor可能无法正常工作。针对这种情况,我们可以尝试分析EXE文件的二进制结构,手动定位.pyc文件的位置。不过对于大多数CTF题目和实际应用场景,pyinstxtractor已经足够应对。

1.3 常见问题与解决方案

在使用pyinstxtractor的过程中,可能会遇到以下几个常见问题:

  1. Python版本不匹配:pyinstxtractor需要与目标EXE文件使用的Python版本兼容。如果遇到错误,可以尝试使用不同版本的Python运行pyinstxtractor。

  2. 文件提取不完整:有时候pyinstxtractor可能无法提取出所有文件,特别是当EXE文件被压缩或加密时。这时可以尝试使用--debug参数获取更多调试信息。

  3. 提取出的文件无法识别:PyInstaller可能会对提取出的文件进行重命名或修改,需要根据文件内容和结构进行判断。

针对“ezpython”这道题,解包过程相对顺利。但接下来我们会遇到一个更棘手的问题——提取出的.pyc文件无法直接反编译。

2. pyc文件头修复:从损坏字节码到可反编译文件

从PyInstaller打包的EXE中提取出的.pyc文件通常是不完整的,它们缺少了标准的文件头信息,这就是为什么我们无法直接使用uncompyle6等工具进行反编译。要解决这个问题,我们需要理解.pyc文件的结构,并学会如何修复损坏的文件头。

2.1 pyc文件结构深度解析

一个完整的.pyc文件由三部分组成:Magic Number时间戳代码对象。其中Magic Number是最关键的部分,它标识了该.pyc文件是由哪个特定版本的Python编译器生成的。不同Python版本的Magic Number是不同的,如果Magic Number错误或缺失,反编译工具就无法正确解析文件。

组成部分 大小(字节) 说明
Magic Number 4 Python版本的标识符
时间戳 4 源文件最后修改时间
代码对象 可变 编译后的字节码数据

在“ezpython”这道题中,我们从EXE文件中提取出的src文件(实际上是主程序的.pyc文件)缺少了前8个字节的Magic Number和时间戳。而同时提取出的struct文件(Python标准库模块的.pyc文件)则包含了完整的文件头信息。这就给了我们修复的机会——我们可以从struct文件中复制Magic Number和时间戳到src文件中。

2.2 使用十六进制编辑器手动修复

修复.pyc文件头最直接的方法是使用十六进制编辑器,如WinHex、010 Editor或HxD。以下是详细的操作步骤:

  1. 同时打开struct文件和src文件:在十六进制编辑器中同时打开这两个文件,确保能够看到它们的二进制内容。

  2. 定位Magic Number的位置:在struct文件中,Magic Number通常位于文件的开头。对于Python 3.4(本题使用的版本),Magic Number的十六进制表示为EE 0C 0D 0A。但需要注意的是,有些情况下Magic Number可能会有所不同。

  3. 复制正确的文件头:从struct文件的开头复制前8个字节(Magic Number + 时间戳),然后粘贴到src文件的开头,覆盖原有的内容。

  4. 保存并重命名文件:将修复后的src文件保存,并为其添加.pyc后缀,得到src.pyc

为了更直观地展示修复过程,下面是一个修复前后的对比示例:

# 修复前的src文件(开头部分):
E3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
...

# 修复后的src.pyc文件(开头部分):
EE 0C 0D 0A 5F 2B 1F 5F E3 00 00 00 00 00 00 00
...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值