
Внедряемый отладчик реального режима x86 для обратной разработки BIOS и отладки произвольного кода реального режима через последовательный кабель, с интеграцией GDB и поддержкой аппаратных точек останова/наблюдения.
BREAD (BIOS Reverse Engineering & Advanced Debugger) — это 'внедряемый' отладчик реального режима x86, который может отлаживать произвольный код реального режима (на реальном оборудовании) с другого ПК через последовательный кабель.
BREAD появился в результате множества неудачных попыток обратного проектирования устаревших BIOS. Учитывая, что подавляющее большинство — если не весь — анализ BIOS выполняется статически с помощью дизассемблеров, понимание BIOS становится чрезвычайно сложным, поскольку невозможно узнать значение регистров или памяти в данном фрагменте кода.
Несмотря на это, BREAD также может отлаживать произвольный код в реальном режиме, например загрузочный код или программы DOS.
Быстрая демонстрация:
https://user-images.githubusercontent.com/8294550/217709970-9007a1e3-7352-470d-a22f-cbb5219d5547.mp4
Изменение имени строки CPU через BREAD
Этот отладчик разделен на две части: отладчик (написанный полностью на ассемблере и работающий на отлаживаемом оборудовании) и мост, написанный на C и работающий на Linux.
Отладчик — это внедряемый код, написанный в 16-битном реальном режиме, и может быть размещен в BIOS ROM или любом другом коде реального режима. При выполнении он настраивает соответствующие обработчики прерываний, переводит процессор в режим пошагового выполнения и ожидает команды на последовательном порту.
Мост, с другой стороны, является связующим звеном между отладчиком и GDB. Мост общается с GDB через TCP и пересылает запросы/ответы отладчику через последовательный порт. Идея моста заключается в том, чтобы убрать сложность пакетов GDB и установить более простой протокол для связи с машиной. Кроме того, более простой протокол позволяет уменьшить конечный размер кода, что упрощает внедрение отладчика в различные среды.
Как показано на следующей диаграмме:
+---------+ simple packets +----------+ GDB packets +---------+
| |--------------->| |--------------->| |
| dbg | | bridge | | gdb |
|(real HW)|<---------------| (Linux) |<---------------| (Linux) |
+---------+ serial +----------+ TCP +---------+
Благодаря реализации GDB-заглушки, BREAD имеет множество функций из коробки. Поддерживаются следующие команды:
Обратное проектирование сырого бинарного файла, такого как BIOS, в GDB автоматически подразумевает отсутствие его исходных символов. Однако по мере продвижения процесса RE пользователь/программист/хакер получает лучшее понимание определенных частей кода, а инструменты статического анализа, такие как IDA, Cutter, Ghidra и другие, позволяют добавлять аннотации, комментарии, определения функций и многое другое. Эти улучшения значительно повышают продуктивность пользователя.
С учетом этого в проекте есть сопутствующий скрипт Python под названием symbolify.py. Получив список символов (адрес метки), он генерирует минимальный 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
Например, учитывая файл символов, доступный в 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 существуют и другие ограничения: невозможно отлаживать код BIOS с самого начала (bootblock), так как для правильной работы BREAD требуется минимальная настройка (например, RAM). Однако можно выполнить "теплую перезагрузку", установив CS:EIP в F000:FFF0. В этом сценарии инициализация BIOS может быть отслежена снова, так как BREAD уже загружен. Обратите внимание, что "путь кода" инициализации BIOS при теплой перезагрузке может отличаться от холодной, и поток выполнения может быть не совсем таким же.
Для сборки требуются только GNU Make, компилятор C (такой как GCC, Clang или TCC), NASM и машина с Linux.
Отладчик имеет два режима работы: опрос (по умолчанию) и на основе прерываний:
Режим опроса — это самый простой подход, который должен хорошо работать в различных средах. Однако из-за природы опроса возникает высокая загрузка ЦП:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make
Режим на основе прерываний оптимизирует использование ЦП, используя прерывания UART для получения новых данных вместо постоянного опроса. Это приводит к тому, что ЦП остается в состоянии 'останова' до получения команд от отладчика, что предотвращает потребление 100% ресурсов ЦП. Однако, поскольку прерывания не всегда включены, этот режим не выбран по умолчанию:
$ git clone https://github.com/Theldus/BREAD.git
$ cd BREAD/
$ make UART_POLLING=no
Использование BREAD требует только последовательный кабель (и да, ваша материнская плата имеет заголовок COM, проверьте руководство) и внедрение кода в соответствующее место.
Для внедрения необходимо внести минимальные изменения в dbg.asm (исходный код отладчика). Должен быть изменен 'ORG' кода, а также то, как код должен возвращаться (ищите ">> CHANGE_HERE <<" в коде для мест, которые нужно изменить).
Используя AMI Legacy в качестве примера, где модуль отладчика будет размещен на месте логотипа 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.
Отладка DOS-программ с BREAD немного сложна, но возможна:
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.
Мост является связующим звеном между отладчиком и 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 в корневой папке BRIDGE, так как в этой папке есть вспомогательные файлы для корректной работы GDB в 16-битном режиме.
BREAD всегда открыт для сообщества и готов принимать вклад, будь то в виде вопросов, документации, тестирования, новых функций, исправлений ошибок, опечаток и т.д. Добро пожаловать.
BREAD распространяется под лицензией MIT. Автор: Davidson Francis и (надеюсь) другие участники.
Точки останова реализованы как аппаратные точки останова, поэтому доступно ограниченное количество точек останова. В текущей реализации одновременно активна только 1 точка останова! ↩
Аппаратные точки наблюдения (как и точки останова) также поддерживаются только по одной за раз. ↩
Обратите внимание, что отладочные регистры по умолчанию не работают на VM. Для bochs его нужно скомпилировать с флагом --enable-x86-debugger=yes. Для Qemu его нужно запускать с включенным KVM: --enable-kvm (make qemu уже делает это). ↩