Binary Analysis 16 · 逆アセンブルの基礎 — 機械語から関数を読み解く

Chapter 16

逆アセンブルの基礎 — 機械語から関数を読み解く

この章のゴール.

Ghidra に生バイナリを正しく読み込ませ(アーキテクチャ・ロードアドレス)、 関数・文字列・呼び出し関係をたどって「このコードが何をするか」の当たりをつけられるようになること。 シンボルがないコードで、周辺レジスタや定数から機能を推定する手がかりを持つこと。

この章で使う既出の用語(定義は各リンク先). Ghidra(02 章 6 節)、CRC(03 章 8 節)、オフセット(03 章 1 節)、チェックサム(03 章 8 節)、マジックナンバー(03 章 6 節)、リトルエンディアン(03 章 2 節)、ELF(05 章 1 節)、ベクタテーブル(07 章 1 節)、リセットハンドラ(07 章 2 節)、リテラルプール(07 章 6 節)、ロードアドレス(07 章 10 節)、周辺レジスタ(07 章 7 節)、定数やアドレス(07 章 6 節)、MCUboot(08 章 1 節)、エントリ(11 章 2 節)、読み方(11 章 2 節)、バイナリ(12 章 3 節)

1. 逆アセンブルと逆コンパイル

Ghidra は両方を並べて見せる。まず擬似コードでざっと把握し、疑問があればアセンブリで確かめる、という使い方が基本である。

逆アセンブルと逆コンパイル機械語(バイト列)7f b5 04 46 …アセンブリpush {r0-r2,r4,lr} …(1対1に近い)C 風擬似コード推測が入るが読みやすいGhidra は両方を並べて見せる。擬似コードで把握し、疑問はアセンブリで確かめる
逆アセンブルは命令に、逆コンパイルは C 風に戻す。擬似コードで把握しアセンブリで確認

2. Ghidra に読み込ませる — 3 つの設定

生バイナリ(.bin)を開くときは、Ghidra が構造を知らないので、3 つを人間が指定する。ここを間違えると全部ずれる。

設定 1: アーキテクチャ(Language)

ELF なら Ghidra が自動判定する(05 章)。生バイナリは 07 章で調べた CPU を指定する。

設定 2: ロードアドレス(Base Address)

生バイナリのオフセット 0 が、実機のどのアドレスに焼かれるか(07 章で推定した値)。 Cortex-M アプリなら 0x08000000 や 0x08008000(MCUboot の後ろ、08 章)。 これが正しいと、コード中のポインタ(リテラルプール)が正しい場所を指し、文字列や関数への参照がつながる。 間違っていると参照が全部外れる。

設定 3: エントリポイント / ベクタテーブル

Cortex-M なら、先頭をベクタテーブルとして解釈させ、リセットハンドラ(オフセット 4、Thumb ビットを落とす)を関数として解析開始点にする。 Ghidra の「ARM Cortex」ローダや、手動で Disassemble(Thumb モード)を指定する。

生バイナリを Ghidra に読ませる 3 設定① アーキテクチャCortex-M は ARM:LE:32:Cortex。Cortex-A は v7/AArch64。7 章で調べた CPU② ロードアドレスオフセット 0 が焼かれる実アドレス(7 章)。0x08000000 や 0x08008000(MCUboot の後ろ)③ ベクタテーブル先頭を表として解釈させ、リセットハンドラ(−1 の偶数)から解析開始ロードアドレスが正しいと、コード中のポインタ(リテラルプール、7 章)が正しい場所を指し、文字列や関数への参照がつながる間違っていると参照が全部外れる。ELF なら Ghidra が自動判定(5 章)
アーキテクチャ・ロードアドレス・ベクタテーブルの 3 設定。ロードアドレスで参照がつながる

3. 自動解析が見つけるもの

読み込むと Ghidra が自動解析(Analyze)を走らせ、次を見つける。

4. どこから読むか — とっかかりの見つけ方

シンボルがないと、数百の FUN_08001234 が並ぶだけである。意味のある場所から入る。

どこから読むか — とっかかりの見つけ方文字列から特徴的な文字列("Login failed"、フォーマット文字列)を XREF でたどる。エラーメッセージは機能の目印周辺レジスタから0x40000000 台の定数 = ハードウェアレジスタ。データシートで「UART/SPI/GPIO を触る関数」と分かる既知の定数からAES の S-box、SHA の初期値 0x6a09e667、CRC 多項式、マジックナンバーを検索割り込みハンドラからベクタテーブルの各エントリ = その周辺機器の処理。SysTick=タイマ、USART=通信エントリポイントからリセットハンドラ → スタートアップ → main と順にたどるシンボルがないと FUN_08001234 が並ぶだけ。意味のある入口から入り、XREF で役割を逆にたどる
文字列・周辺レジスタ・既知の定数・割り込みハンドラから入り、XREF で役割をたどる

5. Cortex-M の命令を読む勘どころ

擬似コードで足りることも多いが、アセンブリを読むとき知っておくこと。

6. 逆コンパイラの読み方と限界

Ghidra の逆コンパイラは C 風コードを出すが、推測が入ることを忘れない。

7. objdump で足りることも

ELF があってシンボルも残っているなら(05 章)、Ghidra を開くより速い。

arm-none-eabi-objdump -d -j .text firmware.elf | less
arm-none-eabi-objdump -d firmware.elf | grep -A30 "<uart_send>:"   # 関数名で絞る

「Ghidra は生バイナリや大規模解析のため、objdump はシンボル付き ELF の一関数を素早く見るため」と使い分ける。

8. 手を動かす

逆アセンブルのとっかかりを選ぶ


この章のポイント