Dekompilieren kompilierter Python-Binaries (exe, elf) - Aus .pyc wiederherstellen
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Vom kompilierten Binary zur .pyc
Aus einem mit ELF kompilierten Binary kannst du die .pyc wie folgt extrahieren:
pyi-archive_viewer <binary>
# The list of python modules will be given here:
[(0, 230, 311, 1, 'm', 'struct'),
(230, 1061, 1792, 1, 'm', 'pyimod01_os_path'),
(1291, 4071, 8907, 1, 'm', 'pyimod02_archive'),
(5362, 5609, 13152, 1, 'm', 'pyimod03_importers'),
(10971, 1473, 3468, 1, 'm', 'pyimod04_ctypes'),
(12444, 816, 1372, 1, 's', 'pyiboot01_bootstrap'),
(13260, 696, 1053, 1, 's', 'pyi_rth_pkgutil'),
(13956, 1134, 2075, 1, 's', 'pyi_rth_multiprocessing'),
(15090, 445, 672, 1, 's', 'pyi_rth_inspect'),
(15535, 2514, 4421, 1, 's', 'binary_name'),
...
? X binary_name
to filename? /tmp/binary.pyc
In einer kompilierten python exe binary kann man die .pyc erhalten, indem man Folgendes ausführt:
python pyinstxtractor.py executable.exe
Von .pyc zu python code
Für die .pyc Daten (“kompiliertes” python) solltest du versuchen, den ursprünglichen python code zu extrahieren:
uncompyle6 binary.pyc > decompiled.py
Stelle sicher, dass die Binärdatei die Erweiterung “.pyc” hat (falls nicht, wird uncompyle6 nicht funktionieren)
Beim Ausführen von uncompyle6 können die folgenden Fehler auftreten:
Error: Unknown magic number 227
/kali/.local/bin/uncompyle6 /tmp/binary.pyc
Unknown magic number 227 in /tmp/binary.pyc
Um das zu beheben, müssen Sie die korrekte magic number am Anfang der generierten Datei hinzufügen.
Magic numbers variieren je nach python-Version, um die magic number von python 3.8 zu erhalten, müssen Sie ein python 3.8 Terminal öffnen und folgendes ausführen:
>> import imp
>> imp.get_magic().hex()
'550d0d0a'
Die magic number in diesem Fall für python3.8 ist 0x550d0d0a. Um diesen Fehler zu beheben, müssen Sie am Anfang der .pyc file die folgenden Bytes hinzufügen: 0x0d550a0d000000000000000000000000
Sobald Sie diesen magic header hinzugefügt haben, sollte der Fehler behoben sein.
So sieht ein korrekt hinzugefügter .pyc python3.8 magic header aus:
hexdump 'binary.pyc' | head
0000000 0d55 0a0d 0000 0000 0000 0000 0000 0000
0000010 00e3 0000 0000 0000 0000 0000 0000 0000
0000020 0700 0000 4000 0000 7300 0132 0000 0064
0000030 0164 006c 005a 0064 0164 016c 015a 0064
Fehler: Generische Decompilierungsfehler
Andere Fehler wie: class 'AssertionError'>; co_code should be one of the types (<class 'str'>, <class 'bytes'>, <class 'list'>, <class 'tuple'>); is type <class 'NoneType'> können auftreten.
Dies bedeutet wahrscheinlich, dass du die magic number nicht korrekt hinzugefügt hast oder nicht die korrekte magic number verwendet hast, also stelle sicher, dass du die korrekte verwendest (oder versuche eine andere).
Siehe die vorherige Fehlerdokumentation.
Automatisches Tool
Die python-exe-unpacker tool dient als Kombination mehrerer in der Community verfügbarer Tools, die Forschende beim Entpacken und Decompilieren von in Python geschriebenen Executables unterstützen, insbesondere solchen, die mit py2exe und pyinstaller erstellt wurden. Es enthält YARA-Regeln, um zu identifizieren, ob ein Executable Python-basiert ist und um das Erstellungstool zu bestätigen.
ImportError: File name: ‘unpacked/malware_3.exe/pycache/archive.cpython-35.pyc’ doesn’t exist
Ein häufiges Problem ist eine unvollständige Python-Bytecode-Datei, die durch den Entpackungsprozess mit unpy2exe oder pyinstxtractor entsteht und dann von uncompyle6 wegen einer fehlenden Python-Bytecode-Versionsnummer nicht erkannt wird. Um dies zu beheben, wurde eine prepend-Option hinzugefügt, die die benötigte Python-Bytecode-Versionsnummer anfügt und so den Decompilierungsprozess erleichtert.
Beispiel für das Problem:
# Error when attempting to decompile without the prepend option
test@test: uncompyle6 unpacked/malware_3.exe/archive.py
Traceback (most recent call last):
...
ImportError: File name: 'unpacked/malware_3.exe/__pycache__/archive.cpython-35.pyc' doesn't exist
# Successful decompilation after using the prepend option
test@test:python python_exe_unpack.py -p unpacked/malware_3.exe/archive
[*] On Python 2.7
[+] Magic bytes are already appended.
# Successfully decompiled file
[+] Successfully decompiled.
Analyse von python assembly
Wenn Sie den python “Original”-code mit den vorherigen Schritten nicht extrahieren konnten, können Sie versuchen, die assembly zu extrahieren (aber es ist nicht sehr aussagekräftig, also versuchen Sie, den Originalcode erneut zu extrahieren). In here habe ich einen sehr einfachen Code gefunden, um die .pyc Binärdatei zu disassemble (viel Glück beim Verstehen des code flow). Wenn die .pyc von python2 ist, verwenden Sie python2:
Disassemble a .pyc
```python >>> import dis >>> import marshal >>> import struct >>> import imp >>> >>> with open('hello.pyc', 'r') as f: # Read the binary file ... magic = f.read(4) ... timestamp = f.read(4) ... code = f.read() ... >>> >>> # Unpack the structured content and un-marshal the code >>> magic = struct.unpack(' at 0x7fd54f90d5b0, file "hello.py", line 1>)
>>>
>>> # Verify if the magic number corresponds with the current python version
>>> struct.unpack('>>
>>> # Disassemble the code object
>>> dis.disassemble(code)
1 0 LOAD_CONST 0 ()
3 MAKE_FUNCTION 0
6 STORE_NAME 0 (hello_world)
9 LOAD_CONST 1 (None)
12 RETURN_VALUE
>>>
>>> # Also disassemble that const being loaded (our function)
>>> dis.disassemble(code.co_consts[0])
2 0 LOAD_CONST 1 ('Hello {0}')
3 LOAD_ATTR 0 (format)
6 LOAD_FAST 0 (name)
9 CALL_FUNCTION 1
12 PRINT_ITEM
13 PRINT_NEWLINE
14 LOAD_CONST 0 (None)
17 RETURN_VALUE
```
PyInstaller raw marshal & Pyarmor v9 statischer Entpack-Workflow
- Eingebettete marshal-Blobs extrahieren:
pyi-archive_viewer sample.exe und rohe Objekte exportieren (z. B. eine Datei namens vvs). PyInstaller speichert nackte marshal-Streams, die mit 0xe3 (TYPE_CODE mit FLAG_REF) beginnen, anstatt vollständiger .pyc-Dateien. Füge den korrekten 16-Byte .pyc Header (magic für die eingebettete Interpreter-Version + nullgesetzter Zeitstempel/Größe) vor dem marshal-Payload ein, damit Decompiler ihn akzeptieren. Für Python 3.11.5 kannst du das magic via imp.get_magic().hex() holen und es mit dd/printf vor dem marshal-Payload patchen.
- Mit versionsbewussten Tools dekompilieren:
pycdc -c -v 3.11.5 vvs.pyc > vvs.py oder PyLingual. Wenn nur Teilcode benötigt wird, kannst du den AST traversieren (z. B. ast.NodeVisitor), um bestimmte Argumente/Konstanten zu extrahieren.
- Den Pyarmor v9 Header parsen, um Krypto-Parameter zu rekonstruieren: Signatur
PY<license> bei 0x00, Python major/minor bei 0x09/0x0a, Protection Type 0x09 wenn BCC aktiviert ist (0x08 sonst), ELF Start/End Offsets bei 0x1c/0x38 und die 12-Byte AES-CTR-Nonce verteilt über 0x24..0x27 und 0x2c..0x33. Dasselbe Muster wiederholt sich nach dem eingebetteten ELF.
- Pyarmor-modifizierte Codeobjekte berücksichtigen:
co_flags hat Bit 0x20000000 gesetzt und ein zusätzliches length-prefixed Feld. Deaktiviere CPython deopt_code() während des Parsings, um Dekryptionsfehler zu vermeiden.
- Verschlüsselte Codebereiche identifizieren: Bytecode ist umschlossen von
LOAD_CONST __pyarmor_enter_*__ … LOAD_CONST __pyarmor_exit_*__. Entschlüssele den eingeschlossenen Blob mit AES-128-CTR unter Verwendung des Runtime-Keys (z. B. 273b1b1373cf25e054a61e2cb8a947b8). Leite die regionsspezifische Nonce ab, indem du den payload-spezifischen 12-Byte XOR-Schlüssel (aus dem Pyarmor-Runtime) mit den 12 Bytes im __pyarmor_exit_*__-Marker per XOR kombinierst. Nach der Entschlüsselung siehst du möglicherweise auch __pyarmor_assert_*__ (verschlüsselte Strings) und __pyarmor_bcc_*__ (kompilierte Dispatch-Ziele).
- Pyarmor „mixed“ Strings entschlüsseln: Konstanten mit Präfix
0x81 sind AES-128-CTR-verschlüsselt (Klartext nutzt 0x01). Verwende denselben Schlüssel und die runtime-abgeleitete String-Nonce (z. B. 692e767673e95c45a1e6876d), um lange String-Konstanten wiederherzustellen.
- BCC-Modus handhaben: Pyarmor
--enable-bcc kompiliert viele Funktionen in ein Begleit-ELF und lässt Python-Stubs zurück, die __pyarmor_bcc_*__ aufrufen. Mappe diese Konstanten auf ELF-Symbole mit Tools wie bcc_info.py, und dekompiliere/analysiere dann das ELF an den gemeldeten Offsets (z. B. __pyarmor_bcc_58580__ → bcc_180 bei Offset 0x4e70).
Python zu ausführbaren Programmen
Zu Beginn zeigen wir, wie Payloads mit py2exe und PyInstaller kompiliert werden können.
Erstellen eines Payloads mit py2exe:
- Installiere das py2exe-Paket von http://www.py2exe.org/
- Für das Payload (in diesem Fall nennen wir es hello.py) verwende ein Script wie das in Abbildung 1. Die Option “bundle_files” mit dem Wert 1 bündelt alles, einschließlich des Python-Interpreters, in eine einzige exe.
- Sobald das Script fertig ist, führen wir den Befehl “python setup.py py2exe” aus. Dadurch wird die ausführbare Datei erstellt, wie in Abbildung 2.
from distutils.core import setup
import py2exe, sys, os
sys.argv.append('py2exe')
setup(
options = {'py2exe': {'bundle_files': 1}},
#windows = [{'script': "hello.py"}],
console = [{'script': "hello.py"}],
zipfile = None,
)
C:\Users\test\Desktop\test>python setup.py py2exe
running py2exe
*** searching for required modules ***
*** parsing results ***
*** finding dlls needed ***
*** create binaries ***
*** byte compile python files ***
*** copy extensions ***
*** copy dlls ***
copying C:\Python27\lib\site-packages\py2exe\run.exe -> C:\Users\test\Desktop\test\dist\hello.exe
Adding python27.dll as resource to C:\Users\test\Desktop\test\dist\hello.exe
Um ein payload mit PyInstaller zu erstellen:
- Installiere PyInstaller mit pip (pip install pyinstaller).
- Danach geben wir den Befehl “pyinstaller –onefile hello.py” aus (zur Erinnerung: ‘hello.py’ ist unser payload). Dadurch wird alles in eine ausführbare Datei gebündelt.
C:\Users\test\Desktop\test>pyinstaller --onefile hello.py
108 INFO: PyInstaller: 3.3.1
108 INFO: Python: 2.7.14
108 INFO: Platform: Windows-10-10.0.16299
………………………………
5967 INFO: checking EXE
5967 INFO: Building EXE because out00-EXE.toc is non existent
5982 INFO: Building EXE from out00-EXE.toc
5982 INFO: Appending archive to EXE C:\Users\test\Desktop\test\dist\hello.exe
6325 INFO: Building EXE from out00-EXE.toc completed successfully.
Quellen
- https://blog.f-secure.com/how-to-decompile-any-python-binary/
- VVS Discord Stealer Using Pyarmor for Obfuscation and Detection Evasion
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.