How to Decompile a Python .exe Back to Python Source Code
This guide explains how to extract .pyc files from a Python‑generated executable, restore missing bytecode headers, and use tools such as pyinstxtractor, pyi‑archive_viewer, and uncompyle6 to decompile the bytecode back into readable Python scripts while highlighting common pitfalls and protection mechanisms.
Introduction
Decompiling a Python executable (.exe) back to its original Python script is a useful technique for understanding program logic, but it can be challenging because tools like PyInstaller or cx_Freeze strip metadata and compress the bytecode.
Version
Python 3.9
Decompilation Overview
Python source is compiled to bytecode (.pyc). When packaged into an .exe, the bytecode is stored inside a CArchive created by PyInstaller or similar tools. To recover the source you must extract the .pyc files and then decompile them.
Extracting .pyc Files
The pyi-archive_viewer utility bundled with PyInstaller can directly view and extract the embedded .pyc files from an .exe. Official documentation is available at the PyInstaller website.
<code># 使用 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'
... (truncated for brevity) ...
</code>For larger projects, the open‑source Python-exe-unpacker project provides the pyinstxtractor.py script, which automates extraction of all embedded files.
<code>\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
</code>Restoring Missing Bytecode Headers
Extracted .pyc files from PyInstaller miss the first 16 bytes (magic number and timestamp). Without these bytes, tools like uncompyle6 raise an "Unknown magic number" error. You can prepend the correct header using a hex‑capable editor (e.g., UltraEdit) or a small Python script.
<code>//读取从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())
</code>Non‑entry modules miss a different number of bytes; a similar script can restore them by inserting the appropriate padding.
<code># 补全非入口类文件
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())
</code>Decompiling the Restored .pyc Files
Install uncompyle6 and run it against the repaired .pyc files:
<code>pip install uncompyle6
uncompyle6 ./pycfile_tmp/main.pyc > main.py
# Example output header
# 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
</code>Note that uncompyle6 may not support certain Python versions (e.g., 3.9.0) for decompilation; you may need to try a different decompiler or adjust the Python version.
Online Decompilation Tools
Web‑based services such as ctfever can also decompile .pyc files without installing anything locally.
Potential Issues
The extracted PYZ-00.pyz_extracted directory may be empty if the Python version used to build the .exe differs from the version used for extraction (e.g., building with Python 2.7 but extracting with Python 3.12).
Using the --key option in newer PyInstaller versions encrypts the bytecode, producing .pyc.encrypted files that cannot be decompiled directly. Bytecode encryption was removed in PyInstaller v6.0.
Conclusion
Decompiling Python executables helps reveal program logic and algorithms, but the process can be limited by packaging choices, encryption, and version mismatches. Ethical and legal considerations should always guide reverse‑engineering activities.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.