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

Apoya a HackTricks

Consejo de bug bounty: regístrate en Intigriti, una plataforma de bug bounty premium creada por hackers, para hackers! Únete a nosotros en https://go.intigriti.com/hacktricks hoy, y comienza a ganar recompensas de hasta $100,000!

De binario compilado a .pyc

De un binario compilado ELF puedes obtener el .pyc con:

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

En un python exe binary compilado puedes obtener el .pyc ejecutando:

python pyinstxtractor.py executable.exe

From .pyc to python code

Para los datos .pyc ("compilado" python) deberías comenzar intentando extraer el código python original:

uncompyle6 binary.pyc  > decompiled.py

Asegúrate de que el binario tenga la extensión ".pyc" (si no, uncompyle6 no va a funcionar)

Mientras ejecutas uncompyle6 podrías encontrar los siguientes errores:

Error: Número mágico desconocido 227

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

Para solucionar esto, necesitas agregar el número mágico correcto al principio del archivo generado.

Los números mágicos varían según la versión de python, para obtener el número mágico de python 3.8 necesitarás abrir un terminal de python 3.8 y ejecutar:

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

El número mágico en este caso para python3.8 es 0x550d0d0a, entonces, para corregir este error necesitarás agregar al principio del .pyc file los siguientes bytes: 0x0d550a0d000000000000000000000000

Una vez que hayas agregado ese encabezado mágico, el error debería estar corregido.

Así es como se verá un .pyc python3.8 magic header correctamente agregado:

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

Error: Decompilación de errores genéricos

Otros errores como: class 'AssertionError'>; co_code debería ser uno de los tipos (<class 'str'>, <class 'bytes'>, <class 'list'>, <class 'tuple'>); es tipo <class 'NoneType'> pueden aparecer.

Esto probablemente significa que no has añadido correctamente el número mágico o que no has utilizado el número mágico correcto, así que asegúrate de usar el correcto (o prueba uno nuevo).

Consulta la documentación de errores anterior.

Herramienta Automática

La herramienta python-exe-unpacker sirve como una combinación de varias herramientas disponibles en la comunidad diseñadas para ayudar a los investigadores a desempaquetar y decompilar ejecutables escritos en Python, específicamente aquellos creados con py2exe y pyinstaller. Incluye reglas YARA para identificar si un ejecutable está basado en Python y confirma la herramienta de creación.

ImportError: Nombre de archivo: 'unpacked/malware_3.exe/pycache/archive.cpython-35.pyc' no existe

Un problema común encontrado involucra un archivo de bytecode de Python incompleto resultante del proceso de desempaquetado con unpy2exe o pyinstxtractor, que luego no es reconocido por uncompyle6 debido a un número de versión de bytecode de Python faltante. Para abordar esto, se ha añadido una opción de prepend, que agrega el número de versión de bytecode de Python necesario, facilitando el proceso de decompilación.

Ejemplo del 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.

Análisis de ensamblaje de python

Si no pudiste extraer el código "original" de python siguiendo los pasos anteriores, entonces puedes intentar extraer el ensamblaje (pero no es muy descriptivo, así que intenta extraer nuevamente el código original). En aquí encontré un código muy simple para desensamblar el binario .pyc (buena suerte entendiendo el flujo del código). Si el .pyc es de python2, usa 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 a Ejecutable

Para comenzar, vamos a mostrarte cómo se pueden compilar los payloads en py2exe y PyInstaller.

Para crear un payload usando py2exe:

  1. Instala el paquete py2exe desde http://www.py2exe.org/

  2. Para el payload (en este caso, lo llamaremos hello.py), utiliza un script como el de la Figura 1. La opción “bundle_files” con el valor de 1 agrupará todo, incluyendo el intérprete de Python, en un solo exe.

  3. Una vez que el script esté listo, emitiremos el comando “python setup.py py2exe”. Esto creará el ejecutable, tal como en la Figura 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

Para crear un payload usando PyInstaller:

  1. Instala PyInstaller usando pip (pip install pyinstaller).

  2. Después de eso, emitiremos el comando “pyinstaller –onefile hello.py” (un recordatorio de que ‘hello.py’ es nuestro payload). Esto empaquetará todo en un solo ejecutable.

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.

Referencias

Consejo de recompensas por errores: regístrate en Intigriti, una plataforma de recompensas por errores premium creada por hackers, para hackers. ¡Únete a nosotros en https://go.intigriti.com/hacktricks hoy, y comienza a ganar recompensas de hasta $100,000!

Apoya a HackTricks

Last updated