Chapter 20
ケーススタディ — 4 つの調査を最初から最後まで
この章のゴール.
これまでの章の手順を、現実的な 4 つの調査の中で通しで使い、 「何をどの順で判断するか」を身につけること。 各ケースで、どの章の道具を、なぜ、その順で使うのかを追う。
各ケースは「状況 → 手順 → 判断」の形で示す。手を動かすデモ(ページ下部)で、簡略版を実際に操作できる。
この章で使う既出の用語(定義は各リンク先). エントロピー(01 章 3 節)、メモリマップ(01 章 3 節)、Ghidra(02 章 6 節)、binwalk(02 章 3 節)、CRC(03 章 8 節)、オフセット(03 章 1 節)、チェックサム(03 章 8 節)、SWD(04 章 2 節)、ブートローダ(04 章 5 節)、ELF(05 章 1 節)、ベクタテーブル(07 章 1 節)、ロードアドレス(07 章 10 節)、周辺レジスタ(07 章 7 節)、文字列(07 章 6 節)、MCUboot(08 章 1 節)、バージョン(08 章 3 節)、署名(08 章 4 節)、NVM3(10 章 1 節)、オブジェクト(10 章 4 節)、ページ(10 章 4 節)、バイナリ(12 章 3 節)、パターン(13 章 10 節)、秘密鍵(13 章 2 節)、圧縮(14 章 4 節)、Unicorn(17 章 9 節)
ケース A: 更新に失敗する機器
状況. STM32 + MCUboot の機器。OTA 更新後、旧バージョンに戻ってしまう。
手順.
- SWD でフラッシュ全体をダンプ(04 章)。2 回読んで一致を確認。SHA-256 を記録(01 章)
binwalkとベクタテーブル(07 章)でメモリマップを作る。ブートローダ・slot0・slot1・設定領域に分ける- slot0 と slot1 の MCUboot ヘッダ(08 章)を読む。バージョンは slot1 のほうが新しい(更新は届いている)
- 各スロットの末尾トレーラ(08 章)を読む。slot1 の image ok がセットされていない
- slot1 の TLV の SHA-256(08 章)を、アプリ本体から計算し直して照合 → 一致(イメージは壊れていない)
判断. イメージは正しく、署名も通っている。だが「テスト起動後にアプリが自身を確認(image ok を書く)する処理」が呼ばれていない。 アプリ側の「自己確認」ロジックが動いていないため、ブートローダが「確認されなかった」と判断してロールバックしている。アプリのコードを 16 章で確認する方向へ。
ケース B: 設定が化ける
状況. EFR32(Zigbee)の機器。ときどき送信出力の設定が異常値になる。
手順.
- Simplicity Commander で NVM3 をダンプ(10 章)。
commander nvm3 parseを試す → 一部のキーで「壊れている」警告 - 手動で NVM3 のページをたどる(10 章)。消去カウンタでページの新旧を並べる
- 問題のキーの全履歴を拾う。追記型なので過去の値が残っている
- 履歴を時系列に並べると、正常値の後に途中で切れたオブジェクトがあり、その後に異常値
- 切れたオブジェクトの位置は、あるページの末尾付近
判断. 設定書き込みの最中に電源が切れ、オブジェクトが不完全なまま残った。次回の読み出しで、その不完全なオブジェクトを(実装が)誤って有効と解釈し、異常値になった。 対策は書き込みの電源断耐性(18 章の破損の考え方)。プログラム側の NVM3 の使い方を見直す。
ケース C: 秘密が漏れていないかの監査
状況. 新製品の出荷前セキュリティレビュー。
手順.
- 量産設定のフラッシュをダンプ。まず読み出し保護が有効かを確認(04 章)→ 有効だったが、レビューのため保護を外した開発機でダンプ
binwalk -Eでエントロピーマップ(13 章・14 章)。高エントロピー区間を列挙- 高エントロピー区間を圧縮/暗号/鍵に分類(14 章)。ファーム本体は暗号化なし(H≈6、コード)
- PEM / DER の証明書と鍵を検索(13 章)→ サーバの公開鍵と証明書はあるが、秘密鍵はない(正しい)
- 設定ストレージ(NVS)の全履歴を検査(11 章・13 章)→ 開発中に登録したテスト用 Wi-Fi パスワードが、削除済みマーカ付きで平文で残存
- パターンスキャン(13 章)で API キー・トークンを検索 → なし
判断. 致命的な鍵漏れはないが、(1) ファーム本体が暗号化されておらず読み出し保護だけが防御、(2) 削除したはずの資格情報が履歴に残る、の 2 点が指摘事項。 対策は PSA 編・IoT セキュリティ編(フラッシュ暗号化、セキュアエレメント、ストレージの適切な消去)。監査は 19 章でスクリプト化し、以後のビルドで自動化。
ケース D: 素性の分からないファーム
状況. 保守対象の古い機器。ソースもビルド環境も失われた .bin だけがある。
手順.
file/strings/binwalk(02 章)。ELF ではない生バイナリ。文字列に"FreeRTOS V10.4.3"、"USART"、"/dev/..."はなし(ベアメタル/RTOS)- 先頭を
xxd -e -g4(07 章)。オフセット 0 が 0x20005000(RAM)、オフセット 4 が 0x08004401(フラッシュ・奇数)→ Cortex-M イメージ確定。ロードアドレスは 0x08004000 付近(ブートローダの後ろ) - ベクタテーブルの繰り返しアドレスからデフォルトハンドラを特定、割り込み数からチップ系列を推定(07 章)
- Ghidra に ARM Cortex / ロードアドレス 0x08004000 / ベクタテーブルで読み込む(16 章)
- 文字列
"FreeRTOS V10.4.3"を XREF でたどり、スケジューラ初期化とタスク生成を特定(16 章)。周辺レジスタ 0x40013800(USART1)を触る関数から通信処理を発見 - 独自の設定チェックサム関数が見つかる。Unicorn でその関数だけ動かし(17 章)、既知の設定に対する出力を確認 → CRC-16/CCITT と判明
判断. ソースなしでも、使用 RTOS・通信周辺・設定形式が復元でき、保守に必要な範囲は把握できた。分かった構造は 19 章でパーサに残す。
まとめ — 共通する型
4 ケースに共通する流れ(01 章のワークフローの実践):
- 固定(ダンプの由来とハッシュ)
- 全体を見る(エントロピー・文字列・マジック)
- 地図(メモリマップ)
- 対象ごとに解く(各章の手順。公式ツール → 手動)
- 判断して記録(分かったこと・対策・パーサ)
そして繰り返し現れた原則:
- まず既知の形式と公式ツールを試す(01 章・02 章)
- 追記型ストレージには履歴が残る(10 章〜12 章)
- 辻褄が合わなければ、まず自分を疑う(03 章・18 章)
- 静的と動的を往復する(16 章・17 章)
- 秘密が読めたら、それは設計の問題(13 章)
- 解いたら再現できる形に残す(19 章)
デモ
4 つのケースを通しで追う
この章のポイント
- 実際の調査は「固定 → 全体 → 地図 → 対象ごとに解く → 判断して記録」の 5 段(01 章)の反復
- 更新失敗は MCUboot のヘッダ・トレーラ・image ok(08 章)で段階を特定
- 設定の化けは NVM3 の履歴と切れたオブジェクト(10 章・18 章)
- 監査は エントロピー・鍵/証明書検索・ストレージ履歴(13 章・14 章)、指摘は対策(PSA / IoT セキュリティ編)へ
- 素性不明のファームは ベクタテーブル → ロードアドレス → Ghidra → 文字列/周辺から機能(07 章・16 章・17 章)
- 共通原則: 公式ツール優先、履歴を疑う、自分を疑う、静的動的の往復、読めたら設計の問題、再現可能に残す
これで全 20 章は終わりである。シリーズの目次に戻る。