> For the complete documentation index, see [llms.txt](https://docs.elow.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.elow.dev/blog-entries/pyinstaller.md).

# PyInstaller

## What is PyInstaller?

[PyInstaller](https://pyinstaller.org/en/stable/) bundles everything needed to run a python application into a single executable.

While Python itself already has a bunch of functionalities, developers commonly use other packages (pre-written code) to enhance their own Python project.&#x20;

After implementing an external package, the Python project will need to include that package as its dependency to successfully run. This can be a nightmare to manage if the Python application will be widely deployed to different hosts.

It is not guaranteed that hosts will have the appropriate Python version or external packages installed for the application to run without any issues. This is when PyInstaller comes in to mitigate this headache as it wraps everything nicely into a single file.

Sounds like a great tool for developers and end users right?

Here comes the bad news...

Threat actors are abusing this tool to bundle up their nasty code into a single executable too.

<figure><img src="/files/O36hNmQZGYNBy9iJPj1v" alt=""><figcaption></figcaption></figure>

***

## How do I know if a PyInstaller file is bad?

Easy! We just have to extract and decompile the PyInstaller .EXE to review the source code.

Let's do a quick demo.

### Required Tools

Extractor:

* [PyInstaller Extractor](https://github.com/extremecoders-re/pyinstxtractor)

Decompilers:

* [PyLingual - Online Tool](https://pylingual.io/)
* [Uncomplye6](https://github.com/rocky/python-uncompyle6/)
* [Decompyler++](https://github.com/zrax/pycdc)

{% hint style="info" %}
SHA256 used in this demo -`104e350f0bf3fd6ac43c12a0ec6c905da987462acceaecc92eaa5ec66c7d0d3e`
{% endhint %}

***

### Extract

First thing we need to do is to extract the Python files from the executable.

```python
python pyinstxtractor.py <executable_name>
```

<figure><img src="/files/kEGgABXTydHfjUnibDzN" alt=""><figcaption></figcaption></figure>

Once we have extracted it with the tool, let's head into the extracted folder to see if we can find anything. Within this folder, you will notice a lot of different legitimate Python dependencies that were bundled into the malicious PyInstaller .EXE.

Typically, the nasty stuff will be within a `.PYC` file. The `.PYC` files are not human-readable as they are compiled Python bytecodes. Think of it as machine code that only the machine can understand.

<figure><img src="/files/OVQicOS8tGDq2iJwegbk" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/Xy3MgfinjnWaHb7WEjxL" alt=""><figcaption></figcaption></figure>

***

### Decompile

Now, time to decompile those Python bytecodes and dig into the interesting stuff. Just a heads up, decompiler tools for Python are version dependant. It's best to do some quick research to determine a compatible decompiler.

To save us some headache, I've opted to use an online Python decompiler - <https://pylingual.io/> instead that supports most Python versions.

{% hint style="danger" %}
It is recommended to use locally hosted decompiler tools (such as Decompyler++ or Uncomplye6) when analysing non-public artifacts for operational security.
{% endhint %}

<figure><img src="/files/09TGDK1I9YKWc11jybpz" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/HePzRFcHmv8M8PeBqpUH" alt=""><figcaption></figcaption></figure>

And there we go!&#x20;

We now have the malicious Python source code for further analysis.
