Reversing Python Exes
Python Exes
Malicious actors are increasingly turning to Python as a weapon of choice for crafting sophisticated malware—thanks to its simplicity and rich library ecosystem. But distributing raw Python scripts is a red flag, so attackers often obfuscate and package their code using tools like py2exe. This Windows-only utility converts .py files into .exe binaries, effectively hiding the logic, embedding dependencies, and making reverse engineering trickier—but not impossible.
In this post, we dive into the inner workings of py2exe, explore how malware authors leverage it to conceal functionality, and walk through the reverse engineering process to unpack and analyze such executables. Along the way, we’ll discuss:
- The structure of a py2exe-generated EXE
- Common obfuscation techniques
- Tools and techniques to recover readable Python source
- Real-world examples and red flags
Whether you’re a malware analyst or just curious about how Python EXEs work under the hood, this guide will give you a hands-on look at how to dismantle the illusion of protection.
Understanding pyinstaller, py2exe, and MEIPASS2: Converting Python Scripts to EXEs
If you’re working with Python and need to distribute your code as a standalone executable, tools like pyinstaller and py2exe are your go-to solutions. These tools bundle your Python scripts, the Python interpreter, and all dependencies into a single .exe file that can run on Windows systems without requiring Python to be installed.
🛠 How it Works
pyinstaller is the most popular tool. It analyzes your script and packages everything into a self-contained EXE.
It can operate in two modes:
- one-folder (output is a folder of files including the EXE)
- and one-file (a single executable that extracts itself at runtime).
🧪 What’s with MEIPASS2?
When you compile with –onefile, pyinstaller creates a temporary folder where it extracts all your bundled files before launching your app. This temporary directory is referenced by the _MEIPASS (or MEIPASS2 in older versions) environment variable. This is crucial for internal access to files like bundled assets or modules.
So when you’re reverse engineering a pyinstaller EXE, you’re often looking for:
- The _MEIPASS path.
- Embedded .pyc files (compiled Python files).
- Signs of encryption or compression that hide the source.
🔧 Tools You’ll Need
- python-decompile3 — CLI decompiler for
.pyc
files, supporting newer Python versions. - pylingual.io — a web GUI for quick
.pyc
to.py
conversion. - pyinstxtractor — (optional) for extracting embedded
.pyc
files from PyInstaller packages. - A hex editor and/or unpacker (e.g., PE Explorer or Detect It Easy).
- Python (ideally same version used to build EXE, for
.pyc
compatibility).
🔍 Step 1: Inspect the Binary
Start by scanning the executable using a tool like Detect It Easy. Identify whether the EXE was built with py2exe
, pyinstaller
, or another packer.
Check for:
- Embedded
.zip
files orPYTHONSCRIPT
resources - Presence of
library.zip
orPYTHON27.DLL
- A
PYZ
orPKG
block inside
🪓 Step 2: Extract Embedded .pyc Files
If using py2exe
, you can often manually extract .pyc
files from library.zip
, or look for them inside the resource section of the PE.
Once located:
unzip library.zip
Save the .pyc to a folder for decompilation
Later versions of python may require pyinstxtractor-ng[https://github.com/pyinstxtractor/pyinstxtractor-ng]
Running pyinstxtractor-ng, should auto-magically, extract all the components into an _extracted directory.
🔁 Step 3: Decompile with python-decompile3
Install the tool:
pip install git+https://github.com/rocky/python-decompile3.git
Decompile each .pyc:
decompile3 my_script.pyc > my_script_decompiled.py
✅ Supports many newer Python versions and preserves structure better than older tools.
🌐 Step 4: Use Pylingual for Quick Conversions
Alternatively, drag and drop .pyc files into pylingual.io. It will auto-detect the Python version and show you the decompiled source code in your browser.
This is especially helpful if:
- You’re not sure what Python version was used.
- You want to preview code quickly before deeper analysis.
Example
Running pyextractor-ng
Running pyinstxtractor-ng on a random python exe sample, may look like:
└── extracted/
├── PYZ-00.pyz_extracted/
├── mymodule.pyc
├── crypto.dll
├── utils.pyc
└── library.zip
Where as a real-world example, extracted content may look similar to:
As you can see the pyinstaller/py2exe pulls in all dependancies so that the program can run neatly on other computers.
There is no source-code available only Shared Objects/DLLs and .pyc’s.
We then have to decompile the pyc files to retrieve a rough comparision of the original source code.
Note: as we are reversing python byte-code, the reversing process is not exact, the program could be broken and un runnable (if its malware we may not want to run it anyway, but sometime sandbox analysis can reveal operational TTPs as a quick dirty intel grab).
Decompile and https://pylingual.io/
We can then decompile the pyc to get an idea of the original source code in order to:
- Understand the pythons operation
- Recover secrets
- Command & Control (C2) infrastructure
- Look for hardcoded IPs, URLs, or domains.
- Credential theft mechanisms
- Code interfacing with browsers, file systems, or keyloggers.
- Persistence techniques
- Registry manipulation, scheduled tasks, or autoruns.
- Dropped payloads
- Look for open(), write(), or file unpacking logic.
- Obfuscation or anti-analysis checks
- Functions like os.getenv, anti-VM checks, or suspicious imports.
📎 References & Further Reading
- python-decompile3 GitHub
- pyinstxtractor-ng
- pylingual.io
- py2exe Project
- Reverse Engineering PyInstaller Malware
- Chatgpt.com Image Generation
Share on: