
dyen は、macOS 標準の Python を使用して Mach-O dylib をメモリ内にロードし、その dylib に対して dlopen を使わずにエクスポートされたエントリポイントを呼び出します。目的は、イメージがターゲット上で単独のファイルになることがなく、dyld に登録されることもないようにすることです。
作者: cenobyte [email protected] 2026
https://github.com/cenobyte-vincit/dyen
dyen は、macOS の標準 /usr/bin/python3 向けの、標準ライブラリのみを使用するインメモリ Mach-O ローダー (loader.py) です。プレーンな dylib、スタンドアロンの DYEN v1 ブロブ(暗号化エンベロープ)、または EOF にそのブロブが追記されたカバーファイルを読み取り、ホストスライスを匿名 mmap でマップし、クラシックな rebase/bind フィックスアップを適用し、ctypes の関数ポインタ経由で dyld_main を呼び出します。
encrypt_dyld.py はステージングツールです。dylib を DYEN ブロブとして stdout に出力するか、ブロブを追記したカバーファイルを dyld_<basename> にコピーします。バンドルされている dyld/dyld.c ペイロードは hello world を出力するだけです。loader.py は macOS 専用で、暗号化ツールは Linux または macOS で動作します。
ツリー内のローダーは、ローカルパスまたは標準入力を読み取ります。ドロッパーやステージ1は、ネットワーク経由でカバーファイル(または生のバイト列)を取得し、read_dyld_bytes() に渡すことになるため、暗号化された dylib がターゲット上で単独のファイルとして置かれることはありません。
.dyen、またはカバーファイル。hello world)。診断情報は stderr に出力されます。/usr/bin/python3(標準ライブラリのみ)loader.py は macOS 専用です。PoC dylib のコンパイルには Mac 上の Apple clang が必要です。encrypt_dyld.py は、事前にビルドされた dylib に対して Linux または macOS で動作します。
clang と make を備えた macOS(dyld/dyld.c のコンパイル用)encrypt_dyld.py 用の Python 3(標準ライブラリのみ)ruff、pylint、cppcheck(brew install ruff pylint cppcheck)make
これにより build/libdyld.dylib(ユニバーサル x86_64 + arm64)がコンパイルされ、build/libdyld.dyen が書き込まれ、同じ暗号化 dylib が pdf/dyld_c4611_sample_explain.pdf に埋め込まれます。生成された PDF はコミットされません。make が書き込み、make clean が削除します。ソースのカバーファイルは pdf/c4611_sample_explain.pdf です。
プレーン dylib は中間生成物です。実行時、ローダーはマッピング前に DYEN ブロブを復号します。スタンドアロンの .dyen ファイルからか、末尾に暗号化された dyld が追記されたカバーファイルからのいずれかです。
# Standalone DYEN blob to stdout (Makefile redirects to build/libdyld.dyen)
python3 encrypt_dyld.py build/libdyld.dylib CHANGEMEASAP > build/libdyld.dyen
# Hide encrypted dyld inside a cover file -> pdf/dyld_c4611_sample_explain.pdf
python3 encrypt_dyld.py build/libdyld.dylib CHANGEMEASAP pdf/c4611_sample_explain.pdf
# Use this Mac's serial number as the encryption key instead of a password (local testing)
python3 encrypt_dyld.py build/libdyld.dylib "$(python3 loader.py --serial)" > build/libdyld.dyen
# Use the target Mac's serial number as the encryption key (fictitious serial)
python3 encrypt_dyld.py build/libdyld.dylib C07XH4V2MD6T > build/libdyld.dyen
encrypt_dyld.py は Linux または macOS で動作します。loader.py は macOS のみで動作します。
ローダーの CLI は 1 つのファイルパスを受け取るか、標準入力を読み取ります。パスワードフラグはありません。復号は、まずこの Mac のハードウェアシリアルを試し、次に DEFAULT_DYLD_KEY(CHANGEMEASAP)を試します。シリアルを読み取れない場合は、ハードコードされたキーのみが試されます。CLI は常に入力をロードして dyld_main を呼び出します。loader.py をターゲットにコピーしてください。
/usr/bin/python3 loader.py build/libdyld.dylib
/usr/bin/python3 loader.py pdf/dyld_c4611_sample_explain.pdf
/usr/bin/python3 loader.py build/libdyld.dyen
cat build/libdyld.dylib | /usr/bin/python3 loader.py
cat pdf/dyld_c4611_sample_explain.pdf | /usr/bin/python3 loader.py
cat build/libdyld.dyen | /usr/bin/python3 loader.py
ブロブが追記されていないカバーファイルは失敗します:
/usr/bin/python3 loader.py pdf/c4611_sample_explain.pdf
loader.py --serial はこの Mac のハードウェアシリアルを出力します(macOS のみ):
python3 loader.py --serial
詳細な stderr トレースには、LOADER_DEBUG に任意の値を設定します:
LOADER_DEBUG=1 /usr/bin/python3 loader.py build/libdyld.dyen
loader.py と encrypt_dyld.py:
| コード | 意味 |
|---|---|
| 0 | 成功 |
| 1 | 使用法エラー、復号失敗、シリアル欠落、または I/O エラー |
LOADER_DEBUG(任意の値)は、入力ソース、ヘッダー/マジックチェック、DYEN ブロブ検出、復号、map/bind、シンボル解決の詳細なトレースを stderr に出力します。make はデフォルトで LOADER_DEBUG=1 を encrypt_dyld.py に渡します。make LOADER_DEBUG= は暗号化ステップのトレースを抑制します。
loader.py の DEFAULT_DYLD_KEY は CHANGEMEASAP です。暗号化には、encrypt_dyld.py への明示的なパスワードまたは Mac シリアル引数が必要です。
反復回数(loader.py の PBKDF2_ITERATIONS)は、PBKDF2-HMAC-SHA256 に関する現在の OWASP Password Storage Cheat Sheet の推奨事項(600,000 回)に従います。PKCS #5 / RFC 8018 は PBKDF2 を定義していますが、反復回数はデプロイヤーに委ねられています。NIST SP 800-63B は PBKDF2 を許可していますが、特定の数値は義務付けていません。OWASP は、ハードウェアが高速化するにつれて上昇する実用的な目標値を公開しているため、この値は定期的に見直す必要があります。これは、テストキーに適用されるパスワードストレッチングのガイダンスであり、恒久的なプロトコル定数ではありません。
ビルドホスト:
make test
make lint # cppcheck (C) + ruff + pylint (Python)
make check # cppcheck only
make lint-python # ruff + pylint
make test は、リンターを実行し、PoC dylib をビルドし、それを暗号化し(スタンドアロンの .dyen と PDF 埋め込み)、/usr/bin/python3 で統合テストを実行します。各ロードテストは、stdout に hello world が含まれることを検証します。これらのテストは同じ場所に置かれた Mac 上で実行されます。クリーンランタイムの証明ではありません。
CrowdStrike Falcon macOS はこの手法を検出します。他の EDR は未テストです。
ローダーは、クラシックな LC_DYLD_INFO rebase/bind のみを実装しています。chained fixups、Objective-C、Swift には対応していません。dlopen は、Apple 署名されたシステム依存関係(例: /usr/lib/libSystem.B.dylib)にのみ使用されます。ペイロードが dyld に登録されることはありません。
EOF 追記による隠蔽は、16進ダンプで確認できます。
武器化: 運用前に DYENCRYPT を変更してください。DYLD_BLOB_MAGIC(loader.py)の PoC マジックは静的シグネチャです。YARA ルール、ファイルスキャナー、単純な rfind ヒューリスティックは、これを手がかりにできます。任意の独自の値に置き換えてください(暗号化ツールとローダーの同期を維持すること。長さは固定ではありませんが、埋め込みカバーファイルには長い方が適しています)。
ツリー内の dylib は hello world を出力するだけです。