BREAD (BIOS Reverse Engineering & Advanced Debugger) adalah debugger x86 mode nyata 'yang dapat diinjeksi' yang dapat men-debug kode mode nyata sewenang-wenang (pada HW nyata) dari PC lain melalui kabel serial.
ROTI muncul dari banyak upaya yang gagal untuk merekayasa balik BIOS lama. Mengingat sebagian besar -- jika tidak semua -- analisis BIOS dilakukan secara statis menggunakan disassembler, memahami BIOS menjadi sangat sulit, karena tidak ada cara untuk mengetahui nilai register atau memori dalam potongan kode tertentu.
Meskipun demikian, BREAD juga dapat men-debug kode arbitrer dalam mode nyata, seperti kode bootable atau program DOS juga.
Demo cepat:
Mengubah nama string CPU melalui BREAD
Debugger ini dibagi menjadi dua bagian: debugger (seluruhnya ditulis dalam rakitan dan dijalankan pada perangkat keras yang sedang di-debug) dan jembatan, ditulis dalam C dan dijalankan di Linux.
Debugger adalah kode yang dapat disuntikkan, ditulis dalam mode nyata 16-bit, dan dapat ditempatkan di dalam ROM BIOS atau kode mode nyata lainnya. Ketika dijalankan, ia akan mengatur penangan interupsi yang sesuai, menempatkan prosesor dalam mode satu langkah, dan menunggu perintah pada port serial.
Bridge, di sisi lain, adalah penghubung antara debugger dan GDB. Bridge berkomunikasi dengan GDB melalui TCP dan meneruskan permintaan/tanggapan ke debugger melalui port serial. Ide dibalik jembatan ini adalah untuk menghilangkan kompleksitas paket GDB dan membangun protokol yang lebih sederhana untuk berkomunikasi dengan mesin. Selain itu, protokol yang lebih sederhana memungkinkan ukuran kode akhir menjadi lebih kecil, sehingga memudahkan debugger untuk dapat disuntikkan ke berbagai lingkungan berbeda.
Seperti yang ditunjukkan pada diagram berikut:
+---------+ simple packets +----------+ GDB packets +---------+
| | --------------- > | | --------------- > | |
| dbg | | bridge | | gdb |
| ( real HW ) | <- -------------- | ( Linux ) | <- -------------- | ( Linux ) |
+---------+ serial +----------+ TCP +---------+
Dengan mengimplementasikan rintisan GDB, BREAD memiliki banyak fitur yang out-of-the-box. Perintah berikut ini didukung:
Merekayasa balik biner mentah, seperti BIOS, di GDB secara otomatis berarti tidak memiliki simbol aslinya. Namun, seiring berjalannya proses RE, pengguna/programmer/peretas memperoleh pemahaman yang lebih baik tentang bagian-bagian tertentu dari kode, dan alat analisis statis seperti IDA, Cutter, Ghidra, dan lainnya memungkinkan penambahan anotasi, komentar, definisi fungsi, dan banyak lagi. Peningkatan ini secara signifikan meningkatkan produktivitas pengguna.
Dengan mengingat hal ini, ada skrip Python pendamping dalam proyek yang disebut symbolify.py
. Dengan adanya daftar simbol (label alamat), ini menghasilkan file ELF minimal dengan tambahan simbol-simbol ini. ELF ini kemudian dapat dimuat ke GDB nanti dan digunakan untuk menyederhanakan proses debugging.
File simbol dapat berisi spasi putih, baris kosong, komentar (#), dan komentar pada baris alamat. Alamat dapat dalam format desimal atau heksadesimal, dan label/simbol (dipisahkan oleh satu atau lebih karakter spasi) dapat dalam bentuk [a-z0-9_]+, seperti pada (contoh nyata dapat ditemukan di simbol/ami_ipm41d3. txt):
#
# This is a comment
#
0xdeadbeef my_symbol1
0x123 othersymbol # This function does xyz
# Example with decimal address
456 anotherone
Misalnya, mengingat file simbol tersedia di simbol/ami_ipm41d3.txt, pengguna dapat melakukan sesuatu seperti:
$ ./simbolify.py symbols/ami_ipm41d3.txt ip41symbols.elf
Kemudian, muat di GDB seperti pada:
(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_
Perhatikan bahwa pelengkapan otomatis GDB pun berfungsi seperti yang diharapkan, luar biasa?
Berapa banyak? Ya. Karena kode yang sedang di-debug tidak menyadari bahwa kode tersebut sedang di-debug, kode tersebut dapat mengganggu proses debugger dalam beberapa cara, antara lain:
Lompatan mode terlindungi: Jika kode yang di-debug beralih ke mode terlindungi, struktur untuk penangan interupsi, dll. akan diubah dan debugger tidak akan lagi dipanggil pada titik tersebut dalam kode. Namun, ada kemungkinan bahwa lompatan kembali ke mode nyata (memulihkan keadaan penuh sebelumnya) akan memungkinkan debugger bekerja kembali.
Perubahan IDT: Jika karena alasan apa pun kode yang di-debug mengubah IDT atau alamat dasarnya, penangan debugger tidak akan dipanggil dengan benar.
Tumpukan: ROTI menggunakan tumpukan dan menganggapnya ada! Ini tidak boleh dimasukkan ke lokasi di mana tumpukan belum dikonfigurasi.
Untuk debugging BIOS, ada batasan lain seperti: tidak mungkin untuk men-debug kode BIOS dari awal (bootblock), karena pengaturan minimum (seperti RAM) diperlukan agar BREAD dapat berfungsi dengan benar. Namun, dimungkinkan untuk melakukan "warm-reboot" dengan mengatur CS:EIP ke F000:FFF0
. Dalam skenario ini, inisialisasi BIOS dapat diikuti kembali, karena BREAD telah dimuat dengan benar. Harap dicatat bahwa "jalur kode" inisialisasi BIOS selama boot ulang hangat mungkin berbeda dari boot ulang dingin dan alur eksekusi mungkin tidak persis sama.
Pembuatan hanya memerlukan GNU Make, kompiler C (seperti GCC, Clang, atau TCC), NASM, dan mesin Linux.
Debugger memiliki dua mode operasi: polling (default) dan berbasis interupsi:
Mode pemungutan suara adalah pendekatan paling sederhana dan dapat bekerja dengan baik di berbagai lingkungan. Namun, karena sifat polling, terdapat penggunaan CPU yang tinggi:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make
Mode berbasis interupsi mengoptimalkan pemanfaatan CPU dengan memanfaatkan interupsi UART untuk menerima data baru, alih-alih melakukan polling terus-menerus. Hal ini mengakibatkan CPU tetap berada dalam status 'berhenti' hingga menerima perintah dari debugger, sehingga mencegahnya mengonsumsi 100% sumber daya CPU. Namun, karena interupsi tidak selalu diaktifkan, mode ini tidak ditetapkan sebagai opsi default:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make UART_POLLING=no
Menggunakan BREAD hanya membutuhkan kabel serial (dan ya, motherboard Anda memiliki header COM, periksa manualnya) dan menyuntikkan kode di lokasi yang sesuai.
Untuk menyuntikkan, perubahan minimal harus dilakukan di dbg.asm (src debugger). 'ORG' kode harus diubah dan juga bagaimana kode harus dikembalikan (cari " >> CHANGE_HERE <<
" pada kode untuk tempat yang perlu diubah).
Menggunakan warisan AMI sebagai contoh, di mana modul debugger akan ditempatkan di tempat logo BIOS ( 0x108200
atau FFFF:8210
) dan instruksi berikut di ROM telah diganti dengan panggilan jauh ke modul:
...
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
...
patch berikut sudah cukup:
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
Penting untuk dicatat bahwa jika Anda telah mengubah beberapa instruksi dalam ROM Anda untuk menjalankan kode debugger, instruksi tersebut harus dipulihkan sebelum kembali dari debugger.
Alasan untuk mengganti kedua instruksi ini adalah karena keduanya dijalankan tepat sebelum BIOS menampilkan logo di layar, yang sekarang menjadi debugger, memastikan beberapa poin penting:
Menemukan lokasi yang baik untuk memanggil debugger (di mana BIOS telah cukup diinisialisasi, namun belum terlambat) dapat menjadi tantangan, namun mungkin saja terjadi.
Setelah ini, dbg.bin
siap dimasukkan ke posisi yang benar di ROM.
Men-debug program DOS dengan BREAD agak rumit, tetapi mungkin saja:
dbg.asm
agar DOS memahaminya sebagai program DOS yang valid:times
)int 0x20
)Patch berikut mengatasi hal ini:
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
Buat image floppy FreeDOS (atau DOS) yang dapat di-boot yang hanya berisi kernel dan terminal: KERNEL.SYS
dan COMMAND.COM
. Tambahkan juga ke gambar floppy ini program yang akan di-debug dan DBG.COM
( dbg.bin
).
Langkah-langkah berikut harus diambil setelah membuat gambar:
bridge
yang sudah terbuka (lihat bagian selanjutnya untuk instruksi).DBG.COM
.DBG.COM
terus berjalan hingga selesai.Penting untuk dicatat bahwa DOS tidak menghapus gambar proses setelah keluar. Hasilnya, debugger dapat dikonfigurasi seperti program DOS lainnya dan breakpoint yang sesuai dapat diatur. Permulaan debugger diisi dengan NOP, sehingga diharapkan bahwa proses baru tidak akan menimpa memori debugger, sehingga memungkinkannya untuk terus berfungsi bahkan setelah proses tersebut tampak "selesai". Hal ini memungkinkan BREAD untuk men-debug program lain, termasuk DOS itu sendiri.
Bridge adalah perekat antara debugger dan GDB dan dapat digunakan dengan berbagai cara, baik pada perangkat keras nyata atau mesin virtual.
Parameternya adalah:
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)
Untuk menggunakannya pada perangkat keras nyata, cukup aktifkan tanpa parameter. Secara opsional, Anda dapat mengubah jalur perangkat dengan parameter -d
:
./bridge
atau ./bridge -d /path/to/device
)Single-stepped, you can now connect GDB!
dan kemudian luncurkan GDB: gdb
. Untuk digunakan di mesin virtual, urutan eksekusi sedikit berubah:
./bridge
atau ./bridge -d /path/to/device
)make bochs
atau make qemu
)Single-stepped, you can now connect GDB!
dan kemudian luncurkan GDB: gdb
.Dalam kedua kasus tersebut, pastikan untuk menjalankan GDB di dalam folder root BRIDGE, karena ada file tambahan di folder ini agar GDB dapat berfungsi dengan baik dalam 16-bit.
BREAD selalu terbuka untuk komunitas dan bersedia menerima kontribusi, baik dalam hal masalah, dokumentasi, pengujian, fitur baru, perbaikan bug, kesalahan ketik, dan lain-lain. Selamat bergabung.
ROTI dilisensikan di bawah Lisensi MIT. Ditulis oleh Davidson Francis dan (semoga) kontributor lainnya.
Breakpoint diimplementasikan sebagai breakpoint perangkat keras dan oleh karena itu memiliki jumlah breakpoint yang tersedia terbatas. Dalam implementasi saat ini, hanya 1 breakpoint aktif dalam satu waktu! ↩
Watchpoint perangkat keras (seperti breakpoint) juga hanya didukung satu per satu. ↩
Harap perhatikan bahwa register debug tidak berfungsi secara default di VM. Untuk bochs, ini perlu dikompilasi dengan flag --enable-x86-debugger=yes
. Untuk Qemu, ini harus dijalankan dengan KVM diaktifkan: --enable-kvm
( make qemu
sudah melakukan ini). ↩