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

Apprenez le piratage AWS de zéro à héros avec htARTE (HackTricks AWS Red Team Expert)!

Autres façons de soutenir HackTricks:

Astuce de prime de bug: inscrivez-vous à Intigriti, une plateforme de prime de bug premium créée par des pirates informatiques, pour les pirates informatiques**! Rejoignez-nous sur https://go.intigriti.com/hacktricks aujourd'hui, et commencez à gagner des primes allant jusqu'à 100 000 $!

Du binaire compilé à .pyc

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

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 exe Python compilé, vous pouvez obtenir le .pyc en exécutant :

python pyinstxtractor.py executable.exe

De .pyc au code Python

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

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

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

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

Les numéros magiques varient en fonction de 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é :

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 telles que : class 'AssertionError'>; co_code should be one of the types (<class 'str'>, <class 'bytes'>, <class 'list'>, <class 'tuple'>); is type <class 'NoneType'> peuvent apparaître.

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

Vérifiez la documentation de l'erreur précédente.

Outil Automatique

L'outil python-exe-unpacker sert de combinaison de plusieurs outils disponibles dans la communauté conçus pour aider les chercheurs à déballer et décompiler des exécutables écrits en Python, en particulier 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 du fichier : 'unpacked/malware_3.exe/pycache/archive.cpython-35.pyc' n'existe pas

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

Exemple du problème :

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

Analyse de l'assemblage Python

Si vous n'avez pas pu extraire le code Python "original" en suivant les étapes précédentes, vous pouvez essayer d'extraire l'assemblage Python (mais il n'est pas très descriptif, donc essayez d'extraire à nouveau le code original). Ici, j'ai trouvé un code très simple pour désassembler le binaire .pyc (bonne chance pour comprendre le flux du code). Si le .pyc est de Python2, utilisez Python2 :

>>> 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 vers Exécutable

Pour commencer, nous allons vous montrer comment les charges utiles peuvent être compilées avec 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, dans un seul exe.

  3. Une fois le script prêt, nous émettrons la commande "python setup.py py2exe". Cela créera l'exécutable, comme dans la 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

Pour créer une charge utile en utilisant PyInstaller :

  1. Installez PyInstaller en utilisant pip (pip install pyinstaller).

  2. Ensuite, nous allons exécuter la commande "pyinstaller --onefile hello.py" (n'oubliez pas que 'hello.py' est notre charge utile). Cela regroupera tout en un 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

Astuce de prime de bug: inscrivez-vous à Intigriti, une plateforme de prime de bug premium créée par des hackers, pour des hackers! Rejoignez-nous sur https://go.intigriti.com/hacktricks aujourd'hui, et commencez à gagner des primes allant jusqu'à 100 000 $!

Apprenez le piratage AWS de zéro à héros avec htARTE (Expert de l'équipe rouge AWS de HackTricks)!

Autres façons de soutenir HackTricks:

Dernière mise à jour