Dekompilacja skompilowanych binarek python (exe, elf) - Pobierz z .pyc

Reading time: 7 minutes

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks

Z Skompilowanej Binarki do .pyc

Z ELF skompilowanej binarki możesz uzyskać .pyc za pomocą:

bash
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 uzyskać .pyc uruchamiając:

bash
python pyinstxtractor.py executable.exe

Z .pyc do kodu python

Dla danych .pyc ("skompilowany" python) powinieneś zacząć próbować wyodrębnić oryginalny kod python:

bash
uncompyle6 binary.pyc  > decompiled.py

Upewnij się, że plik binarny ma rozszerzenie ".pyc" (w przeciwnym razie, uncompyle6 nie zadziała)

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

Błąd: Nieznana liczba magiczna 227

bash
/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 dla python 3.8, musisz otworzyć terminal python 3.8 i wykonać:

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

Liczba magic w tym przypadku dla python3.8 to 0x550d0d0a, następnie, aby naprawić ten błąd, musisz dodać na początku pliku .pyc następujące bajty: 0x0d550a0d000000000000000000000000

Gdy dodasz ten nagłówek magiczny, błąd powinien być naprawiony.

Tak będzie wyglądał poprawnie dodany nagłówek magiczny .pyc python3.8:

bash
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 błędów ogólnych

Inne błędy takie jak: class 'AssertionError'>; co_code powinien być jednym z typów (<class 'str'>, <class 'bytes'>, <class 'list'>, <class 'tuple'>); jest typu <class 'NoneType'> mogą się pojawić.

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

Sprawdź dokumentację wcześniejszych błędów.

Narzędzie automatyczne

Narzędzie python-exe-unpacker służy jako połączenie kilku dostępnych w społeczności narzędzi zaprojektowanych w celu pomocy badaczom w rozpakowywaniu i dekompilacji plików wykonywalnych napisanych w Pythonie, szczególnie tych stworzonych za pomocą py2exe i pyinstaller. Zawiera zasady YARA do identyfikacji, czy plik wykonywalny jest oparty na Pythonie i potwierdza narzędzie do jego stworzenia.

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

Powszechnym problemem jest niekompletny plik bajtowy Pythona wynikający z procesu rozpakowywania za pomocą unpy2exe lub pyinstxtractor, który następnie nie jest rozpoznawany przez uncompyle6 z powodu brakującego numeru wersji bajtowego Pythona. Aby to naprawić, dodano opcję prepend, która dodaje niezbędny numer wersji bajtowego Pythona, ułatwiając proces dekompilacji.

Przykład problemu:

python
# 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
python
# 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 assemblera Pythona

Jeśli nie udało ci się wyodrębnić "oryginalnego" kodu Pythona zgodnie z poprzednimi krokami, możesz spróbować wyodrębnić assembler (ale nie jest to zbyt opisowe, więc spróbuj ponownie wyodrębnić oryginalny kod). W tutaj znalazłem bardzo prosty kod do deasemblacji binarnego pliku .pyc (powodzenia w zrozumieniu przepływu kodu). Jeśli .pyc jest z python2, użyj python2:

bash
>>> 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 na Wykonywalny

Na początek pokażemy, jak ładunki mogą być kompilowane w py2exe i PyInstaller.

Aby stworzyć ł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 na Rysunku 1. Opcja “bundle_files” z wartością 1 połączy wszystko, w tym interpreter Pythona, w jeden plik exe.
  3. Gdy skrypt będzie gotowy, wydamy polecenie “python setup.py py2exe”. To stworzy plik wykonywalny, tak jak na Rysunku 2.
python
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,
)
bash
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 stworzyć ładunek za pomocą PyInstaller:

  1. Zainstaluj PyInstaller za pomocą pip (pip install pyinstaller).
  2. Następnie wydamy polecenie “pyinstaller –onefile hello.py” (przypomnienie, że ‘hello.py’ to nasz ładunek). To połączy 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.

Odniesienia

tip

Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Wsparcie HackTricks