Décompiler des binaires python compilés (exe, elf) - Récupérer depuis .pyc

Reading time: 7 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks

D'un binaire compilé à .pyc

À partir d'un binaire compilé ELF, vous pouvez obtenir le .pyc avec :

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

Dans un binaire python exe compilé, vous pouvez obtenir le .pyc en exécutant :

bash
python pyinstxtractor.py executable.exe

De .pyc à du code python

Pour les données .pyc ("python compilé"), vous devriez commencer par essayer d'extraire le code python original :

bash
uncompyle6 binary.pyc  > decompiled.py

Assurez-vous que le binaire a l'extension ".pyc" (sinon, uncompyle6 ne fonctionnera pas)

Lors de l'exécution de uncompyle6, vous pourriez rencontrer les erreurs suivantes :

Erreur : Numéro magique inconnu 227

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

Pour corriger cela, vous devez ajouter le bon numéro magique au début du fichier généré.

Les numéros magiques varient selon la version de python, pour obtenir le numéro magique de python 3.8, vous devrez ouvrir un terminal python 3.8 et exécuter :

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

Le nombre magique dans ce cas pour python3.8 est 0x550d0d0a, puis, pour corriger cette erreur, vous devrez ajouter au début du fichier .pyc les octets suivants : 0x0d550a0d000000000000000000000000

Une fois que vous avez ajouté cet en-tête magique, l'erreur devrait être corrigée.

Voici à quoi ressemblera un en-tête magique .pyc python3.8 correctement ajouté :

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

Erreur : Décompilation des erreurs génériques

D'autres erreurs comme : class 'AssertionError'>; co_code devrait être l'un des types (<class 'str'>, <class 'bytes'>, <class 'list'>, <class 'tuple'>); est de type <class 'NoneType'> peuvent apparaître.

Cela signifie probablement que vous n'avez pas correctement ajouté le numéro magique ou que vous n'avez pas utilisé le bon numéro magique, alors assurez-vous d'utiliser le bon (ou essayez un nouveau).

Vérifiez la documentation des erreurs précédentes.

Outil Automatique

L'outil python-exe-unpacker sert de combinaison de plusieurs outils disponibles dans la communauté conçus pour aider les chercheurs à décompresser et décompiler des exécutables écrits en Python, spécifiquement ceux créés avec py2exe et pyinstaller. Il inclut des règles YARA pour identifier si un exécutable est basé sur Python et confirme l'outil de création.

ImportError : Nom de fichier : 'unpacked/malware_3.exe/pycache/archive.cpython-35.pyc' n'existe pas

Un problème courant rencontré implique un fichier de bytecode Python incomplet résultant du processus de décompression avec unpy2exe ou pyinstxtractor, qui n'est ensuite pas reconnu par uncompyle6 en raison d'un numéro de version de bytecode Python manquant. Pour y remédier, une option de préfixe a été ajoutée, qui ajoute le numéro de version de bytecode Python nécessaire, facilitant le processus de décompilation.

Exemple du problème :

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.

Analyser l'assemblage python

Si vous n'avez pas pu extraire le code "original" python en suivant les étapes précédentes, vous pouvez essayer d'extraire l'assemblage (mais ce n'est pas très descriptif, donc essayez d'extraire à nouveau le code original). J'ai trouvé ici un code très simple pour désassembler le binaire .pyc (bonne chance pour comprendre le flux du code). Si le .pyc provient de python2, utilisez 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 en Exécutable

Pour commencer, nous allons vous montrer comment les charges utiles peuvent être compilées dans py2exe et PyInstaller.

Pour créer une charge utile en utilisant py2exe :

  1. Installez le package py2exe depuis http://www.py2exe.org/
  2. Pour la charge utile (dans ce cas, nous l'appellerons hello.py), utilisez un script comme celui de la Figure 1. L'option “bundle_files” avec la valeur de 1 regroupera tout, y compris l'interpréteur Python, en un seul exe.
  3. Une fois le script prêt, nous allons exécuter la commande “python setup.py py2exe”. Cela créera l'exécutable, tout comme dans la Figure 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

Pour créer un payload en utilisant PyInstaller :

  1. Installez PyInstaller en utilisant pip (pip install pyinstaller).
  2. Ensuite, nous allons exécuter la commande “pyinstaller –onefile hello.py” (un rappel que ‘hello.py’ est notre payload). Cela regroupera tout en un seul exécutable.
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éférences

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks