Декомпіляція скомпільованих python бінарників (exe, elf) - Отримання з .pyc

Reading time: 7 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks

Від скомпільованого бінарника до .pyc

З ELF скомпільованого бінарника ви можете отримати .pyc за допомогою:

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

У зкомпільованому python exe бінарному файлі ви можете отримати .pyc, запустивши:

bash
python pyinstxtractor.py executable.exe

Від .pyc до коду python

Для даних .pyc ("скомпільований" python) ви повинні почати намагатися витягти оригінальний код python:

bash
uncompyle6 binary.pyc  > decompiled.py

Переконайтеся, що бінарний файл має розширення ".pyc" (якщо ні, uncompyle6 не буде працювати)

Під час виконання uncompyle6 ви можете зіткнутися з наступними помилками:

Помилка: Невідомий магічний номер 227

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

Щоб виправити це, вам потрібно додати правильний магічний номер на початку згенерованого файлу.

Магічні номери відрізняються в залежності від версії python, щоб отримати магічний номер python 3.8, вам потрібно відкрити термінал python 3.8 і виконати:

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

Число magic number в цьому випадку для python3.8 - це 0x550d0d0a, тоді, щоб виправити цю помилку, вам потрібно додати на початку .pyc файлу наступні байти: 0x0d550a0d000000000000000000000000

Якщо ви додали цей магічний заголовок, помилка повинна бути виправлена.

Ось як правильно доданий .pyc python3.8 magic header буде виглядати:

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

Помилка: Декомпіляція загальних помилок

Інші помилки такі як: class 'AssertionError'>; co_code повинен бути одним з типів (<class 'str'>, <class 'bytes'>, <class 'list'>, <class 'tuple'>); є тип <class 'NoneType'> можуть з'явитися.

Це, ймовірно, означає, що ви не додали правильно магічний номер або що ви не використали правильний магічний номер, тому переконайтеся, що ви використовуєте правильний (або спробуйте новий).

Перевірте документацію попередніх помилок.

Автоматичний інструмент

python-exe-unpacker tool служить комбінацією кількох доступних у спільноті інструментів, призначених для допомоги дослідникам у розпаковуванні та декомпіляції виконуваних файлів, написаних на Python, зокрема тих, що створені за допомогою py2exe та pyinstaller. Він включає правила YARA для ідентифікації, чи є виконуваний файл на основі Python, і підтверджує інструмент створення.

ImportError: Ім'я файлу: 'unpacked/malware_3.exe/pycache/archive.cpython-35.pyc' не існує

Звичайна проблема, з якою стикаються, пов'язана з неповним файлом байт-коду Python, що виникає внаслідок процесу розпакування з unpy2exe або pyinstxtractor, який потім не розпізнається uncompyle6 через відсутній номер версії байт-коду Python. Щоб вирішити цю проблему, було додано опцію prepend, яка додає необхідний номер версії байт-коду Python, полегшуючи процес декомпіляції.

Приклад проблеми:

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.

Аналіз python assembly

Якщо вам не вдалося витягти "оригінальний" код python, дотримуючись попередніх кроків, ви можете спробувати витягти assembly (але це не дуже описово, тому спробуйте знову витягти оригінальний код). У цьому я знайшов дуже простий код для дизасемблювання двійкового файлу .pyc (бажаю удачі в розумінні потоку коду). Якщо .pyc з python2, використовуйте 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 в Executable

Для початку ми покажемо вам, як вантажі можуть бути скомпільовані в py2exe та PyInstaller.

Щоб створити вантаж за допомогою py2exe:

  1. Встановіть пакет py2exe з http://www.py2exe.org/
  2. Для вантажу (в цьому випадку ми назвемо його hello.py) використовуйте скрипт, подібний до того, що на Рисунку 1. Опція “bundle_files” зі значенням 1 об'єднає все, включаючи інтерпретатор Python, в один exe.
  3. Коли скрипт буде готовий, ми виконаємо команду “python setup.py py2exe”. Це створить виконуваний файл, так само як на Рисунку 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

Щоб створити payload за допомогою PyInstaller:

  1. Встановіть PyInstaller за допомогою pip (pip install pyinstaller).
  2. Після цього ми виконаємо команду “pyinstaller –onefile hello.py” (нагадування, що ‘hello.py’ - це наш payload). Це об'єднає все в один виконуваний файл.
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.

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Підтримайте HackTricks