Reversing Python Logo

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 or PYTHONSCRIPT resources
  • Presence of library.zip or PYTHON27.DLL
  • A PYZ or PKG 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:

dir listing

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.

source code

📎 References & Further Reading


Share on: