
注入可能なリアルモード x86 デバッガ。BIOSリバースエンジニアリングおよびシリアルケーブルを介した任意のリアルモードコードデバッグ用。GDB統合とハードウェアブレークポイント/ウォッチポイントサポート付き。
BREAD(BIOS Reverse Engineering & Advanced Debugger)は、「注入可能な」リアルモード x86 デバッガであり、シリアルケーブルを介して別の PC から任意のリアルモードコード(実際のハードウェア上で)をデバッグできます。
BREAD は、レガシー BIOS をリバースエンジニアリングする多くの失敗した試みから生まれました。BIOS 解析の大部分(すべてではないにせよ)は逆アセンブラを使って静的に行われているため、特定のコード片におけるレジスタやメモリの値を知る方法がなく、BIOS を理解することが非常に困難です。
それにもかかわらず、BREAD はブート可能なコードや DOS プログラムなど、リアルモードの任意のコードもデバッグできます。
簡単なデモ:
https://user-images.githubusercontent.com/8294550/217709970-9007a1e3-7352-470d-a22f-cbb5219d5547.mp4
このデバッガは 2 つの部分に分かれています。デバッガ(完全にアセンブリで書かれ、デバッグ対象のハードウェア上で動作)と、C で書かれ Linux 上で動作するブリッジです。
デバッガは注入可能なコードで、16 ビットリアルモードで記述されており、BIOS ROM や他のリアルモードコード内に配置できます。実行されると、適切な割り込みハンドラを設定し、プロセッサをシングルステップモードにし、シリアルポートでコマンドを待機します。
一方、ブリッジはデバッガと GDB の間のリンクです。ブリッジは TCP 経由で GDB と通信し、リクエスト/レスポンスをシリアルポート経由でデバッガに転送します。ブリッジの背後にある考え方は、GDB パケットの複雑さを取り除き、マシンと通信するためのよりシンプルなプロトコルを確立することです。さらに、シンプルなプロトコルにより最終的なコードサイズを小さくすることができ、デバッガを様々な環境に注入しやすくなります。
次の図のように:
+---------+ simple packets +----------+ GDB packets +---------+
| |--------------->| |--------------->| |
| dbg | | bridge | | gdb |
|(real HW)|<---------------| (Linux) |<---------------| (Linux) |
+---------+ serial +----------+ TCP +---------+
GDB スタブを実装しているため、BREAD はすぐに使える多くの機能を持っています。以下のコマンドがサポートされています:
BIOS のような生のバイナリを GDB でリバースエンジニアリングすると、当然ながら元のシンボルが欠如していることを意味します。しかし、RE プロセスが進むにつれて、ユーザー/プログラマー/ハッカーはコードの特定部分についてより深く理解するようになり、IDA、Cutter、Ghidra などの静的解析ツールでは注釈、コメント、関数定義などを追加できます。これらの強化により、ユーザーの生産性が大幅に向上します。
これを考慮して、プロジェクトには symbolify.py という補助 Python スクリプトが付属しています。シンボルのリスト(アドレス ラベル)を指定すると、これらのシンボルが追加された最小限の ELF ファイルを生成します。この ELF は後で GDB にロードでき、デバッグプロセスを大幅に簡素化するために使用できます。
シンボルファイルには、空白、空行、コメント(#)、およびアドレス行のコメントを含めることができます。アドレスは 10 進数または 16 進数形式で、ラベル/シンボル(1 つ以上の空白文字で区切られる)は [a-z0-9_]+ の形式でなければなりません(実際の例は symbols/ami_ipm41d3.txt にあります):
#
# これはコメントです
#
0xdeadbeef my_symbol1
0x123 othersymbol # この関数は xyz を行います
# 10 進アドレスの例
456 anotherone
例えば、symbols/ami_ipm41d3.txt にあるシンボルファイルを考慮すると、ユーザーは次のようにできます:
$ ./simbolify.py symbols/ami_ipm41d3.txt ip41symbols.elf
次に、GDB で次のようにロードします:
(gdb) add-symbol-file ip41symbols.elf 0
add symbol table from file "ip41symbols.elf" at
.text_addr = 0x0
(y or n) y
Reading symbols from ip41symbols.elf...
(No debugging symbols found in ip41symbols.elf)
(gdb) p cseg_
cseg_change_video_mode_logo cseg_get_cpuname
(gdb) p cseg_
GDB のオートコンプリートも期待通りに動作することに注目してください。すごいでしょう?
いくつあるかって?たくさんあります。 デバッグ中のコードはデバッグされていることを認識していないため、さまざまな方法でデバッガに干渉する可能性があります。いくつか例を挙げます:
プロテクトモードへのジャンプ: デバッグ中のコードがプロテクトモードに切り替わると、割り込みハンドラなどの構造が変更され、デバッガはその時点で呼び出されなくなります。ただし、以前の状態を完全に復元してリアルモードに戻ると、デバッガが再び動作する可能性があります。
IDT の変更: 何らかの理由でデバッグ中のコードが IDT またはそのベースアドレスを変更した場合、デバッガハンドラは適切に呼び出されません。
スタック: BREAD はスタックを使用し、それが存在することを前提としています!スタックがまだ構成されていない場所に挿入しないでください。
BIOS デバッグの場合、他の制限もあります。例えば、BREAD が正しく動作するためには最低限のセットアップ(RAM など)が必要なため、BIOS コードの最初(ブートブロック)からデバッグすることはできません。ただし、CS:EIP を F000:FFF0 に設定して「ウォームリブート」を実行することは可能です。このシナリオでは、BREAD が適切にロードされているため、BIOS 初期化を再度追跡できます。ウォームリブート時の BIOS 初期化の「コードパス」はコールドリブートとは異なる場合があり、実行フローがまったく同じではない可能性があることに注意してください。
ビルドには GNU Make、C コンパイラ(GCC、Clang、TCC など)、NASM、および Linux マシンのみが必要です。
デバッガには 2 つの動作モードがあります: ポーリング(デフォルト)と割り込みベース:
ポーリングモードは最も単純なアプローチであり、さまざまな環境でうまく動作するはずです。ただし、ポーリングの性質上、CPU 使用率が高くなります:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make
割り込みベースモードは、UART 割り込みを使用して新しいデータを受信することで、CPU 使用率を最適化します。これにより、CPU はデバッガからコマンドを受信するまで「停止」状態を維持し、CPU リソースを 100% 消費するのを防ぎます。ただし、割り込みが常に有効であるとは限らないため、このモードはデフォルトでは設定されていません:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make UART_POLLING=no
BREAD を使用するには、シリアルケーブル(そしてそうです、あなたのマザーボードには COM ヘッダーがあります、マニュアルを確認してください)と、適切な場所へのコードの注入のみが必要です。
注入するには、dbg.asm(デバッガのソース)に最小限の変更を加える必要があります。コードの「ORG」を変更し、コードの戻り方を変更する必要があります(変更が必要な場所については、コード内の「>> CHANGE_HERE <<」を探してください)。
AMI レガシーを例にとると、デバッガモジュールは BIOS ロゴの場所(0x108200 または FFFF:8210)に配置され、ROM 内の以下の命令がモジュールへの far call に置き換えられます:
...
00017EF2 06 push es
00017EF3 1E push ds
00017EF4 07 pop es
00017EF5 8BD8 mov bx,ax -┐ 置換後: call 0xFFFF:0x8210 (dbg.bin)
00017EF7 B8024F mov ax,0x4f02 -┘
00017EFA CD10 int 0x10
00017EFC 07 pop es
00017EFD C3 ret
...
次のパッチで十分です:
diff --git a/dbg.asm b/dbg.asm
index caedb70..88024d3 100644
--- a/dbg.asm
+++ b/dbg.asm
@@ -21,7 +21,7 @@
; SOFTWARE.
[BITS 16]
-[ORG 0x0000] ; >> CHANGE_HERE <<
+[ORG 0x8210] ; >> CHANGE_HERE <<
%include "constants.inc"
@@ -140,8 +140,8 @@ _start:
; >> CHANGE_HERE <<
; Overwritten BIOS instructions below (if any)
- nop
- nop
+ mov ax, 0x4F02
+ int 0x10
nop
nop
ROM 内でデバッガコードを呼び出すためにいくつかの命令を変更した場合、それらはデバッガから戻る前に復元する必要があることに注意することが重要です。
これらの 2 つの命令を置き換える理由は、BIOS がロゴを画面に表示する直前に実行されるためであり、これによりいくつかの重要な点が保証されます:
デバッガを呼び出す適切な場所(BIOS が十分に初期化されているが、遅すぎない場所)を見つけるのは難しい場合がありますが、可能です。
この後、dbg.bin は ROM 内の正しい位置に挿入する準備が整います。
BREAD で DOS プログラムをデバッグするのは少し注意が必要ですが、可能です:
dbg.asm を編集します:times)int 0x20)を設定する次のパッチで対応します:
diff --git a/dbg.asm b/dbg.asm
index caedb70..b042d35 100644
--- a/dbg.asm
+++ b/dbg.asm
@@ -21,7 +21,10 @@
; SOFTWARE.
[BITS 16]
-[ORG 0x0000] ; >> CHANGE_HERE <<
+[ORG 0x100]
+
+times 40*1024 db 0x90 ; 距離を保つ,
+ ; 40kB で十分なはず
%include "constants.inc"
@@ -140,7 +143,7 @@ _start:
; >> CHANGE_HERE <<
; Overwritten BIOS instructions below (if any)
- nop
+ int 0x20 ; DOS 割り込みでプロセスを終了
nop
カーネルとターミナルだけを含む起動可能な FreeDOS(または DOS)フロッピーイメージを作成します: KERNEL.SYS と COMMAND.COM。このフロッピーイメージに、デバッグするプログラムと DBG.COM(dbg.bin)も追加します。
イメージ作成後、次の手順を実行します:
bridge で起動します(次のセクションの指示を参照)。DBG.COM を実行します。DBG.COM プロセスが終了するまで続行を許可します。DOS はプロセスが終了してもプロセスイメージを消去しないことに注意することが重要です。その結果、デバッガは他の DOS プログラムと同様に設定でき、適切なブレークポイントを設定できます。デバッガの先頭は NOP で埋められているため、新しいプロセスがデバッガのメモリを上書きしないことが予想され、デバッガは「終了」したように見えても機能し続けます。これにより、BREAD は他のプログラム(DOS 自体も含む)をデバッグできます。
ブリッジはデバッガと GDB の間の接着剤であり、実際のハードウェアでも仮想マシンでも、さまざまな方法で使用できます。
そのパラメータは次のとおりです:
Usage: ./bridge [options]
Options:
-s Enable serial through socket, instead of device
-d <path> Replaces the default device path (/dev/ttyUSB0)
(does not work if -s is enabled)
-p <port> Serial port (as socket), default: 2345
-g <port> GDB port, default: 1234
-h This help
If no options are passed the default behavior is:
./bridge -d /dev/ttyUSB0 -g 1234
Minimal recommended usages:
./bridge -s (socket mode, serial on 2345 and GDB on 1234)
./bridge (device mode, serial on /dev/ttyUSB0 and GDB on 1234)
実際のハードウェアで使用するには、パラメータなしで起動するだけです。必要に応じて、-d パラメータでデバイスパスを変更できます:
./bridge または ./bridge -d /path/to/device)Single-stepped, you can now connect GDB! を待ち、その後 GDB を起動する: gdb仮想マシンで使用する場合、実行順序が少し変わります:
./bridge または ./bridge -d /path/to/device)make bochs または make qemu)Single-stepped, you can now connect GDB! を待ち、その後 GDB を起動する: gdbどちらの場合も、GDB が 16 ビットで正しく動作するために必要な補助ファイルがあるため、BRIDGE のルートフォルダ内で GDB を実行してください。
BREAD は常にコミュニティに開かれており、問題、ドキュメント、テスト、新機能、バグ修正、タイポなどの貢献を歓迎します。ようこそ。
BREAD は MIT ライセンスの下でライセンスされています。Davidson Francis および(できれば)他の コントリビューター によって作成されました。
ブレークポイントはハードウェアブレークポイントとして実装されているため、使用可能なブレークポイントの数に制限があります。現在の実装では、同時にアクティブなブレークポイントは 1 つだけです! ↩
ハードウェアウォッチポイント(ブレークポイントと同様)も、同時に 1 つだけサポートされています。 ↩
デバッグレジスタは VM ではデフォルトで動作しないことに注意してください。bochs の場合は、--enable-x86-debugger=yes フラグを付けてコンパイルする必要があります。Qemu の場合は、KVM を有効にして実行する必要があります: --enable-kvm(make qemu はすでにこれを実行しています)。 ↩