Decompile compiled python binaries (exe, elf) - Retreive from .pyc

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Wskazówka dotycząca bug bounty: Zarejestruj się na platformie Intigriti, premium platformie bug bounty stworzonej przez hakerów, dla hakerów! Dołącz do nas na https://go.intigriti.com/hacktricks już dziś i zacznij zarabiać nagrody aż do $100,000!

Z pliku skompilowanego do .pyc

Z pliku skompilowanego ELF można uzyskać plik .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 pliku python exe binary można uzyskać plik .pyc, uruchamiając:

python pyinstxtractor.py executable.exe

Z pliku .pyc do kodu Pythona

Dla danych w formacie .pyc ("skompilowany" kod Pythona) należy rozpocząć próbę wyodrębnienia oryginalnego kodu Pythona:

uncompyle6 binary.pyc  > decompiled.py

Upewnij się, że plik binarny ma rozszerzenie ".pyc" (jeśli nie, uncompyle6 nie będzie działać)

Podczas wykonywania uncompyle6 możesz napotkać następujące błędy:

Błąd: Nieznany numer magiczny 227

/kali/.local/bin/uncompyle6 /tmp/binary.pyc
Unknown magic number 227 in /tmp/binary.pyc

Aby to naprawić, musisz dodać poprawny numer magiczny na początku wygenerowanego pliku.

Numery magiczne różnią się w zależności od wersji Pythona, aby uzyskać numer magiczny Pythona 3.8, musisz otworzyć terminal Pythona 3.8 i wykonać:

>> import imp
>> imp.get_magic().hex()
'550d0d0a'

W tym przypadku numer magiczny dla pythona 3.8 to 0x550d0d0a, następnie, aby naprawić ten błąd, musisz dodać na początku pliku .pyc następujące bajty: 0x0d550a0d000000000000000000000000

Po dodaniu tego magicznego nagłówka, błąd powinien zostać naprawiony.

Tak będzie wyglądał poprawnie dodany nagłówek magiczny .pyc pythona 3.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

Błąd: Dekompilacja ogólnych błędów

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ć.

Oznacza to prawdopodobnie, że nie dodałeś poprawnie numeru magicznego lub nie użyłeś poprawnego numeru magicznego, więc upewnij się, że używasz właściwego (lub spróbuj nowego).

Sprawdź dokumentację dotyczącą poprzedniego błędu.

Narzędzie automatyczne

Narzędzie python-exe-unpacker służy jako kombinacja kilku narzędzi dostępnych w społeczności, przeznaczonych do pomocy badaczom w rozpakowywaniu i dekompilowaniu plików wykonywalnych napisanych w Pythonie, w szczególności tych utworzonych za pomocą py2exe i pyinstaller. Zawiera reguły YARA do identyfikacji, czy plik wykonywalny jest oparty na Pythonie, oraz potwierdza narzędzie tworzenia.

ImportError: Nazwa pliku: 'unpacked/malware_3.exe/pycache/archive.cpython-35.pyc' nie istnieje

Częstym problemem napotykanym podczas procesu rozpakowywania za pomocą unpy2exe lub pyinstxtractor jest niekompletny plik bajtowy Pythona, co skutkuje nieuznaniem go przez uncompyle6 z powodu braku numeru wersji bajtów Pythona. Aby temu zaradzić, dodano opcję prepend, która dodaje niezbędny numer wersji bajtów 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 asemblera Pythona

Jeśli nie udało ci się wydobyć "oryginalnego" kodu Pythona, postępuj zgodnie z poprzednimi krokami, a następnie spróbuj wydobyć asembler (jednak nie jest on zbyt opisowy, więc spróbuj ponownie wydobyć oryginalny kod). W tutaj znalazłem bardzo prosty kod do dysasemblacji pliku .pyc (powodzenia z zrozumieniem przepływu kodu). Jeśli plik .pyc pochodzi z Pythona 2, użyj Pythona 2:

>>> 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('<H', magic[:2])
>>> timestamp = struct.unpack('<I', timestamp)
>>> code = marshal.loads(code)
>>> magic, timestamp, code
((62211,), (1425911959,), <code object <module> at 0x7fd54f90d5b0, file "hello.py", line 1>)
>>>
>>> # Verify if the magic number corresponds with the current python version
>>> struct.unpack('<H', imp.get_magic()[:2]) == magic
True
>>>
>>> # Disassemble the code object
>>> dis.disassemble(code)
1           0 LOAD_CONST               0 (<code object hello_world at 0x7f31b7240eb0, file "hello.py", line 1>)
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

Python do pliku wykonywalnego

Aby rozpocząć, pokażemy Ci, jak ładunki mogą być skompilowane za pomocą py2exe i PyInstaller.

Aby utworzyć ładunek za pomocą py2exe:

  1. Zainstaluj pakiet py2exe z http://www.py2exe.org/

  2. Dla ładunku (w tym przypadku nazwiemy go hello.py), użyj skryptu podobnego do tego z Rysunku 1. Opcja "bundle_files" z wartością 1 spowoduje, że wszystko, włącznie z interpreterem Pythona, zostanie spakowane w jedno exe.

  3. Gdy skrypt będzie gotowy, wydamy polecenie "python setup.py py2exe". Spowoduje to utworzenie pliku wykonywalnego, tak jak na Rysunku 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ć ładunek za pomocą PyInstaller:

  1. Zainstaluj PyInstaller za pomocą pip (pip install pyinstaller).

  2. Następnie wydamy polecenie „pyinstaller --onefile hello.py” (przypomnijmy, że „hello.py” to nasz ładunek). Spowoduje to spakowanie wszystkiego w jedno wykonywalne plik.

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.

Odnośniki

Wskazówka dotycząca nagrody za błąd: Zarejestruj się na platformie do nagród za błędy Intigriti, stworzonej przez hakerów, dla hakerów! Dołącz do nas na https://go.intigriti.com/hacktricks już dziś i zacznij zarabiać nagrody do 100 000 dolarów!

Dowiedz się, jak hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Last updated