反编译已编译的python二进制文件(exe, elf) - 从.pyc中提取
Reading time: 9 minutes
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
从已编译的二进制文件到.pyc
从一个ELF已编译的二进制文件中,你可以获取.pyc:
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:
python pyinstxtractor.py executable.exe
从 .pyc 到 python 代码
对于 .pyc 数据(“编译的” python),您应该开始尝试 提取 原始 python 代码:
uncompyle6 binary.pyc > decompiled.py
确保二进制文件具有扩展名“.pyc”(如果没有,uncompyle6 将无法工作)
在执行uncompyle6时,您可能会遇到以下错误:
错误:未知的魔术数字 227
/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'
在这种情况下,python3.8 的 magic number 是 0x550d0d0a
,然后,要修复此错误,您需要在 .pyc 文件 的 开头 添加以下字节:0x0d550a0d000000000000000000000000
一旦 您 添加 了该魔术头,错误应该会被修复。
这就是正确添加的 .pyc python3.8 magic header 的样子:
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 工具 是多个社区可用工具的组合,旨在帮助研究人员解包和反编译用 Python 编写的可执行文件,特别是那些使用 py2exe 和 pyinstaller 创建的文件。它包括 YARA 规则,以识别可执行文件是否基于 Python,并确认创建工具。
ImportError: 文件名:'unpacked/malware_3.exe/pycache/archive.cpython-35.pyc' 不存在
一个常见问题是由于 使用 unpy2exe 或 pyinstxtractor 解包过程导致的不完整 Python 字节码文件,这会导致 uncompyle6 无法识别,因为缺少 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
# 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 汇编
如果您无法按照之前的步骤提取 Python "原始" 代码,那么您可以尝试 提取 汇编(但 它不是很具描述性,所以 尝试 再次提取 原始代码)。在 这里 我找到了一段非常简单的代码来 反汇编 .pyc 二进制文件(祝您理解代码流程好运)。如果 .pyc 是来自 Python2,请使用 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 转为可执行文件
首先,我们将向您展示如何在 py2exe 和 PyInstaller 中编译有效载荷。
使用 py2exe 创建有效载荷:
- 从 http://www.py2exe.org/ 安装 py2exe 包
- 对于有效载荷(在这种情况下,我们将其命名为 hello.py),使用如图 1 所示的脚本。选项“bundle_files”的值为 1,将把所有内容(包括 Python 解释器)打包成一个 exe。
- 一旦脚本准备好,我们将发出命令“python setup.py py2exe”。这将创建可执行文件,就像图 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
使用 PyInstaller 创建有效载荷:
- 使用 pip 安装 PyInstaller(pip install pyinstaller)。
- 之后,我们将发出命令“pyinstaller –onefile hello.py”(提醒一下,‘hello.py’ 是我们的有效载荷)。这将把所有内容打包成一个可执行文件。
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 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。