Mastodon
Showing posts with label отладка. Show all posts
Showing posts with label отладка. Show all posts

Monday, July 2, 2012

VMware + GDB stub + IDA

Многим известно, что в системе виртуализации VMware Worstation с самых незапамятных времен существует стандартный GDB интерфейс для удалённой отладки, однако, мало кто пользуется ним в повседневной работе в силу того, что отладку в "голом" GDB тяжело назвать удобной. Однако, в роли вполне вменяемого клиента для отладочного интерфейса GDB может выступать IDA Pro, в которой соответствующий функционал был добавлен ещё в 5-й ветке, но работать должным образом и без багов начал только в 6.0.
Отладка гостевой операционной системы Windows c использованием связки VMware + GDB stub + IDA бывает незаменимой когда приходится иметь дело с детектированием удалённых отладчиков ядра, отладкой PatchGuard, отладкой процесса загрузки ОС а так же критического кода (например, векторов прерываний) который по тем или иным причинам тяжело отлаживать с помощью WinDbg.
Очевидно, что отладочный интерфейс гипервизора ничего не знает о среде запущенной на нем  операционной системы Windows сам по себе, поэтому, для комфортной отладки с использованием VMware + GDB stub требуется некоторая настройка IDA Pro как debug клиента. В Hex Blog, в своё время, была опубликована заметка с описанием подобной настройки, к которой прилагался скрипт vmware_modules.py, который осуществляет:
  • Нахождение nt!PsLoadedModuleList и перечисление всех загруженных в настоящий момент модулей режима ядра, с последующим созданием сегментов для каждого из них.
  • Загрузку отладочных символов для всех найденных модулей.
Из-за ряда ошибок и недоработок в оригинальном скрипте, который не позволял нормально работать с ним - я решил его переписать, внеся следующие изменения:
  • Переписан алгоритм нахождения nt!PsLoadedModuleList. Оригинальный скрипт использовал базу сегмента FS для доступа к структуре _KPCR, что является не лучшей идеей: на остановленной в случайное время виртуальной машине в FS может быть загружен как user-mode, так и kernel-mode селектор, а поскольку в режиме пользователя FS указывает на совсем другую структуру (_TEB) - очень часто скрипт vmware_modules.py попросту не срабатывал. Вместо этого я реализовал поиск адреса nt!PsLoadedModuleList по сигнатуре в исполняемом образе ядра, адрес загрузки которого, в свою очередь, находится путём анализа векторов прерываний.
  • Добавлена полная поддержка Windows x64, отсутствовавшая в оригинальном скрипте. Поскольку стандартный способ для определения разрядности debug target-а через idainfo.is_32bit() / idainfo.is_64bit() на моей версии IDA Pro и IDAPython не работал (соответствующие функции всегда возвращали False) - я реализовал данный функционал путём небольшого хака с проверкой значения старших 24-х бит базы IDT.
  • Исправлены ошибки с загрузкой отладочных символов для модулей, чей полный путь не соответствует формату \SystemRoot\System32\<some_path>.
Моя версия скрипта доступна для загрузки в репозитории на GitHub:
https://github.com/Cr4sh/IDA-VMware-GDB

Необходимые шаги по настройке:
  1. Установить IDA Pro от 6.0 и старше и актуальную версию IDAPython для неё.
  2. Отредактировать .vmx файл виртуальной машины, добавив в него строчку debugStub.listen.guest32 = "TRUE" для 32-х разрядных гостевых операционных систем или debugStub.listen.guest64 = "TRUE" для 64-х разрядных.
  3. Для успешной загрузки отладочных символов необходимо скопировать директорию %SystemRoot%\system32 из гостевой операционной системы в хостовую, и указать путь к ней в переменной SYSTEM32_COPY_PATH внутри скрипта IDA-VMware-GDB.py.
  4. После запуска гостевой операционной системы в VMware Workstation можно в любое время подключить к виртуальной машине удалённый отладчик, для чего в IDA Pro следует выбрать пункт главного меню "Debugger" → "Attach" → "Remote GDB Debugger", указав номер порта 8832 для 32-х разрядного debug target-а или 8864 для 64-х разрядного. При этом IDA и GDB stub не конфликтуют с удалённой отладкой ядра Windows с помощью WinDbg и использовать с виртуальной машиной можно оба отладчика одновременно.
После выполнения данных шагов и запуска скрипта IDA-VMware-GDB.py становится возможной навигация между загруженными в память модулями режима ядра:


А так же навигация по именам, которые были получены из отладочных символов:


Monday, November 14, 2011

DbgCb: интеграция приложения/драйвера с удалённым отладчиком ядра

Думаю, для многих программистов и исследователей набор вида "виртуальная машина с Windows + WinDbg в качестве удалённого отладчика режима ядра" давно стал привычным и необходимым инструментом. Для того, что бы выжать из такой связки максимум возможностей и упростить рутинную работу, многие применяют вещи вроде скриптовых движков для отладчика (pykd, IDAPython) или отдельных врапперов для отладочных интерфейсов Windows (PyDbg).

Однако, очень часто возникает задача не только частичной автоматизации работы отладчика, но и необходимость взаимодействия с ним из кода отлаживаемого на удалённой системе приложения или драйвера. В стандартном случае, это самое приложение или драйвер о присутствии удалённого отладчика вообще ничего не знает, но при решении некоторых задач для него было бы полезно получать от отладчика разного рода информацию а так же исполнять в его контексте команды без непосредственного участия человека.

Примеры из жизни, где подобные механизмы интеграции приложения/драйвера с отладчиком были бы кстати:
  • Исследование программ и компонентов операционной системы в процессе их работы, в ходе которого может понадобится устанавливать перехваты кода, и делать достаточно сложную обработку полученных данных, для реализации которой попросту не хватит возможностей (или производительности) скрипта на pykd, использующего точки останова.
  • Иногда, для того что бы быстро проверить какую-то идею/технологию, бывает необходимо прямо в "живой" проект добавить код, работающий с какими-либо не экспортируемыми функциями операционной системы (или недокументированными структурами), но вместо жесткого хардкодинга их адресов/смещений было бы удобно "спросить" об этих адресах/смещениях удалённый отладчик, который может извлечь всю необходимую информацию из доступных ему актуальных отладочных символов.
  • В процессе отладки своего кода бывает необходимо с некоторой периодичностью выполнять определённые манипуляции в отладчике (вывести стек вызовов в определённой точке, снять/установить точку останова в момент наступления какого-то события, итд.), без необходимости отвлекаться на, собственно, сам отладчик.
Для решения таких вот небольших повседневных задач я разработал инструмент, под названием DbgCb (Kernel Debugger Communication Engine), который и хочу представить широкому вниманию в данной заметке.

Функционально DbgCb состоит из двух компонентов:
  • Клиентский код, встраиваемый в приложение или драйвер, из которого доступен ряд функций, совершающих полезные действия с удалённым ядерным отладчиком.
  • Расширение для Windows Debugging Tools - выполнено в виде плагина для отладчика WinDbg/KD, предназначено для обработки запросов от клиентского кода.



Взаимодействие этих компонентов реализовано тривиально: плагин для WinDbg использует интерфейс IDebugEventCallbacks, позволяющий ему отслеживать различные события (срабатывание точек останова, изменения состояния отлаживаемой системы, итд.), а клиентский код, при вызове полезной функции, просто генерирует #DB (debug exception), поместив перед этим в регистры процессора специальные "магические" константы, значения которых и сообщают плагину о том, выполнение какой операции от него требуется.

Клиентский код выполнен в виде небольшого фрагмента на языке C, который может быть встроен в произвольное приложение или драйвер, а так же оформлен как библиотека. В текущей версии, в нём реализованы следующие три полезных функции:
 1 /**
 2  * Execute debuuger command (IDebugControl::Execute()).
 3  */
 4 BOOLEAN dbg_exec(PCHAR lpFormat, ...);
 5 
 6 /**
 7  * Evaluate debuuger expression (IDebugControl::Evaluate()).
 8  */
 9 PVOID dbg_eval(PCHAR lpFormat, ...);
10 
11 /**
12  * Get offset of the some structure field
13  */
14 LONG dbg_field_offset(PCHAR lpFormat, ...);

Пример использования API клиентского кода DbgCb в драйвере режима ядра:
 1 NTSTATUS NTAPI DriverEntry(
 2     PDRIVER_OBJECT DriverObject,
 3     PUNICODE_STRING RegistryPath)
 4 {    
 5     DriverObject->DriverUnload = DriverUnload;
 6     
 7     DbgPrint(__FUNCTION__"()\n");    
 8 
 9     // Test debugger command execution.
10     if (dbg_exec(".printf /D \"<b>Hello from " __FUNCTION__ "()</b>\\n\""))
11     {
12         // another DML example
13         dbg_exec(
14             ".printf /D \"<exec cmd=\\\"!drvobj "IFMT"\\\">"
15             "Show _DRIVER_OBJECT information.</exec>\\n\"", 
16             DriverObject
17         );
18 
19         DbgPrint("Breaking into the kernel debugger (check the DML link above)...\n");
20         DbgBreakPoint();
21 
22         // Test symbol querying.
23         PVOID Addr = dbg_eval("nt!KiDispatchException");
24         if (Addr)
25         {
26             DbgPrint("<?dml?><b>nt!KiDispatchException() is at "IFMT"</b>\n", Addr);
27         }        
28         else
29         {
30             DbgPrint(__FUNCTION__"() ERROR: dbg_eval() fails\n");
31         }
32 
33         // Test structure field offset querying.
34         LONG Offset = dbg_field_offset("nt!_EPROCESS::ImageFileName");
35         if (Offset >= 0)
36         {
37             DbgPrint("<?dml?><b>_EPROCESS::ImageFileName offset is 0x%x</b>\n", Offset);
38         }
39         else
40         {
41             DbgPrint(__FUNCTION__"() ERROR: dbg_field_offset() fails\n");
42         }
43     }    
44     else
45     {
46         DbgPrint(__FUNCTION__"() WARNING: dbgcb extension is not loaded\n");
47     }
48     
49     return STATUS_SUCCESS;
50 }

Исходный код DbgCb доступен в репозитории на GitHub, и включает в себя:
  • ./dbgcb/ - Исходные тексты расширения для отладчика.
  • ./dbgcb.dll, ./dbgcb_x64.dll - Исполняемые файлы расширения для отладчика WinDbg/KD.
  • ./common/ - Исходные тексты клиентского кода.
  • ./dbgcb_app/ - Исходные тексты примера использования клиентского кода в приложении.
  • ./dbgcb_app.exe, ./dbgcb_app_x64.exe - Исполняемые файлы примера приложения.
  • ./dbgcb_drv/ - Исходные тексты примера использования клиентского кода в драйвере.
  • ./dbgcb_drv.sys, ./dbgcb_drv_x64.sys - Исполняемые файлы примера драйвера.
Перед запуском примера драйвера или приложения, в среде отлаживаемой операционной системы, необходимо выполнить загрузку модуля DbgCb в отладчик, используя команду ".load dbgcb.dll".

При запуске примера драйвера в Command Window отладчика появятся следующие сообщения, свидетельствующие о том, что Kernel Debugger Communication Engine работает:



Помимо пользы для исследовательских задач, данный инструмент существенно облегчает мне жизнь при анализе уязвимостей и отладке приложений, использующих глубокую интеграцию с системой не совсем "легальными" способами. Возможно, кому-то из вас он так же пригодится.

Идеи по реализации дополнительных возможностей в рамках API клиентского кода очень приветствуются, и принимаются к рассмотрению для реализации.