BREAD(BIOS 逆向工程和高級調試器)是一種「可注入」實模式 x86 調試器,可透過串行電纜從另一台 PC 調試任意實模式代碼(在真實硬體上)。
BREAD 是在對傳統 BIOS 進行逆向工程的多次失敗嘗試中誕生的。鑑於絕大多數(如果不是全部)BIOS 分析是使用反彙編程式靜態完成的,理解 BIOS 變得極其困難,因為無法知道給定程式碼段中暫存器或記憶體的值。
儘管如此,BREAD 還可以在實模式下偵錯任意程式碼,例如可啟動程式碼或 DOS 程式。
快速示範:
透過 BREAD 更改 CPU 字串名稱
這個調試器分為兩部分:調試器(完全用彙編編寫並在被調試的硬體上運行)和橋接器,用 C 編寫並在 Linux 上運行。
偵錯器是可注入程式碼,以 16 位元實模式編寫,可以放置在 BIOS ROM 或任何其他實模式程式碼中。執行時,它會設定適當的中斷處理程序,將處理器置於單步模式,並等待串行埠上的命令。
另一方面,橋是調試器和 GDB 之間的連結。橋接器透過 TCP 與 GDB 通信,並透過串行埠將請求/回應轉送到偵錯器。橋接器背後的想法是消除 GDB 資料包的複雜性並建立一個更簡單的協定來與機器通訊。此外,更簡單的協定使最終程式碼大小更小,使偵錯器更容易注入到各種不同的環境中。
如下圖所示:
+---------+ simple packets +----------+ GDB packets +---------+
| | --------------- > | | --------------- > | |
| dbg | | bridge | | gdb |
| ( real HW ) | <- -------------- | ( Linux ) | <- -------------- | ( Linux ) |
+---------+ serial +----------+ TCP +---------+
透過實現 GDB 存根,BREAD 具有許多開箱即用的功能。支援以下命令:
在 GDB 中對原始二進位(例如 BIOS)進行逆向工程自動意味著沒有其原始符號。然而,隨著 RE 過程的進展,使用者/程式設計師/駭客可以更好地理解程式碼的某些部分,並且 IDA、Cutter、Ghidra 等靜態分析工具允許添加註釋、註釋、函數定義等等。這些增強功能顯著提高了使用者的工作效率。
考慮到這一點,專案中有一個名為symbolify.py
的配套 Python 腳本。給定一個符號列表(地址標籤),它會產生一個添加了這些符號的最小 ELF 檔案。然後可以將該 ELF 載入到 GDB 中並用於大幅簡化偵錯過程。
符號檔案可以包含空格、空白行、註解 (#) 以及位址行上的註解。位址可以採用十進位或十六進位格式,標籤/符號(由一個或多個空格字元分隔)可以採用[a-z0-9_]+ 的形式,如(真實範例可以在symbols/ami_ipm41d3.txt 中找到)。
#
# This is a comment
#
0xdeadbeef my_symbol1
0x123 othersymbol # This function does xyz
# Example with decimal address
456 anotherone
例如,考慮到符號/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 偵錯,還有其他限制,例如:不可能從一開始(引導區塊)調試 BIOS 程式碼,因為 BREAD 需要最低限度的設定(例如 RAM)才能正常運作。但是,可以透過將 CS:EIP 設定為F000:FFF0
來執行「熱重啟」。在這種情況下,可以再次進行 BIOS 初始化,因為 BREAD 已經正確載入。請注意,熱重啟期間 BIOS 初始化的「程式碼路徑」可能與冷重啟期間不同,且執行流程可能不完全相同。
建置只需要 GNU Make、C 編譯器(例如 GCC、Clang 或 TCC)、NASM 和 Linux 機器。
調試器有兩種操作模式:輪詢(預設)和基於中斷:
輪詢模式是最簡單的方法,應該適用於各種環境。然而,由於輪詢的性質,CPU 使用率很高:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make
基於中斷的模式透過利用 UART 中斷接收新資料而不是不斷輪詢來最佳化 CPU 使用率。這會導致 CPU 保持「暫停」狀態,直到收到來自偵錯器的命令,從而防止它消耗 100% 的 CPU 資源。然而,由於中斷並不總是啟用,因此該模式未設定為預設選項:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make UART_POLLING=no
使用BREAD只需要一條串行電纜(是的,你的主機板有一個COM頭,請檢查手冊)並在適當的位置注入代碼。
要注入,必須在 dbg.asm(調試器的 src)中進行最小的更改。必須更改程式碼的“ORG”以及程式碼應如何返回(在程式碼中尋找“ >> CHANGE_HERE <<
”以查找需要更改的位置)。
以 AMI 舊版為例,其中偵錯器模組將放置在 BIOS 標誌的位置( 0x108200
或FFFF:8210
),並且 ROM 中的以下指令已替換為對該模組的遠端呼叫:
...
00017EF2 06 push es
00017EF3 1E push ds
00017EF4 07 pop es
00017EF5 8BD8 mov bx , ax - ┐ replaced by: 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 中的一些指令來呼叫偵錯器程式碼,則必須在從偵錯器返回之前恢復它們。
替換這兩個指令的原因是它們是在 BIOS 在螢幕上顯示徽標(現在是偵錯器)之前執行的,確保了幾個關鍵點:
找到一個調用偵錯器的好位置(BIOS 已經充分初始化,但還不算太晚)可能具有挑戰性,但這是可能的。
此後, dbg.bin
就可以插入到 ROM 中的正確位置了。
使用 BREAD 調試 DOS 程式有點棘手,但也是可能的:
dbg.asm
以便 DOS 將其理解為有效的 DOS 程式: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 ; keep some distance,
+ ; 40kB should be enough
%include "constants.inc"
@@ -140,7 +143,7 @@ _start:
; >> CHANGE_HERE <<
; Overwritten BIOS instructions below (if any)
- nop
+ int 0x20 ; DOS interrupt to exit process
nop
建立僅包含核心和終端的可引導 FreeDOS(或 DOS)磁碟映像: KERNEL.SYS
和COMMAND.COM
。也要將要偵錯的程式和DBG.COM
( dbg.bin
) 新增到該軟碟映像中。
建立鏡像後應執行以下步驟:
bridge
已經打開的情況下啟動它(請參閱下一節的說明)。DBG.COM
。DBG.COM
進程繼續運作直至完成。需要注意的是,DOS 退出後不會刪除進程映像。因此,偵錯器可以像任何其他 DOS 程式一樣進行配置,並且可以設定適當的斷點。調試器的開頭充滿了 NOP,因此預計新進程不會覆蓋調試器的內存,從而使其即使在看起來“完成”後也能繼續運行。這允許 BREAD 調試其他程序,包括 DOS 本身。
Bridge 是調試器和 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
。在這兩種情況下,請務必在 BRIDGE 根資料夾中執行 GDB,因為該資料夾中存在 GDB 在 16 位元下正常工作的輔助檔案。
BREAD 始終向社區開放,並願意接受貢獻,無論是問題、文件、測試、新功能、錯誤修復、拼寫錯誤等。
BREAD 根據 MIT 許可證獲得許可。由戴維森·弗朗西斯和(希望如此)其他貢獻者撰寫。
斷點作為硬體斷點實現,因此可用斷點的數量有限。在目前的實作中,一次只有 1 個活動斷點! ↩
硬體觀察點(如斷點)也一次僅支援一個。 ↩
請注意,調試寄存器預設在虛擬機器上不起作用。對於 bochs,需要使用--enable-x86-debugger=yes
標誌進行編譯。對於 Qemu,它需要在啟用 KVM 的情況下運行: --enable-kvm
( make qemu
已經這樣做了)。 ↩