Chapter 01
バイナリ解析とは何か — 目的・作法・ワークフロー
この章のゴール.
「フラッシュの中身を読む」作業が、何を知りたくて、どの順で、何に気をつけて行うものかを説明できるようになること。 ダンプを手に入れた直後にやるべき 5 つの手順を覚えること。
1. バイナリ解析が必要になる場面
バイナリ解析とは、ソースコードや仕様書に頼らず、メモリやファイルの中身(バイト列)そのものを読んで意味を復元する作業である。 組み込み開発では、次の場面で必ず必要になる。
| 場面 | 知りたいこと | 対象 |
|---|---|---|
| 現場で壊れた機器の調査 | 設定はどう残っているか。ログに何が記録されたか。フラッシュのどこが壊れたか | NVM3・NVS などの設定領域、ログ領域 |
| 更新が失敗する | 書き込んだイメージは正しいか。ヘッダ・署名・スロットの状態は | ELF、HEX、MCUboot のヘッダとトレーラ |
| セキュリティの自己監査 | 鍵や証明書が平文で残っていないか。デバッグ用の文字列が残っていないか | フラッシュ全域 |
| 古い製品の保守 | ソースが失われた・ビルド環境が再現できないファームウェアの中身を知る | ELF、生バイナリ、逆アセンブル |
| 相互運用 | 他社機器のファイル形式・通信フォーマットを理解する | 設定ファイル、ダンプ |
| 不具合の再現 | RAM の状態(スタック、ヒープ、タスク)を止まった瞬間に読む | RAM ダンプ |
共通するのは、「バイト列には必ず構造がある」ということである。 書いた人間(またはコンパイラ・ライブラリ)は、意味のある形でバイトを並べた。 本編は、その構造を対象ごとに読み解く手順と、対象を問わず使える読み方の基礎を扱う。
2. 法と倫理 — 先に決めておくこと
バイナリ解析の技術は、自分の製品を調べるときも、他人の製品を調べるときも同じである。 だから始める前に立場を確認する。
- 自分(自社)の製品: 制約はない。ただし、ダンプには顧客の設定・鍵・個人情報が含まれることがある。取り扱いは秘密情報として管理する
- 他社の製品: 多くの国で、相互運用性の確保やセキュリティ研究を目的とした解析は認められている。一方で、コピー防止機構の回避、ライセンス条件の違反、解析結果の不正な利用は違法または契約違反になる。製品の利用規約と、その国の法律(日本では不正競争防止法・著作権法)を確認する
- 脆弱性を見つけたら: 公開の前に開発元へ報告する(責任ある開示)。IPA や JPCERT/CC の窓口が使える
本編の例はすべて自分の製品を調べるという前提で書く。
3. ワークフロー — ダンプを手にしてからの 5 手順
どんな対象でも、最初の 5 手順は同じである。
手順 1: 証拠を固定する
- ダンプファイルのハッシュ(SHA-256)を記録し、原本は読み取り専用にする。以後の作業はコピーで行う
- ダンプの由来を記録する: どの機器(シリアル番号)、どのアドレス範囲、どの方法(SWD・SPI 直読み・ブートローダ)、いつ、誰が
- 空領域の値(0xFF か 0x00 か)と、読み出しに失敗した範囲(読み出し保護で 0 埋めされた区間など)を記録する
sha256sum dump.bin > dump.bin.sha256
chmod 444 dump.bin
cp dump.bin work.binこれを怠ると、後で「解析中にファイルを壊したのか、最初から壊れていたのか」が分からなくなる。
手順 2: 全体を眺める
- ファイルサイズを確認する。フラッシュのサイズ(512 KB、1 MB、4 MB…)と一致するか。一致しなければ部分ダンプである
- エントロピー(03 章)を区間ごとに計算して、コード・データ・空き・圧縮・暗号化の領域を粗く分ける
stringsで読める文字列を拾い、製品名・バージョン・ライブラリ名・URL・デバッグメッセージを見つける- 既知のマジックナンバー(03 章)を検索する。ELF、MCUboot、LittleFS、gzip などの目印
ls -l work.bin
binwalk -E work.bin # エントロピーのグラフ
strings -n 8 work.bin | head -100
binwalk work.bin # マジックナンバーの検索手順 3: 地図を描く
アドレス範囲ごとに「何が入っているか」の表を作る。これをメモリマップと呼ぶ。 最初は粗くてよい。作業が進むにつれて埋めていく。
| オフセット | 大きさ | 内容(推定) | 根拠 |
|---|---|---|---|
| 0x00000 | 32 KB | ブートローダ | ベクタテーブルあり、"MCUBOOT" 文字列 |
| 0x08000 | 448 KB | アプリケーション | MCUboot ヘッダ magic、ベクタテーブル |
| 0x78000 | 32 KB | 設定領域 | 4 KB ごとに繰り返す構造、消去カウンタ |
| 0x7E000 | 8 KB | 空き | 0xFF |
チップのリファレンスマニュアルのメモリマップと、リンカスクリプト(あれば)が最良の手がかりである。
手順 4: 対象ごとに構造を解く
地図の各領域について、本編の該当する章の手順で構造を復元する。 「まず既知の形式を疑う」——自分で全部解く前に、そのライブラリの公式ツール(commander nvm3 parse、nvs_partition_gen、imgtool、littlefs-python)があるか確かめる。
手順 5: 記録し、再現可能にする
- 発見した構造は表と図にする(オフセット、大きさ、型、意味)
- 解釈に使ったスクリプトは保存し、同じダンプから同じ結果が出ることを確認する(19 章)
- 「分からなかったこと」も書く。次に見る人(未来の自分)のために
4. 前提となる心構え
- 仮説と検証: 「この 4 バイトは長さだろう」という仮説を立て、他の場所でも成り立つか確かめる。1 か所で成り立っても構造とは限らない
- 書いた側の都合を想像する: マイコンのファームウェアは「4 バイト境界に揃える」「消去済みの 0xFF を『未使用』として使う」「CRC を末尾に置く」といった癖がある。ライブラリのソースコードが公開されていれば、それが最強の仕様書である
- 壊れているかもしれない: ダンプは電源断・書き込み中断・フラッシュの劣化で壊れていることがある。「理屈に合わない 1 か所」は、自分の解釈違いか、実際の破損かを区別する(18 章)
- 時間を区切る: 完全な復元が目的ではない。「知りたいこと」に答えられたら止める
5. 本編の構成
| 部 | 章 | 対象 |
|---|---|---|
| I. 基礎と道具 | 01〜04 | 心構え、道具、バイト列の読み方、ダンプの取り方 |
| II. ファームウェアの入れ物 | 05〜09 | ELF、HEX / SREC、Cortex-M イメージ、MCUboot、ファイルシステム |
| III. データ領域 | 10〜14 | NVM3、NVS 系、ログ、秘密情報、圧縮と暗号化の見分け |
| IV. 実行ファイルと動的解析 | 15〜17 | Linux イメージ、逆アセンブル、動的解析 |
| V. 応用 | 18〜20 | 差分と復元、自動化、ケーススタディ |
6. 手を動かす
解析の手順を追う
この章のポイント
- バイナリ解析は「バイト列の構造を復元する」作業。必ず構造はある
- 始める前に立場と法を確認し、ダンプは秘密情報として扱う
- 最初の 5 手順: 固定 → 眺める → 地図 → 対象ごとに解く → 記録
- 既知の形式にはまず公式ツールを試す。ライブラリのソースが最強の仕様書