コンパイルされた python バイナリ (exe, elf) のデコンパイル - .pyc からの取得
Tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、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 binary から、以下を実行して get the .pyc できます:
python pyinstxtractor.py executable.exe
.pyc から python code へ
対象となる .pyc データ (“compiled” python) については、まず extract して original python code を取得することを試みるべきです:
uncompyle6 binary.pyc > decompiled.py
必ず バイナリが 拡張子 “.pyc” になっていることを確認してください(そうでないと、uncompyle6 は動作しません)
uncompyle6 を実行中に、以下のエラー が発生することがあります:
エラー: Unknown magic number 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 file の 先頭 に次のバイトを 追加 する必要があります: 0x0d550a0d000000000000000000000000
一度 その magic header を 追加したら、エラーは修正されるはずです。
正しく追加された .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 should be one of the types (<class 'str'>, <class 'bytes'>, <class 'list'>, <class 'tuple'>); is type <class 'NoneType'> が表示されることがあります。
これはおそらくマジックナンバーを正しく追加していないか、正しいマジックナンバーを使用していないことを意味します。正しいものを使用しているか確認してください(または新しいものを試してください)。
前のエラー関連ドキュメントを確認してください。
自動ツール
The python-exe-unpacker tool は、py2exe や pyinstaller で作成された Python 実行ファイルの unpack と decompile を支援するためにコミュニティ公開ツールを組み合わせたツールです。YARA ルールが含まれており、実行ファイルが Python ベースかどうかを識別し、作成ツールを確認します。
ImportError: File name: ‘unpacked/malware_3.exe/pycache/archive.cpython-35.pyc’ が存在しません
よくある問題は、unpy2exe または pyinstxtractor による unpacking process の結果として Python バイトコードファイルが不完全になり、そのため Python バイトコードのバージョン番号が欠けていることで uncompyle6 に認識されない、というものです。これに対処するため、必要な Python バイトコードのバージョン番号を追加する prepend オプションが導入され、デコンパイル処理が容易になります。
Example of the issue:
# 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 の「元の」コードを抽出できなかった場合は、extractしてassemblyを取得してみてください(ただしあまり記述的ではないので、元のコードを再度抽出することを試してください)。hereで、.pyc バイナリをdisassembleする非常に簡単なコードを見つけました(コードのフローを理解するのは大変です)。.pyc が python2 の場合は python2 を使用してください:
Disassemble a .pyc
```python >>> 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(' at 0x7fd54f90d5b0, file "hello.py", line 1>)
>>>
>>> # Verify if the magic number corresponds with the current python version
>>> struct.unpack('>>
>>> # Disassemble the code object
>>> dis.disassemble(code)
1 0 LOAD_CONST 0 ()
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
```
PyInstaller raw marshal & Pyarmor v9 static unpack workflow
- 埋め込まれた marshal ブロブを抽出する:
pyi-archive_viewer sample.exe を使って生のオブジェクト(例: vvs という名前のファイル)をエクスポートします。PyInstaller は完全な .pyc ファイルではなく、0xe3(TYPE_CODE with FLAG_REF)で始まる生の marshal ストリームを格納します。逆コンパイラが受け付けるように、正しい 16 バイトの .pyc ヘッダ(埋め込まれたインタプリタバージョンのマジック + タイムスタンプ/サイズをゼロにしたもの)を先頭に付けてください。Python 3.11.5 の場合、imp.get_magic().hex() でマジックを取得し、marshal ペイロードの前に dd/printf でパッチできます。
- バージョン対応のツールでデコンパイルする:
pycdc -c -v 3.11.5 vvs.pyc > vvs.py や PyLingual を使います。部分的なコードだけ必要な場合は、AST(例: ast.NodeVisitor)をトラバースして特定の引数や定数を抽出できます。
- Pyarmor v9 ヘッダを解析して暗号パラメータを回復する: シグネチャ
PY<license> が 0x00、Python の major/minor が 0x09/0x0a、保護タイプは BCC 有効時に 0x09(無効時は 0x08)、ELF の開始/終了オフセットが 0x1c/0x38、12 バイトの AES-CTR ノンスが 0x24..0x27 と 0x2c..0x33 に分割されて格納されています。埋め込まれた ELF の後にも同じパターンが繰り返されます。
- Pyarmor によって変更された code object を考慮する:
co_flags の 0x20000000 ビットがセットされ、長さプレフィックス付きの追加フィールドがあります。解析時に CPython の deopt_code() を無効にしておかないと復号に失敗することがあります。
- 暗号化されたコード領域を特定する: バイトコードは
LOAD_CONST __pyarmor_enter_*__ … LOAD_CONST __pyarmor_exit_*__ でラップされています。囲まれたブロブはランタイムキー(例: 273b1b1373cf25e054a61e2cb8a947b8)を使って AES-128-CTR で復号します。領域ごとのノンスは、payload 固有の 12 バイト XOR キー(Pyarmor ランタイムから取得)と __pyarmor_exit_*__ マーカー内の 12 バイトを XOR することで導出します。復号後に __pyarmor_assert_*__(暗号化された文字列)や __pyarmor_bcc_*__(コンパイル済みのディスパッチターゲット)が見つかることがあります。
- Pyarmor の「mixed」文字列を復号する:
0x81 で始まる定数は AES-128-CTR で暗号化されており(プレーンテキストは 0x01 を使用)、同じキーとランタイム導出の文字列ノンス(例: 692e767673e95c45a1e6876d)を使って長い文字列定数を復元します。
- BCC モードを扱う: Pyarmor の
--enable-bcc は多くの関数を補助 ELF にコンパイルし、Python 側には __pyarmor_bcc_*__ を呼ぶスタブを残します。それらの定数を bcc_info.py のようなツールで ELF シンボルにマッピングし、報告されたオフセットで ELF をデコンパイル/解析します(例: __pyarmor_bcc_58580__ → bcc_180 がオフセット 0x4e70)。
Python to Executable
まず、py2exe と PyInstaller でどのようにペイロードがコンパイルされるかを示します。
To create a payload using py2exe:
- py2exe パッケージを http://www.py2exe.org/ からインストールします。
- ペイロード(この例では hello.py と命名します)には、Figure 1 のようなスクリプトを使用します。オプションの “bundle_files” に 1 を指定すると、Python インタプリタを含めてすべてが 1 つの exe にバンドルされます。
- スクリプトが準備できたら、コマンド “python setup.py py2exe” を実行します。これにより、Figure 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 を使用して payload を作成するには:
- pip を使用して PyInstaller をインストールします(pip install pyinstaller)。
- その後、コマンド “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.
参考文献
- https://blog.f-secure.com/how-to-decompile-any-python-binary/
- VVS Discord Stealer Using Pyarmor for Obfuscation and Detection Evasion
Tip
AWSハッキングを学び、実践する:
HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:
HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。