hacktricks/forensics/basic-forensic-methodology/specific-software-file-type.../.pyc.md

12 KiB

Descompilar binarios de Python compilados (exe, elf) - Recuperar desde .pyc

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Si estás interesado en una carrera de hacking y hackear lo imposible - ¡estamos contratando! (se requiere fluidez en polaco escrito y hablado).

{% embed url="https://www.stmcyber.com/careers" %}

De un binario compilado a .pyc

Desde 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 binario ejecutable de Python compilado, puedes obtener el archivo .pyc ejecutando:

python pyinstxtractor.py executable.exe

De .pyc a código Python

Para los datos .pyc (python "compilado"), debes comenzar intentando extraer el código Python original:

uncompyle6 binary.pyc  > decompiled.py

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

Al ejecutar uncompyle6 es posible que encuentres 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, debes 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, deberás abrir una 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, luego, para solucionar este error deberás agregar al principio del archivo .pyc los siguientes bytes: 0x0d550a0d000000000000000000000000

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

Así es como se verá un encabezado mágico .pyc de python3.8 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: Errores genéricos de descompilación

Pueden aparecer otros errores como: class 'AssertionError'>; co_code should be one of the types (<class 'str'>, <class 'bytes'>, <class 'list'>, <class 'tuple'>); is type <class 'NoneType'>.

Esto probablemente significa que no has agregado 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 con uno nuevo).

Consulta la documentación del error anterior.

Herramienta automática

La herramienta https://github.com/countercept/python-exe-unpacker combina varias herramientas disponibles para la comunidad que ayudan a los investigadores a desempaquetar y descompilar ejecutables escritos en Python (py2exe y pyinstaller).

Se dispone de varias reglas YARA para determinar si el ejecutable está escrito en Python (este script también confirma si el ejecutable fue creado con py2exe o pyinstaller).

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

Actualmente, con unpy2exe o pyinstxtractor, el archivo de bytecode de Python que obtenemos puede no estar completo y, a su vez, no puede ser reconocido por uncompyle6 para obtener el código fuente de Python en texto plano. Esto se debe a la falta de un número de versión de bytecode de Python. Por lo tanto, hemos incluido una opción de prepend; esto incluirá un número de versión de bytecode de Python y ayudará a facilitar el proceso de descompilación. Cuando intentamos usar uncompyle6 para descompilar el archivo .pyc, devuelve un error. Sin embargo, una vez que usamos la opción de prepend, podemos ver que el código fuente de Python se ha descompilado correctamente.

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

Analizando el ensamblado de Python

Si no pudiste extraer el código "original" de Python siguiendo los pasos anteriores, puedes intentar extraer el ensamblado (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 archivo binario .pyc (buena suerte entendiendo el flujo del código). Si el .pyc es de Python 2, utiliza Python 2:

>>> 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 empezar, vamos a mostrarte cómo se pueden compilar cargas útiles en py2exe y PyInstaller.

Para crear una carga útil usando py2exe:

  1. Instala el paquete py2exe desde http://www.py2exe.org/
  2. Para la carga útil (en este caso, la llamaremos hello.py), utiliza un script como el que se muestra en la Figura 1. La opción "bundle_files" con el valor de 1 agrupará todo, incluido el intérprete de Python, en un solo archivo ejecutable.
  3. Una vez que el script esté listo, ejecuta el comando "python setup.py py2exe". Esto creará el ejecutable, tal como se muestra 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 utilizando 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 agrupará todo en un 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

Si estás interesado en una carrera de hacking y hackear lo imposible - ¡estamos contratando! (se requiere fluidez en polaco escrito y hablado).

{% embed url="https://www.stmcyber.com/careers" %}

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥