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

Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks:

Dica de recompensa por bugs: inscreva-se no Intigriti, uma plataforma premium de recompensa por bugs criada por hackers, para hackers! Junte-se a nós em https://go.intigriti.com/hacktricks hoje e comece a ganhar recompensas de até $100.000!

Do Binário Compilado para .pyc

A partir de um binário compilado ELF, você pode obter o .pyc com:

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

Em um binário exe python compilado, você pode obter o .pyc executando:

python pyinstxtractor.py executable.exe

De .pyc para código Python

Para os dados .pyc (python "compilado") você deve começar tentando extrair o código Python original:

uncompyle6 binary.pyc  > decompiled.py

Certifique-se de que o binário tenha a extensão ".pyc" (caso contrário, o uncompyle6 não funcionará)

Ao executar o uncompyle6, você pode encontrar os seguintes erros:

Erro: Número mágico desconhecido 227

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

Para corrigir isso, você precisa adicionar o número mágico correto no início do arquivo gerado.

Os números mágicos variam com a versão do Python, para obter o número mágico do Python 3.8 você precisará abrir um terminal do Python 3.8 e executar:

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

O número mágico neste caso para python3.8 é 0x550d0d0a, então, para corrigir esse erro, você precisará adicionar no início do arquivo .pyc os seguintes bytes: 0x0d550a0d000000000000000000000000

Depois de ter adicionado esse cabeçalho mágico, o erro deve ser corrigido.

Assim é como um cabeçalho mágico .pyc python3.8 corretamente adicionado irá parecer:

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

Erro: Decompilando erros genéricos

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

Isso provavelmente significa que você não adicionou corretamente o número mágico ou que você não usou o número mágico correto, então certifique-se de usar o correto (ou tente um novo).

Verifique a documentação do erro anterior.

Ferramenta Automática

A ferramenta python-exe-unpacker serve como uma combinação de várias ferramentas disponíveis na comunidade, projetadas para ajudar pesquisadores a desempacotar e decompilar executáveis escritos em Python, especificamente aqueles criados com py2exe e pyinstaller. Inclui regras YARA para identificar se um executável é baseado em Python e confirma a ferramenta de criação.

ImportError: Nome do arquivo: 'unpacked/malware_3.exe/pycache/archive.cpython-35.pyc' não existe

Um problema comum encontrado envolve um arquivo de bytecode Python incompleto resultante do processo de desempacotamento com unpy2exe ou pyinstxtractor, que então não é reconhecido pelo uncompyle6 devido à ausência do número de versão do bytecode Python. Para resolver isso, uma opção de prefixo foi adicionada, que anexa o número de versão do bytecode Python necessário, facilitando o processo de decompilação.

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

Analisando o assembly do Python

Se você não conseguiu extrair o código "original" do Python seguindo os passos anteriores, então você pode tentar extrair o assembly (mas não é muito descritivo, então tente extrair novamente o código original). Aqui eu encontrei um código muito simples para desmontar o binário .pyc (boa sorte para entender o fluxo do código). Se o .pyc for do Python 2, use o 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 para Executável

Para começar, vamos mostrar como os payloads podem ser compilados no py2exe e PyInstaller.

Para criar um payload usando py2exe:

  1. Instale o pacote py2exe em http://www.py2exe.org/

  2. Para o payload (neste caso, vamos chamá-lo de hello.py), use um script como o da Figura 1. A opção "bundle_files" com o valor 1 irá agrupar tudo, incluindo o interpretador Python, em um único executável.

  3. Uma vez que o script estiver pronto, emitiremos o comando "python setup.py py2exe". Isso criará o executável, assim como na 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 criar um payload usando o PyInstaller:

  1. Instale o PyInstaller usando o pip (pip install pyinstaller).

  2. Em seguida, emitiremos o comando “pyinstaller –onefile hello.py” (lembrando que ‘hello.py’ é nosso payload). Isso irá agrupar tudo em um executável.

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.

Referências

Dica de recompensa por bugs: cadastre-se no Intigriti, uma plataforma premium de recompensas por bugs criada por hackers, para hackers! Junte-se a nós em https://go.intigriti.com/hacktricks hoje e comece a ganhar recompensas de até $100,000!

Aprenda hacking na AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks:

Last updated