analyzing-binaries について
トリアージ、静的・動的解析、証拠に基づく調査を扱うバイナリリバースエンジニアリングのセキュリティスキルです。未知の実行ファイルやファームウェアのコンポーネント、クローズドソースの脆弱性を調べる分析者向けです。
主な機能
- 01バイナリのトリアージと形式判定
- 02逆アセンブラによる静的解析
- 03動的トレースとデバッグ
- 04ファームウェア解析とエミュレーション
- 05解析妨害技術の評価
活用シーン
- 01未知の実行ファイルの調査
- 02プロトコルやファイル形式の復元
- 03ファームウェア内のネイティブコンポーネントの調査
- 04クローズドソースバイナリの欠陥検出
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trilwu/ secskills --skill analyzing-binariesインストール後、Claude Code で/analyzing-binariesを入力して読み込まれたか確認します。
詳細
バイナリの解析
リバースエンジニアリングとは、ソースコードを読めないプログラムに対する仮説検証です。 作業時間の大半は、どれだけのコードを調べるかで決まります。そのため、この分野では対象を絞り込むことが重要です。まずトリアージを行い、注目すべき数パーセントを見つけてから、そこを慎重に読み解きます。
使用する場面
- 不明またはドキュメントのない実行ファイルの動作を理解する
- バイナリからアルゴリズム、ファイル形式、ワイヤープロトコルを復元する
- クローズドソースの対象にある脆弱性を特定する
- 組み込み機器や IoT デバイスのファームウェアを解析する
- CTF のリバースエンジニアリング課題に取り組む
使用しない場面
- 実行によって発症させる意図のある稼働中のマルウェア — 隔離と安全な実行を扱う
analyzing-malwareを使用してください。逆アセンブルには、その後で戻ってきてください。 - ヘッダーのない生のシェルコード blob(位置独立ペイロード、ステージャー、エッグハンター)—
analyzing-shellcodeを使用してください - ソースコードが入手できる場合 —
auditing-code-for-vulnerabilitiesを使用してください - Android/iOS アプリ全体の評価 —
testing-mobile-applicationsを使用してください。アプリに含まれるネイティブの.so/Mach-O コンポーネントには、このスキルを使用してください - 通常のバイナリではなく、フレームワークのランタイムの場合 — ツールチェーンが固有のため、汎用的なリバースエンジニアリングでは対応できません。
libapp.so/Dart スナップショットにはreversing-flutter-apps、libil2cpp.soとglobal-metadata.datにはreversing-unity-il2cpp、Hermes バイトコードにはreversing-react-native-appsを使用してください - 独自のシンボル復元機能を持つ言語ランタイム —
analyzing-go-binaries(pclntabはストリップ後も残ります)、analyzing-rust-binaries(パニック文字列からソースパスが漏れます)、analyzing-dotnet-assemblies(IL を C# に逆コンパイルできます)を使用してください。これらに汎用的なリバースエンジニアリングを使うと、作業の大半が無駄になります - パックまたは保護された実行ファイル — まず
unpacking-protected-binariesを使用してください。ダンプするまでは逆アセンブルするものがありません - リバースエンジニアリングの前にファームウェアイメージ全体を抽出する必要がある場合 —
binwalk、ファイルシステムのカービング、クロスアーキテクチャエミュレーションにはanalyzing-firmware-imagesを使用し、その中にある個別のバイナリの解析にはこちらに戻ってきてください
まずトリアージ — いきなり逆アセンブラーを開かない
ここで費やす1分が、デコンパイラーでの1時間を節約します。
file target && du -h target
# Architecture, endianness, PIE, stripped or not — all decide your tooling
readelf -hSd target # ELF: headers, sections, dynamic deps
rabin2 -I target # radare2's normalized summary of any format
objdump -p target # PE/ELF imports and load config
# Protections tell you what the author expected
checksec --file=target # NX, canary, RELRO, PIE, Fortify
# Strings, but read them for structure rather than skimming
strings -n 8 -t x target | less # ASCII with offsets
strings -e l -n 8 target # UTF-16LE, essential on Windows
逆アセンブルする前に、トリアージで答えを出すべきこと:
| 質問 | 手がかり |
|---|---|
| どの言語/ツールチェーンでビルドされたか? | Rust/Go のランタイム文字列、libstdc++、__gxx_personality、MSVC RTTI |
| パックされているか? | 高いエントロピー、小さなインポートテーブル、UPX や .themida という名前のセクション |
| 何と通信しているか? | ソケット/HTTP API のインポート、埋め込み URL、証明書 blob |
| 注目すべきロジックはどこか? | 暗号/ファイル/レジストリ/プロセス API のインポート |
| シンボルがストリップされているか? | nm -D の出力が空、.symtab がない |
# Entropy scan finds packed or embedded-blob regions
binwalk -E target
# Unpack the common case
upx -d target -o target.unpacked
Go と Rust のバイナリは、重要な情報が失われる形でストリップされていることは通常ありません。何よりも先にシンボルを復元してください。作業時間が数時間から数分に変わります。
# Go: recover function names and types
GoReSym -t -p target > syms.json # or the redress / IDAGolangHelper plugins
# Rust: demangle
nm -C target 2>/dev/null | head
静的解析のワークフロー
ツールを1つ選んで深く掘り下げてください。解析の途中でツールを切り替えると、注釈が失われます。
# Ghidra headless: batch import, auto-analyze, run a script
analyzeHeadless /proj MyProj -import target -postScript Decompile.java
# radare2 / rizin interactive
r2 -AA target
# aaa analyze everything
# afl list functions, sorted by size — big ones first
# axt @ sym.f cross-references TO a function (who calls this?)
# pdg @ main decompile with ghidra plugin (r2ghidra)
# iz / izz strings in data / whole binary
# /x deadbeef search for a byte pattern
# Binary Ninja / IDA headless equivalents exist; the workflow is identical
アドレス順ではなく、証拠を頼りにたどってください。 注目すべきコードを最速で見つける入口は3つあります。
- 文字列 → xref。 実行時に見かけたメッセージを探してクロスリファレンスし、そのメッセージを生成した関数にたどり着きます。
- インポート → xref。
recv、CreateProcess、fopen、EVP_EncryptInitをクロスリファレンスして、関心のある処理を行うコードを見つけます。 - エントロピー/定数。 暗号定数(AES の S-box、SHA-2 のラウンド定数、MD5 のマジックナンバー)は見分けやすく、
binwalk、findcrypt、YARA ルールで特定できます。
そこを起点に、周辺のコードを読み進めてください。各関数や変数の動作がわかったら名前を変更してください。注釈を加えたデコンパイラーのリストは、生のリストとはまったく異なる成果物です。
デコンパイラの出力から構造を見極める
デコンパイラの出力は、C言語風のノイズです。注目すべき点は次のとおりです。
- バイト配列のインデックスを使い、XOR を行うループ — 難読化、または独自実装の暗号です。キーを抽出し、オフラインでデコードします。
- 入力から読み取った小さな整数を使う switch 文 — コマンドのディスパッチです。通常、これはプロトコルであり、ほかのすべてを理解するための手掛かりになります。
- 入力から取得した長さを指定する
memcpy— メモリ安全性のレビューを始める箇所です。auditing-code-for-vulnerabilitiesを参照してください。 - 同じベースに対する
[rax + 8*n]へのアクセスの繰り返し — 構造体です。ツール内で定義すると、逆アセンブル結果が読みやすいコードになります。 - テーブルを読み込んだ直後のレジスタ経由の呼び出し — 仮想関数テーブル、またはコールバックのディスパッチです。テーブルを復元すれば、クラスも復元できます。
動的解析
静的解析で分かるのはコードが実行できること、動的解析で分かるのは実際に行うことです。信頼できないバイナリは、ホストとの共有フォルダーがなく、ネットワークを制御できる隔離 VM 内でのみ実行してください。隔離手順については analyzing-malware を参照してください。
# Syscall and API-level behaviour
strace -f -e trace=network,file,process -o trace.log ./target
ltrace -f ./target
# Windows equivalents: API Monitor, Procmon, drltrace
# Debugging
gdb -q ./target # with pwndbg/GEF: `checksec`, `vmmap`, `heap`, `telescope`
lldb ./target # macOS
x64dbg / WinDbg # Windows
# Instrumentation — the highest-leverage dynamic technique
frida-trace -f ./target -i 'recv*' -i 'EVP_*'
# then edit the generated JS handlers to dump buffers and patch return values
Frida は、アンチデバッグ、独自の暗号処理、ライセンスチェックを突破する最速の手段です。難読化を解除するのではなく、復号の後に関数をフックしてください。
エミュレーションはファームウェアや隔離されたルーチンに使用します。
qemu-arm -L /usr/arm-linux-gnueabi ./target # user-mode
# Unicorn for a single function: map memory, set registers, run, read result
# angr for symbolic execution when you need an input that reaches a state
ファームウェア
binwalk -Me firmware.bin # extract recursively
# Identify the filesystem before extracting: squashfs, jffs2, cramfs, ubifs
unsquashfs -d rootfs squashfs-root.bin
# Then treat the rootfs as a Linux system
rg -n 'password|admin|BEGIN (RSA|OPENSSH) PRIVATE KEY|api[_-]?key' -i rootfs/
find rootfs -name '*.pem' -o -name 'shadow' -o -name '*.conf'
# Web interface and startup scripts are where the bugs are
ls rootfs/etc/init.d rootfs/www rootfs/usr/sbin
ファイルシステムのないブートローダーやベアメタルイメージでは、逆アセンブルする前にロードアドレスを特定してください(ベンダーの SDK に記載されていることが多く、絶対ポインターの集まりから推定できる場合もあります)。ベースアドレスが間違っていると、リスト全体が意味をなさなくなります。
解析対策
手法を見極めたら、それを突破するか、回避するかを判断します。
| 手法 | 見分け方 | 対応 |
|---|---|---|
| パッキング | エントロピーが高く、スタブと大きなセクションが 1 つ | アンパックするか、OEP の後でメモリからダンプします |
| アンチデバッグ | IsDebuggerPresent、ptrace(PTRACE_TRACEME)、タイミングチェック | チェックをパッチするか、Frida でフックします |
| VM 検出 | CPUID チェック、MAC OUI、レジストリの痕跡 | VM を強化するか、検出処理をパッチします |
| 文字列の難読化 | 読み取り可能な文字列はないが、明らかなデコードループがある | すべての呼び出し箇所でデコーダーをエミュレートします |
| 制御フローの平坦化 | 状態変数を使った巨大な switch 文 | シンボリック解析で難読化を解除するか、動的解析で進めます |
回避するほうが、通常は手間がかかりません。静的解析でチェックの突破に手こずっている場合は、その結果を使う関数をフックしてください。
退けるべき思い込み
- 「バイナリ全体を読む。」 読み切れません。トリアージして手掛かりを絞らなければ、ライブラリコードの解析に時間を使い果たします。
- 「デコンパイラの出力が間違っているから、行き詰まりだ。」 呼び出し規約や構造体の解析では、デコンパイラの出力が誤っていることはよくあります。結論を出す前に、該当する命令を逆アセンブルで確認してください。
- 「シンボルが削除されているから、もう何も分からない。」 ライブラリ関数は復元できます(FLIRT/Sigs、コンパイル済みの参照用バイナリとの
bindiff)。Go/Rust のメタデータも通常は残っています。 - 「実行して動作を確認すればいい。」 悪意があるかどうか、どこに隔離されているかを把握するまでは実行しないでください。
- 「文字列を見れば全体像が分かる。」 文字列から分かるのは、どこを調べるべきかです。攻撃者が誤解を招く文字列を仕込んでいることもあります。
成果物
RE レポートは、読者が作業をやり直さずに行動できる内容にしてください。
- 識別情報 — ハッシュ、ファイル形式、アーキテクチャ、コンパイラ、パッカー
- 機能 — アドレスではなく、動作として何をするかを記述
- 主要なルーチン — 名前と一行の目的を付記したアドレス
- プロトコル/形式 — フィールドごとの説明。必要に応じてパーサーまたは Kaitai の仕様を添付
- 指標 — ネットワークエンドポイント、ファイルパス、ミューテックス、キー、定数
- 未解決の疑問 — 解明できなかったことと、その理由
ATT&CK のカバレッジ
secskills-core/ttp-index.json から生成されます。このファイルを編集してから、
python3 scripts/sync_attack.py --write を実行してください。レポートで引用する前に、
ID を現在の ATT&CK リリースと照合して再確認してください。
防御回避 (TA0005)
- T1027 難読化されたファイルまたは情報 —
analyzing-malware、analyzing-shellcodeも参照 - T1027.002 ソフトウェアのパッキング —
analyzing-malwareも参照 - T1140 ファイルまたは情報の難読化解除/デコード —
analyzing-malware、analyzing-shellcodeも参照 - T1497 仮想化/サンドボックスの回避 —
analyzing-malwareも参照 - T1622 デバッガーの回避 —
analyzing-malwareも参照
これらのいずれかに関する検知コンテンツ:engineering-detections。プロアクティブな調査:hunting-threats。侵害後の対応:responding-to-incidents。
参考資料
analyzing-malware— 封じ込め、デトネーション、IOC の抽出testing-mobile-applications— ネイティブコンポーネントを扱う APK/IPA のワークフロー- Ghidra、rizin/radare2、Binary Ninja、IDA — いずれかを選び、深く学びましょう
- 動的解析およびエミュレーション解析には Frida、angr、Unicorn、QEMU