Dekompajliranje kompajliranih python binarnih fajlova (exe, elf) - Dohvatanje iz .pyc

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

Iz kompajliranog binarnog fajla u .pyc

Iz ELF kompajliranog binarnog fajla možete dobiti .pyc pomoću:

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

U kompajliranom python exe binary možete dobiti .pyc pokretanjem:

python pyinstxtractor.py executable.exe

Iz .pyc u python code

Za .pyc podatke (“kompajliran” python) trebalo bi da počnete da pokušavate da izvučete izvorni python code:

uncompyle6 binary.pyc  > decompiled.py

Uverite se da binarni fajl ima ekstenziju.pyc” (u suprotnom, uncompyle6 neće raditi)

Tokom izvršavanja uncompyle6 možete naići na sledeće greške:

Greška: Nepoznat magični broj 227

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

Da biste ovo popravili, morate dodati ispravan magični broj na početak generisanog fajla.

Magični brojevi variraju u zavisnosti od python verzije, da biste dobili magični broj za python 3.8 moraćete da otvorite python 3.8 terminal i izvršite:

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

The magijski broj u ovom slučaju za python3.8 je 0x550d0d0a, zatim, да бисте исправили ову грешку мораћете да додате на почетак .pyc file следеће бајтове: 0x0d550a0d000000000000000000000000

Када додате тај magic header, гршка би требало да буде исправљена.

Овако ће правилно додато .pyc python3.8 magic header изгледати:

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

Greška: Opšte greške pri dekompilaciji

Ostale greške kao: class 'AssertionError'>; co_code should be one of the types (<class 'str'>, <class 'bytes'>, <class 'list'>, <class 'tuple'>); is type <class 'NoneType'> se mogu pojaviti.

Ovo verovatno znači da niste ispravno dodali magic number ili da niste koristili ispravan magic number, pa se postarajte da koristite pravi (ili pokušajte sa novim).

Proverite prethodnu dokumentaciju o greškama.

Automatski alat

The python-exe-unpacker tool služi kao kombinacija nekoliko alata dostupnih u zajednici, dizajniranih da pomognu istraživačima pri raspakivanju i dekompajliranju izvršnih fajlova napisanih u Pythonu, posebno onih kreiranih sa py2exe i pyinstaller. Uključuje YARA pravila za identifikaciju da li je izvršni fajl zasnovan na Pythonu i potvrđuje alat koji ga je kreirao.

ImportError: File name: ‘unpacked/malware_3.exe/pycache/archive.cpython-35.pyc’ doesn’t exist

Čest problem je nepotpuna Python bytecode datoteka koja nastane tokom raspakivanja pomoću unpy2exe ili pyinstxtractor, a koja potom nije prepoznata od strane uncompyle6 zbog nedostajućeg broja verzije Python bytecode-a. Da bi se to rešilo, dodat je prepend option koji dodaje neophodan broj verzije Python bytecode-a i olakšava proces dekompilacije.

Primer problema:

# 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

Ako niste uspeli da izdvojite originalni python kod prateći prethodne korake, možete pokušati da izvučete assembly (ali on nije naročito opisivan, zato pokušajte ponovo da izdvojite originalni kod). U here našao sam vrlo jednostavan kod za disassemble .pyc binary (srećno sa razumevanjem toka koda). Ako je .pyc iz python2, koristite 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('>> timestamp = struct.unpack('>> code = marshal.loads(code) >>> magic, timestamp, code ((62211,), (1425911959,), 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 static unpack workflow

  • Extract embedded marshal blobs: pyi-archive_viewer sample.exe and export raw objects (e.g., a file named vvs. PyInstaller čuva golu marshal strim koji počinje sa 0xe3 (TYPE_CODE with FLAG_REF) umesto kompletnih .pyc fajlova. Prethodite ga odgovarajućim 16-byte .pyc headerom (magic za ugrađenu verziju interpretera + nula timestamp/size) da bi dekompajleri prihvatili fajl. Za Python 3.11.5 možete uzeti magic preko imp.get_magic().hex() i zapatchovati ga sa dd/printf pre marshal payload-a.
  • Decompile with version-aware tools: pycdc -c -v 3.11.5 vvs.pyc > vvs.py or PyLingual. Ako je potreban samo deo koda, možete preći AST (npr. ast.NodeVisitor) da izvučete konkretne argumente/konstante.
  • Parse the Pyarmor v9 header to recover crypto parameters: signature PY<license> at 0x00, Python major/minor at 0x09/0x0a, protection type 0x09 when BCC is enabled (0x08 otherwise), ELF start/end offsets at 0x1c/0x38, and the 12-byte AES-CTR nonce split across 0x24..0x27 and 0x2c..0x33. The same pattern repeats after the embedded ELF.
  • Account for Pyarmor-modified code objects: co_flags has bit 0x20000000 set and an extra length-prefixed field. Disable CPython deopt_code() during parsing to avoid decryption failures.
  • Identify encrypted code regions: bytecode is wrapped by LOAD_CONST __pyarmor_enter_*__LOAD_CONST __pyarmor_exit_*__. Decrypt the enclosed blob with AES-128-CTR using the runtime key (e.g., 273b1b1373cf25e054a61e2cb8a947b8). Derive the per-region nonce by XORing the payload-specific 12-byte XOR key (from the Pyarmor runtime) with the 12 bytes in the __pyarmor_exit_*__ marker. After decryption, you may also see __pyarmor_assert_*__ (encrypted strings) and __pyarmor_bcc_*__ (compiled dispatch targets).
  • Decrypt Pyarmor “mixed” strings: constants prefixed with 0x81 are AES-128-CTR encrypted (plaintext uses 0x01). Use the same key and the runtime-derived string nonce (e.g., 692e767673e95c45a1e6876d) to recover long string constants.
  • Handle BCC mode: Pyarmor --enable-bcc compiles many functions to a companion ELF and leaves Python stubs that call __pyarmor_bcc_*__. Map those constants to ELF symbols with tooling such as bcc_info.py, then decompile/analyze the ELF at the reported offsets (e.g., __pyarmor_bcc_58580__bcc_180 at offset 0x4e70).

Python to Executable

To start, we’re going to show you how payloads can be compiled in py2exe and PyInstaller.

To create a payload using py2exe:

  1. Instalirajte paket py2exe sa http://www.py2exe.org/
  2. Za payload (u ovom slučaju ćemo ga nazvati hello.py), koristite skriptu poput one na Slika 1. Opcija “bundle_files” sa vrednošću 1 pakuje sve, uključujući Python interpreter, u jedan exe.
  3. Kada je skripta spremna, pokrenućemo komandu “python setup.py py2exe”. Ovo će kreirati izvršni fajl, upravo kao na Slika 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

Da biste kreirali payload koristeći PyInstaller:

  1. Instalirajte PyInstaller koristeći pip (pip install pyinstaller).
  2. Nakon toga, izvršićemo komandu “pyinstaller –onefile hello.py” (podsetnik da je ‘hello.py’ naš payload). Ovo će spakovati sve u jedan izvršni fajl.
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.

Reference

Tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks