百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

Python exe 文件反编译为 Python 脚本

liuian 2025-03-01 14:37 45 浏览

前言

  • Python 可执行文件(.exe)反编译为 Python 脚本是一项有趣的技术挑战,可以帮助我们理解程序的工作原理,以及可能包含的逻辑和算法。虽然反编译不是一项简单的任务,并且对于使用各种保护措施的程序可能无效,但对于一般情况下的 Python 可执行文件,我们可以尝试使用一些工具来进行反编译。
  • 下面我们就来学习如何将 Python 可执行文件(.exe)反编译为 Python 脚本。

版本

  • Python 3.9

反编译

  • 反编译是将已编译的程序代码还原为其原始源代码的过程。在 Python 中,由于其解释性质,通常没有像编译语言那样生成的二进制文件,但是我们可以将 Python 脚本转换为字节码文件(.pyc),而 .exe 文件通常是由 pyinstaller、cx_Freeze 等工具编译生成的。

Python 可执行文件(.exe)反编译

  • Python 可执行文件(.exe)反编译为 Python 脚本主要分为两个步骤,(1)从 .exe 文件中提取 pyc 文件 (2)将 pyc 文件转换为 Python 脚本。

打包一个简单的 .exe 可执行文件

# student.py
class Student:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    def get_name(self):
        return self.name

    def get_age(self):
        return self.age

    def get_gender(self):
        return self.gender

    def set_name(self, name):
        self.name = name

    def set_age(self, age):
        self.age = age

    def set_gender(self, gender):
        self.gender = gender

    def display_info(self):
        print("Name:", self.name)
        print("Age:", self.age)
        print("Gender:", self.gender)

# main.py
import time

from student import Student

if __name__ == "__main__":
    # Create a student object
    student1 = Student("Alice", 20, "Female")

    # Display student information
    student1.display_info()

    # Update student information
    student1.set_age(21)
    student1.display_info()

    time.sleep(10)

# 使用 pyinstaller 构建可执行 .exe
pyinstaller --onefile   -p venv/Lib/site-packages .\print-student\main.py

提取 pyc 文件

使用脚本提取

  • pyi-archive_viewerPyInstaller 自己提供的工具,它可以直接提取打包结果exe中的pyc文件。
  • 详细介绍可参考官方文档:https://pyinstaller.readthedocs.io/en/stable/advanced-topics.html#using-pyi-archive-viewer
# 使用 pyi-archive_viewer 查看文件并提取
> pyi-archive_viewer .\main.exe

Options in 'main.exe' (PKG/CArchive):
 pyi-contents-directory _internal
Contents of 'main.exe' (PKG/CArchive):
 position, length, uncompressed_length, is_compressed, typecode, name
 0, 199, 269, 1, 'm', 'struct'
 199, 2008, 3700, 1, 'm', 'pyimod01_archive'
 2207, 7671, 17413, 1, 'm', 'pyimod02_importers'
 9878, 1760, 4029, 1, 'm', 'pyimod03_ctypes'
 11638, 644, 1074, 1, 'm', 'pyimod04_pywin32'
 12282, 603, 851, 1, 's', 'pyiboot01_bootstrap'
 12885, 229, 295, 1, 's', 'main'
......
 4721057, 408332, 1123832, 1, 'b', 'unicodedata.pyd'
 5129389, 702999, 702999, 0, 'z', 'PYZ-00.pyz'
?
U: go up one level
O : open embedded archive with given name // 打开包查看文件
X : extract file with given name // 提取文件
S: list the contents of current archive again
Q: quit
? x main        
Output filename? main.pyc
? o PYZ-00.pyz
Contents of 'PYZ-00.pyz' (PYZ):
 is_package, position, length, name
 0, 17, 2647, '_compat_pickle'
......
 0, 543553, 531, 'student'
 0, 544084, 19733, 'subprocess'
 0, 563817, 27425, 'tarfile'
 0, 591242, 5936, 'textwrap'
 0, 597178, 15612, 'threading'
 0, 612790, 1398, 'token'
 0, 614188, 8969, 'tokenize'
 0, 623157, 6659, 'tracemalloc'
 0, 629816, 27711, 'typing'
 1, 657527, 70, 'urllib'
 0, 657597, 13861, 'urllib.parse'
 0, 671458, 2188, 'uu'
 0, 673646, 26812, 'zipfile'
? x student
Output filename? student.pyc
? ls
U: go up one level
O : open embedded archive with given name
X : extract file with given name
S: list the contents of current archive again
Q: quit
? q
  • 在上面的操作中,我们使用 pyi-archive_viewer 提取了 main.pyc、和 student.pyc 文件,当时大家可以很清楚的看到弊端,即需要一个一个手动提取,对于大项目这是十分麻烦的,推荐使用下面的工具提取。

使用工具提取

  • 我们可以使用开源项目 Python-exe-unpacker 中的脚本 pyinstxtractor.py 脚本进行提取,地址:https://github.com/countercept/Python-exe-unpacker
\print-student> Python pyinstxtractor.py .\main.exe                                            
DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
[*] Processing .\main.exe
[*] Pyinstaller version: 2.1+
[*] Python version: 309
[*] Length of package: 5835756 bytes
[*] Found 59 files in CArchive
[*] Beginning extraction...please standby
[*] Found 81 files in PYZ archive
[*] Successfully extracted pyinstaller archive: .\main.exe

You can now use a python decompiler on the pyc files within the extracted directory

将 .pyc 文件转换为 Python 脚本

入口运行类

  • 对于从 pyinstaller 提取出来的 pyc 文件并不能直接反编译,入口运行类共16字节的 magic 和 时间戳被去掉了。如果直接进行反编译,例如执行 uncompyle6 main.pyc,则会报出如下错误:
ImportError: Unknown magic number 227 in main.pyc
  • 我们可以使用支持16进制编辑的文本编辑器进行处理,比如:UltraEdit32
  • 可以看到前16个字节都被去掉了,其中前四个字节是magic,这四个字节会随着系统和Python版本发生变化,需要保持一致。后四个字节包括时间戳和一些其他的信息,都可以随意填写。我们可以通过 UltraEdit32 向提取的文件添加回信息。
  • 这里我写了一个 python 脚本实现这个过程:
// 读取从pyz目录抽取的pyc文件的前4个字节作基准
pyz_dir = "./main.exe_extracted/PYZ-00.pyz_extracted"
for pyc_file in os.listdir(pyz_dir):
    if pyc_file.endswith(".pyc"):
        file = f"{pyz_dir}/{pyc_file}"
        break
with open(file, "rb") as f:
    head = f.read(4)

// 补全入口类文件
if os.path.exists("pycfile_tmp"):
    shutil.rmtree("pycfile_tmp")
os.mkdir("pycfile_tmp")
main_file_result = "pycfile_tmp/main.pyc"
with open("./main.exe_extracted/main.pyc", "rb") as read, open(main_file_result, "wb") as write:
    write.write(head)
    write.write(b"\0" * 12)
    write.write(read.read())

非入口运行类

  • 对于非入口运行的pyc文件从12字节开始缺4个字节。
# 补全非入口类文件
pyz_dir = "main.exe_extracted/PYZ-00.pyz_extracted"
for pyc_file in os.listdir(pyz_dir):
    pyc_file_src = f"{pyz_dir}/{pyc_file}"
    pyc_file_dest = f"pycfile_tmp/{pyc_file}"
    print(pyc_file_src, pyc_file_dest)
    with open(pyc_file_src, "rb") as read, open(pyc_file_dest, "wb") as write:
        write.write(read.read(12))
        write.write(b"\0"*4)
        write.write(read.read())

转换补全后的 pyc 文件

uncompyle6 反编译

pip install uncompyle6
uncompyle6 xxx.pyc>xxx.py

如:uncompyle6 .\pycfile_tmp\main.pyc
# uncompyle6 version 3.9.0
# Python bytecode version base 3.9.0 (3425)
# Decompiled from: Python 3.9.13 (tags/v3.9.13:6de2ca5, May 17 2022, 16:36:42) [MSC v.1929 64 bit (AMD64)]
# Embedded file name: main.py

Unsupported Python version, 3.9.0, for decompilation


# Unsupported bytecode in file .\pycfile_tmp\main.pyc
# Unsupported Python version, 3.9.0, for decompilation
  • 由于我使用的是 3.9.0 版本,uncompyle6 不再支持 decompilation,有兴趣的朋友可以去试试。

在线工具

  • 我们也可以使用一些在线工具进行解密,比如:https://ctfever.uniiem.com/tools/pyc-decompiler

可能遇到的问题

PYZ-00.pyz_extracted 文件为空

  • 构建 .exe 文件 Python 版本和解压包时使用的版本不一致,比如我使用 Python 2.7 进行解包:
>Python .\pyinstxtractor.py .\main.exe

[*] Processing .\main.exe
[*] Pyinstaller version: 2.1+
[*] Python version: 312
[*] Length of package: 7675728 bytes
[*] Found 60 files in CArchive
[*] Beginning extraction...please standby
[!] Warning: The script is running in a different python version than the one used to build the executable
    Run this script in Python312 to prevent extraction errors(if any) during unmarshalling
[!] Unmarshalling FAILED. Cannot extract PYZ-00.pyz. Extracting remaining files.
[*] Successfully extracted pyinstaller archive: .\main.exe

You can now use a python decompiler on the pyc files within the extracted directory

# 查看解压后的文件
\print-student\main.exe_extracted\PYZ-00.pyz_extracted> ls
\print-student\main.exe_extracted\PYZ-00.pyz_extracted>

如何防止exe被反编译

  • 我们可以在打包命令后面添加 --key 参数来进行加密,例如:
 pyinstaller --onefile   -p venv/Lib/site-packages .\print-student\main.py --key '1234'
  • 再次解压,抽取的中间结果变为了 .pyc.encrypted,无法正常反编译。

思考

  • Bytecode encryption was removed in PyInstaller v6.0. Please remove your --key=xxx argument. For the rationale and alternatives see https://github.com/pyinstaller/pyinstaller/pull/6999
  • 可以看到在 PyInstaller v6.0 加密参数已经被废弃,大家可以思考一下原因。

总结

  • 反编译 Python 可执行文件可以帮助我们理解程序的工作原理和逻辑,但在实践中可能会受到许多因素的限制。对于复杂的程序,反编译可能只是了解其工作原理的第一步,可能需要进一步的分析和研究。最后,我们需要明白技术没有好坏,需要谨守道德和法律的底线。

相关推荐

怎么下载驱动精灵(驱动精灵怎么下载想要的驱动)

下载驱动精灵到U盘步骤如下:1.首先,您需要先准备一个U盘,并将其插入电脑上。注意,将U盘插入电脑时,应该确保电脑识别到了U盘设备,否则您可能需要检查一下您的U盘是否存在问题或者更换一个U盘。2....

小米ax6000路由器怎么设置(小米ax6000路由器如何设置)
  • 小米ax6000路由器怎么设置(小米ax6000路由器如何设置)
  • 小米ax6000路由器怎么设置(小米ax6000路由器如何设置)
  • 小米ax6000路由器怎么设置(小米ax6000路由器如何设置)
  • 小米ax6000路由器怎么设置(小米ax6000路由器如何设置)
电脑桌面图标设置(电脑桌面图标设置自动排列)

电脑的桌面图标设置包括随意摆放图标,调整图标大小及排列顺序等,那么电脑桌面图标怎么设置呢?下面就以iOS13系统版本的iPhone8Plus手机为例来为你解答,一起来看看吧!首先使用鼠标右键单击电...

ps序列号是什么(ps序列号是什么开头的)

ps序列号是AdobePhotoshop软件为了防止盗版而采取的保护措施。序列号有时也指“机器码”,是有些软件为了防止盗版而采取的保护措施。但网络上往往会有注册机等类似软件用以免费获得许可。序列号就...

腾达路由器找不到wifi(腾达路由器找不到高级设置)

如果你的腾达路由器没有wifi信号,可能是未启用wifi功能,或者设置了隐藏wifi,当然也有可能是路由器的wifi功能坏掉了,可以先登录到它的设置页面,正确配置wifi。1.关闭了指示灯有些型号的...

win7如何分区电脑硬盘(win7怎么分硬盘)

在Win7中,你可以通过打开“计算机”窗口,右键点击你要分区的磁盘,然后选择“管理”选项。接着在弹出的“计算机管理”窗口中,找到“存储”下的“磁盘管理”选项,右键点击你要分区的磁盘,在弹出的菜单选择“...

电脑打不开文档和表格怎么办

原因是电脑软件问题。根据你的描述,电脑做了注册表清理,Word文档和Excel都打不开了。原因是:文件关联被删除了。解决方法是:1,打开Word软件,然后在里面选打开找到Word文档,确认就自动打开关...

路由器的作用与功能通俗(路由器的作用与功能通俗讲解)
路由器的作用与功能通俗(路由器的作用与功能通俗讲解)

路由器的功能如下:第一,网络互连:路由器支持各种局域网和广域网接口,主要用于互连局域网和广域网,实现不同网络互相通信。第二,数据处理:提供包括分组过滤、分组转发、优先级、复用、加密、压缩和防火墙等功能。第三,网络管理:路由器提供包括路由器配...

2026-01-05 17:55 liuian

如何安装双系统win10和linux

1.首先在“我的电脑”桌面,用电脑键盘win+R键,进入运行界面,在“运行”中输入msconfig,然后点击“确定”,进入系统引导盘中。2.然后进入系统配置的界面后,点击界面上方的引导选项,进入。3....

ios用什么下载bt或磁力(ios手机用什么下载bt)

ios好用的磁力链接软件是迅雷。苹果商店下架了,可通过电脑在手机安装PP助手,手机打开PP助手,找到该软件后,从简介里找到历史版本,就可把经典的5135版本(520版本可能会闪退)下载回手机里即可。在...

好看免费的壁纸软件(好看免费的壁纸软件下载)
  • 好看免费的壁纸软件(好看免费的壁纸软件下载)
  • 好看免费的壁纸软件(好看免费的壁纸软件下载)
  • 好看免费的壁纸软件(好看免费的壁纸软件下载)
  • 好看免费的壁纸软件(好看免费的壁纸软件下载)
windows+r没反应(windows+l没反应)

原因:1、可能是键盘是没电。按下键盘左侧的大小写切换键CapsLock键,观察键盘上的指示灯Caps灯是否点亮。如能点亮,说明键盘的硬件很有可能已经损坏。如果不能点亮,则检查键盘与电脑主机连接口是否接...

一个电脑装两个显卡会怎么样

同一台主机内安装两块显卡,有两种可能:两块一模一样的、两块不一样的。  两块不一样的对电脑没有任何性能提升,唯一起的作用就是备份和双屏。可以将两台显示器分别接在这两个显卡上实现双屏输出,或者其备份作用...

为什么u盘在电脑上读不出来(为什么u盘的内容在电脑上读不出来)

U盘在电脑上读不出来可能有多种原因。以下是一些常见的问题和解决方法:U盘连接问题:首先,请确保U盘已正确连接到电脑的USB接口。尝试将U盘插入其他USB接口,或者尝试使用不同的USB线缆进行连接。驱动...

ios最新系统是多少(ios文件怎么装系统)

1.iOS13。 2.苹果手机现在的最新版本是iPhone11系列,iPhone11系列将会用上解锁更快更安全的3D结构光人脸解锁方式,搭载基于7纳米...3.iPhone11延续上一代1...