Decompile skompilowanych python binaries (exe, elf) - Pobierz z .pyc
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Z binarnego pliku skompilowanego do .pyc
Z skompilowanego pliku binarnego ELF możesz wydobyć .pyc za pomocą:
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
W skompilowanym python exe binary możesz get the .pyc, uruchamiając:
python pyinstxtractor.py executable.exe
Z .pyc do python code
Dla danych .pyc (“skompilowany” python) powinieneś zacząć od próby wyodrębnienia oryginalnego python code:
uncompyle6 binary.pyc > decompiled.py
Upewnij się, że binary ma rozszerzenie “.pyc” (jeśli nie, uncompyle6 nie będzie działać)
Podczas uruchamiania uncompyle6 możesz napotkać następujące błędy:
Error: Unknown magic number 227
/kali/.local/bin/uncompyle6 /tmp/binary.pyc
Unknown magic number 227 in /tmp/binary.pyc
Aby to naprawić, musisz dodać poprawną liczbę magiczną na początku wygenerowanego pliku.
Liczby magiczne różnią się w zależności od wersji python, aby uzyskać liczbę magiczną dla python 3.8 musisz otworzyć terminal python 3.8 i wykonać:
>> import imp
>> imp.get_magic().hex()
'550d0d0a'
W tym przypadku liczba magiczna dla python3.8 to 0x550d0d0a, więc aby naprawić ten błąd będziesz musiał dodać na początku pliku .pyc następujące bajty: 0x0d550a0d000000000000000000000000
Gdy dodasz ten nagłówek magiczny, błąd powinien zostać naprawiony.
Tak będzie wyglądać poprawnie dodany nagłówek magiczny .pyc python3.8:
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
Error: Decompiling generic errors
Inne błędy takie jak: class 'AssertionError'>; co_code should be one of the types (<class 'str'>, <class 'bytes'>, <class 'list'>, <class 'tuple'>); is type <class 'NoneType'> mogą się pojawić.
To prawdopodobnie oznacza, że nie dodałeś poprawnie liczby magicznej lub że nie użyłeś poprawnej liczby magicznej, więc upewnij się, że używasz właściwej (lub spróbuj innej).
Sprawdź wcześniejszą dokumentację błędów.
Automatic Tool
The python-exe-unpacker tool służy jako połączenie kilku dostępnych w społeczności narzędzi zaprojektowanych, aby pomóc badaczom w unpackingu i dekompilacji plików wykonywalnych napisanych w Pythonie, szczególnie tych stworzonych za pomocą py2exe i pyinstaller. Zawiera YARA rules do identyfikacji, czy plik wykonywalny jest oparty na Pythonie i potwierdza narzędzie użyte do jego stworzenia.
ImportError: File name: ‘unpacked/malware_3.exe/pycache/archive.cpython-35.pyc’ doesn’t exist
Częstym problemem jest niekompletny plik bajt-kodu Pythona wynikający z procesu unpacking z użyciem unpy2exe lub pyinstxtractor, który następnie nie zostaje rozpoznany przez uncompyle6 z powodu brakującego numeru wersji bajt-kodu Pythona. Aby to rozwiązać, dodano opcję prepend, która dopisuje niezbędny numer wersji bajt-kodu Pythona, ułatwiając proces dekompilacji.
Przykład problemu:
# 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.
Analiza python assembly
Jeśli nie udało Ci się wydobyć python “original” code wykonując poprzednie kroki, możesz spróbować extract assembly (ale it isn’t very descriptive, więc try ponownie wydobyć oryginalny kod). W here znalazłem bardzo prosty kod do disassemble binarki .pyc (powodzenia w zrozumieniu przepływu kodu). Jeśli .pyc pochodzi z python2, użyj 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
```
Surowy marshal PyInstaller i statyczny workflow rozpakowywania Pyarmor v9
- Extract embedded marshal blobs:
pyi-archive_viewer sample.exe i eksportuj surowe obiekty (np. plik vvs). PyInstaller przechowuje gołe strumienie marshal, które zaczynają się od 0xe3 (TYPE_CODE with FLAG_REF) zamiast pełnych plików .pyc. Dodaj na początku poprawny 16-bajtowy nagłówek .pyc (magic dla wersji osadzonego interpretera + wyzerowany timestamp/rozmiar), aby dekompilatory go akceptowały. Dla Python 3.11.5 możesz pobrać magic przez imp.get_magic().hex() i wstawić je za pomocą dd/printf przed payloadem marshal.
- Decompile with version-aware tools:
pycdc -c -v 3.11.5 vvs.pyc > vvs.py lub PyLingual. Jeśli potrzebny jest tylko fragment kodu, możesz przejść po AST (np. ast.NodeVisitor), żeby wyciągnąć konkretne argumenty/stałe.
- Parse the Pyarmor v9 header aby odzyskać parametry kryptograficzne: sygnatura
PY<license> znajduje się na 0x00, Python major/minor na 0x09/0x0a, typ ochrony 0x09 gdy BCC jest włączone (0x08 w przeciwnym razie), offsety początku/końca ELF na 0x1c/0x38, oraz 12-bajtowy nonce AES-CTR rozdzielony na 0x24..0x27 i 0x2c..0x33. Ten sam wzorzec powtarza się po osadzonym ELF.
- Account for Pyarmor-modified code objects:
co_flags ma ustawiony bit 0x20000000 oraz dodatkowe pole poprzedzone długością. Wyłącz CPython deopt_code() podczas parsowania, aby uniknąć błędów deszyfrowania.
- Identify encrypted code regions: bajtcode jest otoczony przez
LOAD_CONST __pyarmor_enter_*__ … LOAD_CONST __pyarmor_exit_*__. Zdeszyfruj zawarty blob przy użyciu AES-128-CTR z kluczem runtime (np. 273b1b1373cf25e054a61e2cb8a947b8). Wyprowadź per-region nonce przez XOR klucza XOR specyficznego dla payloadu (z runtime Pyarmor) z 12 bajtami w markerze __pyarmor_exit_*__. Po deszyfrowaniu możesz także zobaczyć __pyarmor_assert_*__ (zaszyfrowane stringi) i __pyarmor_bcc_*__ (skomplikowane cele dispatch).
- Decrypt Pyarmor “mixed” strings: stałe poprzedzone
0x81 są szyfrowane AES-128-CTR (plaintext używa 0x01). Użyj tego samego klucza i runtime-derived string nonce (np. 692e767673e95c45a1e6876d), aby odzyskać długie stałe tekstowe.
- Handle BCC mode: Pyarmor
--enable-bcc kompiluje wiele funkcji do towarzyszącego ELF i zostawia stuby Pythona wywołujące __pyarmor_bcc_*__. Zmapuj te stałe na symbole ELF za pomocą narzędzi takich jak bcc_info.py, a następnie zdekompiluj/analizuj ELF na zgłoszonych offsetach (np. __pyarmor_bcc_58580__ → bcc_180 pod offsetem 0x4e70).
Python to Executable
Na początek pokażemy, jak payloads mogą być kompilowane w py2exe i PyInstaller.
To create a payload using py2exe:
- Zainstaluj pakiet py2exe z http://www.py2exe.org/
- Dla payloadu (w tym przypadku nazwaliśmy go
hello.py) użyj skryptu podobnego do tego z Figure 1. Opcja bundle_files z wartością 1 spakuje wszystko, łącznie z interpreterem Python, do jednego pliku exe.
- Gdy skrypt będzie gotowy, wydaj polecenie
python setup.py py2exe. To utworzy wykonywalny plik, podobnie jak na Figure 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
Aby utworzyć payload przy użyciu PyInstaller:
- Zainstaluj PyInstaller używając pip (pip install pyinstaller).
- Następnie wydamy polecenie “pyinstaller –onefile hello.py” (przypomnienie, że ‘hello.py’ to nasz payload). To spakuje wszystko w jeden plik wykonywalny.
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.
Źródła
- https://blog.f-secure.com/how-to-decompile-any-python-binary/
- VVS Discord Stealer Using Pyarmor for Obfuscation and Detection Evasion
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.