Decompile compiled python binaries (exe, elf) - .pyc’den geri alma

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

Derlenmiş İkili Dosyadan .pyc’ye

Bir ELF derlenmiş ikili dosyasından .pyc’yi şu şekilde elde edebilirsiniz:

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

Derlenmiş bir python exe binary içinde .pyc’yi elde etmek için şu komutu çalıştırabilirsiniz:

python pyinstxtractor.py executable.exe

.pyc’den python koduna

.pyc verileri (“compiled” python) için orijinal python kodunu çıkarmayı denemeye başlamalısınız:

uncompyle6 binary.pyc  > decompiled.py

Emin olun ikili dosyanın uzantısı.pyc” (değilse, uncompyle6 çalışmayacak)

uncompyle6 çalıştırırken aşağıdaki hatalar ile karşılaşabilirsiniz:

Error: Unknown magic number 227

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

Bunu düzeltmek için üretilen dosyanın başına doğru magic number’ı eklemeniz gerekir.

Magic numbers python sürümüne göre değişir, python 3.8 için magic number’ı almak üzere python 3.8 terminali açıp şu komutu çalıştırmanız gerekir:

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

Bu durumda python3.8 için magic number 0x550d0d0a, bu hatayı düzeltmek için .pyc file’ın başına aşağıdaki bytes’ları eklemeniz gerekir: 0x0d550a0d000000000000000000000000

Bir kez o magic header’ı ekledikten sonra, hata düzelmiş olmalıdır.

Doğru şekilde eklenmiş .pyc python3.8 magic header şu şekilde görünecektir:

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

Hata: Decompiling generic errors

Diğer hatalar şu şekilde görünebilir: class 'AssertionError'>; co_code should be one of the types (<class 'str'>, <class 'bytes'>, <class 'list'>, <class 'tuple'>); is type <class 'NoneType'>

Bu muhtemelen magic number’ı doğru şekilde eklemediğiniz veya doğru magic number’ı kullanmadığınız anlamına gelir; bu yüzden doğru olanı kullandığınızdan emin olun (veya yeni bir tane deneyin).

Önceki hata dokümantasyonunu kontrol edin.

Automatic Tool

The python-exe-unpacker tool bir dizi toplulukta bulunan aracın birleşimi olarak hizmet eder; araştırmacıların Python ile yazılmış, özellikle py2exe ve pyinstaller ile oluşturulmuş yürütülebilir dosyaları unpacking ve decompiling süreçlerinde analiz etmelerine yardımcı olmak üzere tasarlanmıştır. Bir yürütülebilir dosyanın Python tabanlı olup olmadığını tespit etmek ve oluşturma aracını doğrulamak için YARA kuralları içerir.

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

Karşılaşılan yaygın bir sorun, unpy2exe veya pyinstxtractor ile yapılan unpacking sürecinden kaynaklanan eksik bir Python bytecode dosyasıdır, ve bu dosya Python bytecode sürüm numarasının eksik olması nedeniyle uncompyle6 tarafından tanınmaz. Bunu çözmek için gerekli Python bytecode sürüm numarasını ekleyen bir prepend seçeneği eklendi; bu, decompiling sürecini kolaylaştırır.

Example of the issue:

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

python assembly’ini analiz etme

Önceki adımları takip ederek python “orijinal” kodunu çıkaramadıysanız, assembly’yi çıkarmayı deneyebilirsiniz (ama çok açıklayıcı değil, bu yüzden orijinal kodu tekrar çıkarmayı deneyin). In here çok basit bir kod buldum .pyc ikilisini disassemble etmek için (kod akışını anlamak için iyi şanslar). Eğer .pyc python2’den ise, python2 kullanın:

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 gömülü olarak tam .pyc dosyaları yerine 0xe3 (TYPE_CODE with FLAG_REF) ile başlayan ham marshal akışlarını saklar. Decompiler’ların kabul etmesi için marshal yükünden önce doğru 16-byte .pyc header’ını (gömülü interpreter sürümünün magic değeri + sıfırlanmış timestamp/size) ekleyin. Python 3.11.5 için magic değerini imp.get_magic().hex() ile alıp marshal yükünden önce dd/printf ile patchleyebilirsiniz.
  • Decompile with version-aware tools: pycdc -c -v 3.11.5 vvs.pyc > vvs.py or PyLingual. Sadece kısmi koda ihtiyaç varsa, belirli argüman/konstantları çekmek için AST üzerinde gezinmek (ör. ast.NodeVisitor) yeterli olabilir.
  • 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. Aynı desen gömülü ELF’ten sonra tekrar eder.
  • Account for Pyarmor-modified code objects: co_flags içinde 0x20000000 biti set edilmiş ve ekstra uzunluk-ön eki olan bir alan bulunur. Şifre çözümlerinin başarısız olmaması için parsing sırasında CPython deopt_code()’u devre dışı bırakın.
  • Identify encrypted code regions: bytecode LOAD_CONST __pyarmor_enter_*__LOAD_CONST __pyarmor_exit_*__ ile sarılmıştır. İçteki blob’u runtime anahtarı ile AES-128-CTR kullanarak decrypt edin (örn. 273b1b1373cf25e054a61e2cb8a947b8). Bölge başına nonce’u, payload-spesifik 12-byte XOR anahtarını (Pyarmor runtime’tan) __pyarmor_exit_*__ marker’ındaki 12 byte ile XORlayarak türetin. Decryption sonrası __pyarmor_assert_*__ (şifrelenmiş stringler) ve __pyarmor_bcc_*__ (derlenmiş dispatch hedefleri) görebilirsiniz.
  • Decrypt Pyarmor “mixed” strings: 0x81 ile başlayan konstantlar AES-128-CTR ile şifrelenmiştir (düz metin 0x01 kullanır). Aynı anahtarı ve runtime’dan türetilen string nonce’unu (örn. 692e767673e95c45a1e6876d) kullanarak uzun string konstantları kurtarın.
  • Handle BCC mode: Pyarmor --enable-bcc ile birçok fonksiyonu companion ELF’e derler ve Python stub’ları __pyarmor_bcc_*__ çağrılarını bırakır. Bu konstantları bcc_info.py gibi araçlarla ELF sembollerine eşleyin, sonra bildirilen offset’lerde ELF’i decompile/analiz edin (örn. __pyarmor_bcc_58580__bcc_180 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. Install the py2exe package from http://www.py2exe.org/
  2. For the payload (in this case, we will name it hello.py), use a script like the one in Figure 1. The option “bundle_files” with the value of 1 will bundle everything including the Python interpreter into one exe.
  3. Once the script is ready, we will issue the command “python setup.py py2exe”. This will create the executable, just like in 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

PyInstaller kullanarak bir payload oluşturmak için:

  1. pip kullanarak PyInstaller’ı yükleyin (pip install pyinstaller).
  2. Bunun ardından “pyinstaller –onefile hello.py” komutunu çalıştıracağız (hatırlatma: ‘hello.py’ bizim payload’ımızdır). Bu, her şeyi tek bir yürütülebilir dosyada birleştirecektir.
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.

Kaynaklar

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin