BREAD (BIOS Reverse Engineering & Superior Debugging) is an ‘injectable’ real-mode x86 debugger that may debug arbitrary real-mode code (on actual HW) from one other PC through serial cable.
Introduction
BREAD emerged from many failed makes an attempt to reverse engineer legacy BIOS. Provided that the overwhelming majority — if not all — BIOS evaluation is completed statically utilizing disassemblers, understanding the BIOS turns into extraordinarily troublesome, since there isn’t any solution to know the worth of registers or reminiscence in a given piece of code.
Regardless of this, BREAD may debug arbitrary code in real-mode, corresponding to bootable code or DOS packages too.
The way it works?
This debugger is split into two components: the debugger (written completely in meeting and working on the {hardware} being debugged) and the bridge, written in C and working on Linux.
The debugger is the injectable code, written in 16-bit real-mode, and might be positioned inside the BIOS ROM or some other real-mode code. When executed, it units up the suitable interrupt handlers, places the processor in single-step mode, and waits for instructions on the serial port.
The bridge, then again, is the hyperlink between the debugger and GDB. The bridge communicates with GDB through TCP and forwards the requests/responses to the debugger via the serial port. The thought behind the bridge is to take away the complexity of GDB packets and set up an easier protocol for speaking with the machine. As well as, the easier protocol permits the ultimate code measurement to be smaller, making it simpler for the debugger to be injectable into numerous completely different environments.
As proven within the following diagram:
+---------+ easy packets +----------+ GDB packets +---------+
| |--------------->| |--------------->| |
| dbg | | bridge | | gdb |
|(actual HW)|<---------------| (Linux) |<---------------| (Linux) |
+---------+ serial +----------+ TCP +---------+
Options
By implementing the GDB stub, BREAD has many options out-of-the-box. The next instructions are supported:
- Learn reminiscence (through x, dump, discover, and relateds)
- Write reminiscence (through set, restore, and relateds)
- Learn and write registers
- Single-Step (si, stepi) and proceed (c, proceed)
- Breakpoints (b, break)1
- {Hardware} Watchpoints (watch and its siblings)2
Limitations
What number of? Sure. For the reason that code being debugged is unaware that it’s being debugged, it will possibly intrude with the debugger in a number of methods, to call a couple of:
-
Protected-mode bounce: If the debugged code switches to protected-mode, the buildings for interrupt handlers, and many others. are altered and the debugger will not be invoked at that time within the code. Nonetheless, it’s potential {that a} bounce again to actual mode (restoring the complete earlier state) will permit the debugger to work once more.
-
IDT modifications: If for any cause the debugged code modifications the IDT or its base tackle, the debugger handlers won’t be correctly invoked.
-
Stack: BREAD makes use of a stack and assumes it exists! It shouldn’t be inserted into areas the place the stack has not but been configured.
For BIOS debugging, there are different limitations corresponding to: it’s not potential to debug the BIOS code from the very beggining (bootblock), at least setup (corresponding to RAM) is required for BREAD to operate appropriately. Nonetheless, it’s potential to carry out a “warm-reboot” by setting CS:EIP to F000:FFF0
. On this state of affairs, the BIOS initialization might be adopted once more, as BREAD is already correctly loaded. Please word that the “code-path” of BIOS initialization throughout a warm-reboot could also be completely different from a cold-reboot and the execution circulate will not be precisely the identical.
Constructing
Constructing solely requires GNU Make, a C compiler (corresponding to GCC, Clang, or TCC), NASM, and a Linux machine.
The debugger has two modes of operation: polling (default) and interrupt-based:
Polling mode
Polling mode is the best method and may work effectively in a wide range of environments. Nonetheless, due the polling nature, there’s a excessive CPU utilization:
Constructing
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make
Interrupt-based mode
The interrupt-based mode optimizes CPU utilization by using UART interrupts to obtain new knowledge, as a substitute of continually polling for it. This leads to the CPU remaining in a ‘halt’ state till receiving instructions from the debugger, and thus, stopping it from consuming 100% of the CPU’s sources. Nonetheless, as interrupts should not all the time enabled, this mode isn’t set because the default choice:
Constructing
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make UART_POLLING=no
Utilization
Utilizing BREAD solely requires a serial cable (and sure, your motherboard has a COM header, verify the handbook) and injecting the code on the applicable location.
To inject, minimal modifications should be made in dbg.asm (the debugger’s src). The code’s ‘ORG’ should be modified and likewise how the code ought to return (search for “>> CHANGE_HERE <<
” within the code for locations that must be modified).
For BIOS (e.g., AMI Legacy):
Utilizing an AMI legacy for example, the place the debugger module will likely be positioned within the place of the BIOS emblem (0x108200
or FFFF:8210
) and the next directions within the ROM have been changed with a far name to the module:
...
00017EF2 06 push es
00017EF3 1E push ds
00017EF4 07 pop es
00017EF5 8BD8 mov bx,ax -┐ changed by: name 0xFFFF:0x8210 (dbg.bin)
00017EF7 B8024F mov ax,0x4f02 -┘
00017EFA CD10 int 0x10
00017EFC 07 pop es
00017EFD C3 ret
...
the next patch is ample:
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 <<
%embody "constants.inc"
@@ -140,8 +140,8 @@ _start:
; >> CHANGE_HERE <<
; Overwritten BIOS directions beneath (if any)
- nop
- nop
+ mov ax, 0x4F02
+ int 0x10
nop
nop
It is very important word that when you’ve got altered a couple of directions inside your ROM to invoke the debugger code, they should be restored previous to getting back from the debugger.
The rationale for changing these two directions is that they’re executed simply previous to the BIOS displaying the emblem on the display screen, which is now the debugger, guaranteeing a couple of key factors:
- The emblem module (which is the debugger) has already been loaded into reminiscence
- Video interrupts from the BIOS already work
- The code round it signifies that the stack already exists
Discovering an excellent location to name the debugger (the place the BIOS has already initialized sufficient, however not too late) might be difficult, however it’s potential.
After this, dbg.bin
is able to be inserted into the right place within the ROM.
For DOS
Debugging DOS packages with BREAD is a bit tough, however potential:
1. Edit dbg.asm
in order that DOS understands it as a sound DOS program:
- Set the ORG to 0x100
- Depart the helpful code away from the start of the file (
occasions
) - Set this system output (
int 0x20
)
The next patch addresses this:
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]
+
+occasions 40*1024 db 0x90 ; preserve far,
+ ; 40kB needs to be sufficient
%embody "constants.inc"
@@ -140,7 +143,7 @@ _start:
; >> CHANGE_HERE <<
; Overwritten BIOS directions beneath (if any)
- nop
+ int 0x20 ; DOS interrupt to exit course of
nop
2. Create a minimal bootable DOS setting and run
Create a bootable FreeDOS (or DOS) floppy picture containing simply the kernel and the terminal: KERNEL.SYS
and COMMAND.COM
. Additionally add to this floppy picture this system to be debugged and the DBG.COM
(dbg.bin
).
The next steps needs to be taken after creating the picture:
- Boot it with
bridge
already opened (discuss with the subsequent part for directions). - Execute
DBG.COM
. - As soon as execution stops, use GDB so as to add any desired breakpoints and watchpoints relative to the subsequent course of you wish to debug. Then, permit the
DBG.COM
course of to proceed till it finishes. - Run the method that you simply wish to debug. The previously-configured breakpoints and watchpoints ought to set off as anticipated.
It is very important word that DOS doesn’t erase the method picture after it exits. In consequence, the debugger might be configured like some other DOS program and the suitable breakpoints might be set. The start of the debugger is crammed with NOPs, so it’s anticipated that the brand new course of won’t overwrite the debugger’s reminiscence, permitting it to proceed functioning even after it seems to be “completed”. This enables BREaD to debug different packages, together with DOS itself.
Bridge
Bridge is the glue between the debugger and GDB and can be utilized in several methods, whether or not on actual {hardware} or digital machine.
Its parameters are:
Utilization: ./bridge [options]
Choices:
-s Allow serial via socket, as a substitute of system
-d <path> Replaces the default system path (/dev/ttyUSB0)
(doesn't work if -s is enabled)
-p <port> Serial port (as socket), default: 2345
-g <port> GDB port, default: 1234
-h This assistIf no choices are handed the default habits is:
./bridge -d /dev/ttyUSB0 -g 1234
Minimal really useful usages:
./bridge -s (socket mode, serial on 2345 and GDB on 1234)
./bridge (system mode, serial on /dev/ttyUSB0 and GDB on 1234)
Actual {hardware}
To apply it to actual {hardware}, simply invoke it with out parameters. Optionally, you’ll be able to change the system path with the -d
parameter:
Execution circulate:
- Join serial cable to PC
- Run bridge (
./bridge
or./bridge -d /path/to/system
) - Activate the PC to be debugged
- Look ahead to the message:
Single-stepped, now you can join GDB!
after which launch GDB:gdb
.
Digital machine
To be used in a digital machine, the execution order modifications barely:
Execution circulate:
- Run bridge (
./bridge
or./bridge -d /path/to/system
) - Open the VM3 (corresponding to:
make bochs
ormake qemu
) - Look ahead to the message:
Single-stepped, now you can join GDB!
after which launch GDB:gdb
.
In each circumstances, you’ll want to run GDB contained in the BRIDGE root folder, as there are auxiliary recordsdata on this folder for GDB to work correctly in 16-bit.
Contributing
BREAD is all the time open to the group and prepared to just accept contributions, whether or not with points, documentation, testing, new options, bugfixes, typos, and and many others. Welcome aboard.
License and Authors
BREAD is licensed beneath MIT License. Written by Davidson Francis and (hopefully) different contributors.