Как и в любом программном обеспечении, драйверы ядра обычно имеют ошибки.
Отладка драйверов, в отличие от пользовательского режима, более сложный процесс.
Отладка драйвера по сути отладка всей системы, не просто конкретного процесса или процессов. Это требует другого мышления.
В этой главе будет обсуждаться отладка ядра с использованием отладчика WinDbg.
В этой главе:
• Инструменты отладки для Windows.
• Введение в WinDbg.
• Отладка ядра.
• Полная отладка ядра.
• Мануал по отладке драйверов ядра.
1) Инструменты отладки для Windows
Пакет средств отладки для Windows содержит набор отладчиков, инструментов и документации, сосредоточим внимание на отладчиках в пакете.
Этот пакет может быть установлен как часть Windows SDK или WDK, но никакой реальной «установки» нет.
Установка просто копирует файлы, но не трогает реестр, то есть пакет зависит только от собственных модулей и библиотеки DLL Windows.
Это позволяет легко копировать весь каталог в любой другой каталог, включая на съемный носитель.
Пакет содержит четыре отладчика: Cdb.exe, Ntsd.Exe, Kd.exe и WinDbg.exe.
Вот краткое изложение основных функциональных возможностей каждого отладчика:
• Cdb и Ntsd - пользовательский режим, консольные отладчики. Это означает, что они могут быть прикреплены к процессу, как и любой другой отладчик пользовательского режима.
Оба имеют консольный интерфейс - введите команду, получите ответ и повторить.
Единственная разница между ними заключается в том, что если запустить консольные окна, Cdb использует ту же консоль, тогда как Ntsd открывает новое консольное окно.
В остальном они идентичны.
• Kd - отладчик ядра с консольным пользовательским интерфейсом. Может подключаться к локальному ядру (Локальная отладка ядра, описанна в следующем разделе) или на другую машину.
• WinDbg - единственный отладчик с графическим интерфейсом пользователя. Может работать в режиме пользователя или в режиме отладки ядра, в зависимости от выбора, выполненного из его меню или аргументов командной строки, с которыми он был запущен.
Недавняя альтернатива классического WinDbg - Windbg Preview, доступный в магазине Microsoft.
Это римейк классического отладчика с гораздо лучшим пользовательским интерфейсом и возможностями.
Может быть установлен на Windows 10 версии 1607 или более поздней.
С функциональной точки зрения, это похоже на классический WinDbg. Но он проще в использовании из-за современного, удобного пользовательского интерфейса.
Все команды, которые мы увидим позже в этой главе должны работать одинаково с любым отладчиком.
Хотя эти отладчики могут отличаться друг от друга, на самом деле отладчики в режиме пользователя по сути те же, что и отладчики ядра.
Все они основаны на одном отладчике.
Движок реализован в виде DLL (DbgEng.Dll).
Различные отладчики могут использовать расширения DLL, которые обеспечивают большую часть функций отладчиков.
Механизм отладчика достаточно документирован в документации по средствам отладки для Windows, и так что можно написать новые отладчики, которые используют тот же движок.
Другие инструменты, которые являются частью пакета, включают (частичный список):
• Gflags.exe - инструмент Global Flags, который позволяет устанавливать некоторые флаги ядра и флаги образов.
• ADPlus.exe - создать файл дампа сбоя или зависания процесса.
• Kill.exe - простой инструмент для завершения процесса (ов) на основе идентификатора процесса, имени или шаблона.
• Dumpchk.exe - инструмент для общей проверки файлов дампа.
• TList.exe - список запущенных процессов в системе с различными параметрами.
• Umdh.exe - анализирует распределение кучи в процессах пользовательского режима.
• UsbView.exe - отображает иерархическое представление USB-устройств и концентраторов.
2) Введение в WinDbg
В этом разделе описываются основы WinDbg, но имейте в виду, что все по сути то же самое для консольных отладчиков, за исключением окон графического интерфейса.
WinDbg построен вокруг команд.
Пользователь вводит команду, а отладчик отвечает текстом, описывающим результаты команды.
С GUI некоторые из этих результатов изображены в выделенных окнах, таких как локальные пользователи, стек, потоки и т. д.
WinDbg поддерживает три типа команд:
• Внутренние команды - эти команды встроены в отладчик и работают на отлаживаемой цели.
• Метакоманды - эти команды начинаются с точки (.) И работают при отладке самого процессв, а не непосредственно на отлаживаемой цели.
• Bang (расширение) команды - эти команды начинаются с восклицательного знака (!). Обеспечивают большую часть возможности отладчика.
По умолчанию отладчик загружает набор предопределенных расширений DLL, но может и больше загружаться из каталога отладчика или из других источников.
Написание расширенных DLL-файлов возможно и полностью задокументировано в документации отладчика. На самом деле, многие такие DLL были созданы и могут быть загружены из соответствующего источника. Эти библиотеки предоставляют новые команды, которые улучшают отладку, часто нацеленные на конкретные сценарии.
3)Уроки. Основы отладки в пользовательском режиме
Если у вас есть опыт работы с WinDbg, вы можете смело пропустить этот раздел.
Этот учебник направлен на получение базового понимания WinDbg и как использовать его для пользовательского режима отладки.
Отладка ядра описана в следующем разделе.
Как правило, есть два способа инициировать отладку в пользовательском режиме - запустить исполняемый файл и прикрепить к нему, или присоединить к уже существующему процессу.
Мы будем использовать последний подход в этом уроке, но за исключением этого первого шага, все остальные операции идентичны.
• Запустите Блокнот.
• Запустите WinDbg (либо Preview, либо классический. На следующих снимках экрана).
• Выберите File / Attach To Process и найдите процесс Notepad в списке (см. Рисунок 5-1). затем нажмите «Прикрепить». Вы должны увидеть результат, похожий на рисунок 5-2.
Окно команд является основным интересующим окном - оно всегда должно быть открыто. Это окно показывает различные ответы на команд. Как правило, большая часть сеанса отладки проходит на взаимодействие с этим окном.
Процесс теперь приостановлен - мы находимся в точке останова, вызванной отладчиком.
• Первая команда, которую мы будем использовать, это shows, которая показывает информацию обо всех потоках в отлаженном процессе:
0:003> ~
0 Id:874c.18068 Suspend: 1 Teb: 00000001`2229d000 Unfrozen
1 Id:874c.46ac Suspend: 1 Teb: 00000001`222a5000 Unfrozen
2 Id:874c.152cc Suspend: 1 Teb: 00000001`222a7000 Unfrozen
3 Id:874c.bb08 Suspend: 1 Teb: 00000001`222ab000 Unfrozen
Точное количество потоков, которые вы увидите, может отличаться от показанного здесь.
Одна вещь, которая очень важна - это наличие правильных символов. Microsoft предоставляет общественности сервер символов, который может использоваться Microsoft для поиска символов для большинства модулей. Это важно в любой низкоуровневой отладке.
• Для быстрой установки символов введите команду .symfix.
• Лучший подход - настроить символы один раз и сделать их доступными для дальнейшей отладки.
Для этого добавьте системную переменную среды с именем _NT_SYMBOL_PATH и установите её:
Средняя часть (между звездочками) - это локальный путь для кэширования символов на вашем локальном компьютере.
Вы можно выбрать любой путь, который вам нравится. Как только эта переменная окружения установлена, следующие вызовы отладчика автоматически найдет символы и при необходимости загрузит их с сервера символов Microsoft.
• Чтобы убедиться, что у вас есть правильные символы, введите команду lm (загруженные модули):
Список модулей показывает все модули (DLL и EXE), загруженные в отлаженный процесс на этом время.
Вы можете увидеть начальные и конечные виртуальные адреса, в которых загружен каждый модуль.
Наименование модуля позволяет увидеть статус символа этого модуля (в скобках).
Возможные значения включают в себя:
• deferred - символы для этого модуля никогда не были нужны в этом сеансе отладки и так что не загружаются в это время. Они будут загружены при необходимости.
• символы pdb - это означает, что были загружены правильные публичные символы. Локальный путь PDB.
• экспортировать символы - для этой DLL доступны только экспортированные символы. Это обычно означает, что там нет символов для этого модуля или они не были найдены.
• нет символов - пытались найти символы этого модуля, но ничего не было найдено, даже не экспортируемые символы (такие модули не имеют экспортированных символов, как в случае исполняемых файлов и файлов драйверов).
Вы можете принудительно загрузить символы модуля, используя команду .reload /f modulename.dll.
Это обеспечит окончательное подтверждение наличия символов для этого модуля.
Пути к символам также можно настроить в диалоговом окне настроек отладчика.
Откройте меню «Файл / Настройки» и найдите «Настройки отладки». Затем вы можете добавить больше путей для поиска символа. Это полезно при отладке собственного кода, искать можно в ваших каталогах, где могут быть найдены соответствующие файлы PDB (см. рисунок 5-3).
Вернитесь к списку потоков - обратите внимание, что один из потоков имеет точку перед своими данными. Это текущий поток. Это означает, что любая выданная команда, которая включает поток, где поток не указан, будет работать на этом потоке.
«Текущий поток» также показан в приглашении (0:003>) - число справа от двоеточия является текущим индексом потока (3 в этом примере).
• Введите команду k, которая показывает трассировку стека текущего потока:
Вы можете увидеть список вызовов этого потока (конечно, только в режиме пользователя).
Вершиной стека является функция DbgBreakPoint, расположенная в модуле ntdll.dll.
Общий формат адресов с символами имеет «modulename!functionname+offset» (Пример:ntdll!DbgUiRemoteBreakin+0x4b). Смещение не является обязательным и может быть нулевым, если это именно начало этой функции.
Также обратите внимание, что имя модуля не имеет расширения.
В приведенном выше выводе DbgBreakpoint был вызван DbgUiRemoteBreakIn, который был вызван BaseThreadInitThunk и так далее.
Этот поток, кстати, был введен отладчиком для принудительного проникновения в цель.
• Чтобы переключиться на другой поток, используйте следующую команду: ∼ ns, где n - индекс потока.
Давайте переключимся на поток 0 и затем отобразим его стек вызовов:
Это основной (первый) поток Блокнота. В верхней части стека показан поток, ожидающий сообщений пользовательского интерфейса.
• Альтернативный способ показать стек вызовов другого потока без переключения на него - это использовать тильду и номер потока перед фактической командой.
Следующий вывод для потока 1:
Обратите внимание, что точка переместилась в нить 0 (текущая нить), показывая хеш-знак (#) в нити 3.
Поток, помеченный как (#), вызвал последнюю точку останова (которая в нашем случае была наша начальное присоединение отладчика).
Основная информация для потока, предоставленная командой is, показана на рисунке 5-4.
Большинство чисел, сообщаемых WinDbg, являются шестнадцатеричными по умолчанию.
Чтобы преобразовать значение в десятичное, можно использовать «?».
• Введите следующее, чтобы получить десятичный идентификатор процесса (затем вы можете сравнить его с указанным PID в диспетчере задач):
0:000> ? 874c
Evaluate expression: 34636 = 00000000`0000874c
• Вы можете выражать десятичные числа с префиксом 0n, чтобы вы могли получить и обратный результат:
0:000> ? 0n34636
Evaluate expression: 34636 = 00000000`0000874c
• Вы можете проверить TEB потока с помощью команды «!teb».
Некоторые данные, показанные командой! Teb, относительно известны:
•StackBase и StackLimit - основа стека пользовательского режима и ограничение для потока.
•ClientId - идентификаторы процессов и потоков.
•LastErrorValue - последний код ошибки Win32 (GetLastError).
•TlsStorage - Массив локального хранилища потока (TLS) для этого потока (полное объяснение TLS выходит за рамки этой книги).
• Адрес PEB - адрес блока среды процесса (PEB), который можно просмотреть с помощью «!Peb».
• Команда !Teb (и аналогичные команды) показывает части реальной структуры, в данном случае _TEB. Вы всегда можете посмотреть на реальную структуру, используя «dt» (тип отображения):
Обратите внимание, что WinDbg не учитывает регистр символов. Также обратите внимание на название структуры, начинаются с подчеркивания; именно так, все структуры определены в Windows (пользовательский режим и режим ядра).
Использование имени typedef (без подчеркивания) может работать или не работать, поэтому всегда рекомендуется использовать подчеркивание.
Как узнать, какой модуль определяет структуру, которую вы хотите просмотреть? Если структура задокументирована, модуль будет указан в документации по структуре.
Вы также можете попробовать указать структуру без имени модуля, заставив отладчик искать.
Как правило, вы «знаете», где структура определяется с опытом, а иногда по контексту.
• Если вы прикрепите адрес к предыдущей команде, вы можете получить фактические значения данных:
Каждый элемент отображается со смещением от начала структуры, его именем и значением.
Простые значения отображаются напрямую, а структурные значения (такие как NtTib выше) обычно отображаются с гиперссылкой. Нажав на эту гиперссылку, вы получите подробную информацию о структуре.
• Нажмите на член NtTib выше, чтобы показать детали этого члена данных:
Отладчик использует более новую команду dx для просмотра данных.
Если вы не видите гиперссылки, возможно, вы используете очень старую WinDbg, где язык разметки отладчика (DML) не включен по умолчанию. Вы можете включить его с помощью команды .prefer_dml 1.
Теперь давайте обратим наше внимание на контрольные точки. Давайте установим точку останова, когда файл будет открыт блокнотом.
• Введите следующую команду, чтобы установить точку останова в функции API CreateFile:
0: 000> bp kernel32! Createfilew
Обратите внимание, что имя функции на самом деле CreateFileW, так как нет функции с именем CreateFile.
• Вы можете перечислить существующие точки останова с помощью команды bl:
Вы можете увидеть индекс точки останова (0), независимо от того, включен он или нет (e = включено, d = отключено) и Вы получаете гиперссылки, чтобы отключить (команда bd) и удалить (команда bc) точку останова.
Теперь давайте продолжим выполнение блокнота, пока не достигнет точки останова:
• Введите команду g или нажмите кнопку «Перейти» на панели инструментов, или нажмите F5:
Вы увидите, что отладчик показывает Busy в командной строке, а область команд показывает, что Debuggee, это означает, что вы не можете вводить команды до следующего перерыва.
• Блокнот теперь должен быть активным. Перейдите в меню «Файл» и выберите «Открыть».
Отладчик должен остановить выполнение блокнота:
• Мы достигли точки останова! Обратите внимание на поток, в котором это произошло. Давайте посмотрим, что вызов выглядит как стек (может потребоваться некоторое время, чтобы показать, нужно ли отладчику загружать символы:
Что мы можем сделать на этом этапе? Вы можете задаться вопросом, какой файл открывается.
Мы можем получить эту информацию, основанной на соглашении о вызовах функции CreateFileW.
Так как это 64-битный процесс (а процессор Intel/AMD), соглашение о вызовах гласит, что первое целое число/указатель аргументы передаются в регистрах RCX, RDX, R8 и R9.
Поскольку имя файла в CreateFileW, это первый аргумент, соответствующий регистр — RCX.
• Отобразите значение регистра RCX с помощью команды r (вы получите другое значение):
0:002> r rcx
rcx=00000001226fabf8
• Мы можем просматривать память, указанную RCX, с помощью различных команд d (отображения).
Команда db показывает память в байтах и символы ASCII справа.
Теперь понятно каково имя файла, но поскольку строка является Unicode, это не очень удобно для просмотра.
• Используйте команду du для более удобного просмотра строки Unicode:
0:002> du 00000001226fabf8
00000001`226fabf8 "C:\Windows\Microsoft.NET\Framewo"
00000001`226fac38 "rk64\\v2.0.50727\clr.dll"
• Вы можете использовать значение регистра напрямую, добавив к его имени префикс @:
0:002> du @rcx
00000001`226fabf8
00000001`226fac38
"C:\Windows\Microsoft.NET\Framewo"
"rk64\\v2.0.50727\clr.dll"
Теперь давайте установим другую точку останова в нативном API, который вызывается CreateFileW — NtCreateFile:
Обратите внимание, что нативный API никогда не использует W или A - он всегда работает со строками Unicode.
• Продолжите выполнение с помощью команды g.
Breakpoint 1 hit
ntdll!NtCreateFile:
00007ffc`20480120 4c8bd1 mov r10,rcx
• Check the call stack again:
•Перечислите следующие 8 инструкций, которые должны быть выполнены с помощью команды u (unassemble):
Обратите внимание, что значение 0x55 копируется в регистр EAX. Это сервисный номер системы для NtCreateFile, как описано в главе 1. Показанная инструкция syscall является той, которая вызывает переход к ядру, а затем выполнить саму системную службу NtCreateFile.
• Вы можете перейти к следующей инструкции с помощью команды p (шаг - нажмите F10 в качестве альтернативы). Вы может войти в функцию (в случае сборки это инструкция вызова) с помощью команды t (трассировка - нажмите F11 как альтернативу):
• Вход в системный вызов невозможен, так как мы находимся в режиме пользователя.
0:002> p
ntdll!NtCreateFile+0x14:
00007ffc`20480134 c3 ret
• Возвращаемое значение функций в соглашении о вызовах x64 сохраняется в EAX или RAX.
Для системы, это NTSTATUS, поэтому EAX содержит возвращенный статус:
0:002> r eax
eax=c0000034
• У нас есть код ошибки. Мы можем получить подробности с помощью команды! Error:
0:002> !error @eax
Error code: (NTSTATUS) 0xc0000034 (3221225524) - Object Name not found.
• Отключите все точки останова и дайте Notepad продолжить работу в обычном режиме:
0:002> bd *
0:002> g
Поскольку в настоящее время у нас нет точек останова, мы можем принудительно выполнить разрыв, нажав кнопку «Разрыв» на панель инструментов, или нажав Ctrl + Break на клавиатуре:
• Обратите внимание на номер потока в приглашении. Показать все текущие потоки:
Много потоков, верно? Они были фактически созданы/вызваны общим открытым диалогом.
• Продолжайте исследовать отладчик так, как хотите!
• Если вы закроете Блокнот, вы достигнете точки останова при завершении процесса:
• Вы можете использовать команду q для выхода из отладчика. Если процесс еще жив, он будет прерван.
Альтернативой является использование команды .detach для отключения от цели без ее уничтожения.
3) Отладка ядра
Отладка в пользовательском режиме включает в себя отладчик, присоединяющийся к процессу, устанавливающий точки останова и т.д. Отладка режима ядра с другой стороны, включает в себя управление всей машиной с помощью отладчика.
Это означает, что если точка останова установлена, то вся машина остановится.
Очевидно, что это не может быть достигнуто с помощью одной машины. В полном объеме отладка ядра, задействованы две машины: хост (где работает отладчик) и цель (То-что отлаживается).
Однако целью может быть виртуальная машина, размещенная на той же машине (хосте), где отладчик выполняется.
Рисунок 5-5 показывает хост и цель, подключенные через некоторую среду соединения.
Прежде чем мы перейдем к полной отладке ядра, мы рассмотрим его более простой родственник — локальная отладка адра.
Локальная отладка ядра
Локальная отладка ядра (LKD) позволяет просматривать системную память и другую системную информацию на местной машине.
Основное различие между локальной и полной отладкой ядра заключается в том, что с LKD нет никакого способа установить точки останова, что означает, что вы всегда смотрите на текущее состояние системы.
Это также означает, что все меняется, даже когда выполняются команды, поэтому некоторая информация может быть ненадежной. При полной отладке ядра команды можно вводить только во время, когда целевая система находится в точке останова, поэтому состояние системы не изменяется.
Чтобы настроить LKD, введите в командной строке с повышенными правами следующее и перезапустите систему:
bcdedit / debug on
После перезапуска системы запустите WinDbg с повышенными привилегиями. Выберите меню File/Attach To Kernel (WinDbg preview) или File/Kernel Debug (классический WinDbg). Выберите вкладку Local и нажмите ОК. Вы должны увидеть вывод, похожий на следующий:
Отладка локального ядра защищена безопасной загрузкой в Windows 10, Server 2016 и более поздних версиях.
Чтобы активировать LKD, вам необходимо отключить безопасную загрузку в настройках BIOS машины. Если по любой причине, это невозможно, есть альтернатива, использующая Sysinternals LiveKd инструмент.
Скопируйте LiveKd.exe в основной каталог Debugging Tools for Windows.
После запустите WinDbg с использованием LiveKd с помощью следующей команды: livekd -w.
Обратите внимание, что приглашение отображает lkd. Это означает, что локальная отладка ядра активна.
4)Мануал по отладке локального ядра
Если вы знакомы с командами отладки ядра, вы можете смело пропустить этот раздел.
• Вы можете отобразить основную информацию для всех процессов, запущенных в системе вместе с процессом 0 0 команда:
Для каждого процесса отображается следующая информация:
• Адрес, присоединенный к тексту PROCESS, является адресом процесса (в пространстве ядра, конечно).
• SessionId - сессия, в котором выполняется процесс.
• Cid - (идентификатор клиента) уникальный идентификатор процесса.
• Peb - адрес блока среды процесса (PEB). Этот адрес находится в пространстве пользователя, естественно.
• ParentCid - (идентификатор родительского процесса) идентификатор родительского процесса. Обратите внимание, что возможно родительский процесс больше не существует, и этот идентификатор можно использовать повторно.
• DirBase - физический адрес (без младших 12 бит) каталога главной страницы для этого процесса, используется в качестве основы для виртуальной трансляции адресов.
На x64 это известно как Page Map Level 4, а на x86 это таблица указателей страниц (PDPT).
• ObjectTable - указатель на приватную таблицу дескрипторов процесса.
• HandleCount - количество дескрипторов в этом процессе.
• Image - имя исполняемого файла или имя специального процесса для тех, кто не связан с исполняемым файлом (примеры: защищенная система, система сжатие памяти).
Команда !Process принимает как минимум два аргумента. Первый указывает на интересующий процесс используя его адрес EPROCESS, где ноль означает «все или любой процесс».
Второй аргумент - это уровень необходимых деталей, где ноль означает наименьшее количество деталей (битовая маска).
Третий аргумент может быть добавлен для поиска конкретного исполняемого файла.
• Перечислить все процессы, на которых запущен csrss.exe:
• Перечислить больше информации для определенного процесса, указав его адрес и более высокий уровень детализации:
Как видно из вышеприведенного вывода, отображается дополнительная информация о процессе.
Некоторые из этой информации является гиперссылкой, что облегчает дальнейшее изучение.
Job, частью которой является этот процесс является гиперссылкой.
• Нажмите на гиперссылку с адресом работы Job:
Job - это объект, который содержит один или несколько процессов, для которых могут применяться различные ограничения.
Подробное обсуждение Job выходит за рамки этой книги. Дополнительную информацию можно найти в книгах по внутренним компонентам Windows.
• Как обычно, такая команда, как! Job, скрывает некоторую информацию, доступную в реальной структуре данных.
В данном случае это EJOB. Используйте команду dt nt! _Ejob с адресом задания, чтобы увидеть все детали.
• Также можно просмотреть PEB процесса, щелкнув его гиперссылку. Это похоже на !Peb команду, которая используется в пользовательском режиме, но здесь должен обязательно быть задан правильный контекст процесса.
Нажмите на гиперссылку Peb. Вы должны увидеть что-то вроде этого:
Правильный контекст процесса задается мета-командой .process, а затем отображается PEB.
Это общая техника, которую вы должны использовать, чтобы показать информацию, которая находится в пространстве пользователя.
• Повторите команду! Process еще раз, но на этот раз без уровня детализации.
Будет показано больше информации для процесса:
Команда перечисляет все потоки в процессе. Каждый поток представлен своим адресом ETHREAD прилагается к тексту “THREAD”.
Стек вызовов также указан в списке - префикс модуля «nt» представляет ядро - нет необходимости использовать «настоящее» имя модуля ядра.
Одна из причин использования «nt» вместо явного указания имени модуля ядра состоит в том, что они различаются между 64 и 32-разрядными системами (ntoskrnl.exe на 64-разрядных и как минимум два варианта на 32 бита).
Символы пользовательского режима не загружаются по умолчанию, поэтому стеки потоков, которые охватывают пользовательский режим, показывают просто числовые адреса.
Вы можете явно загружать пользовательские символы с помощью .reload / user:
Информацию о потоке можно просмотреть отдельно с помощью команды! Thread и адреса потока.
Проверьте документацию отладчика, что-бы увидеть описания различных частей информации отображаемой этой командой.
Другие обычно полезные/интересные команды в режиме отладки ядра включают в себя:
•! Pcr - отображать область управления процессом (PCR) для процессора, указанного как дополнительный index(процессор 0 отображается по умолчанию, если индекс не указан).
•! Vm - отображать статистику памяти для системы и процессов.
•! Running - отображает информацию о потоках, запущенных на всех процессорах системы.
Мы рассмотрим более конкретные команды, полезные для отладки драйверов, в следующих главах.
5)Полная отладка ядра
Полная отладка ядра требует настройки на хосте и таргете. В этом разделе мы увидим, как настроить виртуальную машину как таргет для отладки ядра. Это рекомендуемое и наиболее удобная настройка для работы драйвера ядра (когда не разрабатываются драйверы устройств для оборудования).
Хорошо выполните шаги по настройке виртуальной машины Hyper-V (VM). Если вы используете другие технологии виртуализации (например, VMWare или VirtualBox), пожалуйста, обратитесь к документации этих программ.
Целевой и хост-компьютер должны обмениваться данными с использованием некоторого носителя подключения.
Есть несколько вариантов.
Лучший вариант - использовать сеть.
К сожалению, это требует хоста и таргета для запуска Windows 8 как минимум.
Поскольку Windows 7 по-прежнему является жизнеспособным таргетом, мы будем использовать другой вариант - COM (последовательный) порт.
Конечно, большинство машин больше не имеют последовательных портов, но в любом случае мы подключаемся к виртуальной машине, поэтому никаких реальных кабелей не требуется.
Все платформы виртуализации позволяют перенаправление виртуального последовательного порта на именованный канал на хосте; это конфигурация, которую мы будем использовать.
Конфигурирование таргета
Целевая виртуальная машина должна быть настроена для отладки ядра, аналогично локальной отладке ядра, но с добавленным носителем подключения установленой на виртуальный последовательный порт на этой машине.
Один из способов сделать это - использовать bcdedit в окне команд с повышенными правами:
bcdedit / debug on
bcdedit /dbgsettings serial debugport:1
baudrate:115200
Измените номер порта отладки в соответствии с действительным виртуальным серийным номером (обычно 1).
Виртуальная машина должна быть перезапущена, чтобы эти конфигурации вступили в силу. Прежде чем сделать это, мы можем сопоставить последовательный порт для именованного канала. Вот процедура для виртуальных машин Hyper-V:
• Если виртуальная машина Hyper-V относится к поколению 1 (старше), в настройках виртуальной машины имеется простой пользовательский интерфейс для конфигурации. используйте опцию Add Hardware, чтобы добавить последовательный порт, если он не определен.
Затем настройте последовательный порт для сопоставления с именованным портом по вашему выбору. Рисунок 5-6 показывает это.
• Для виртуальных машин поколения 2 в настоящее время пользовательский интерфейс отсутствует. Чтобы настроить это, убедитесь, что виртуальная машина выключена (хотя не обязательно в самых последних версиях Windows 10) и откройте расширенный PowerShell окно.
• Введите следующее, чтобы установить последовательный порт, сопоставленный с именованным каналом:
Set-VMComPort myvmname -Number 1 -Path \\.\pipe\debug
Измените имя виртуальной машины соответствующим образом и номер COM-порта, как было установлено внутри виртуальной машины ранее с помощью Bcdedit. Убедитесь, что путь pipe уникален.
Вы можете проверить, соответствуют ли настройки ожидаемым с помощью Get-VMComPort:
Get-VMComPort myvmname
VMName
------
myvmname
myvmname
Name Path
---- ----
COM 1 \\.\pipe\debug
COM 2
Вы можете загрузить виртуальную машину - цель готова.
Конфигурирование хоста
Отладчик ядра должен быть настроен для подключения к виртуальной машине через тот же последовательный порт, сопоставленный с одноименным pipe выставленным на хосте.
• Запустите отладчик ядра и выберите File / Attach To Kernel. Перейдите на вкладку COM. Рисунок 5-7 показывает, как выглядят эти настройки.
• Нажмите на Отладчик должен прикрепиться к цели. Если это не так, нажмите кнопку панели инструментов Break.
Вот типичный вывод:
Обратите внимание, что подсказка имеет индекс и слово kd. Индекс - текущий процессор, который вызвал изменения.
В этот момент целевая виртуальная машина полностью заморожена. Теперь вы можете отлаживать нормально.
6)Мануалл отладки драйверов
Как только хост и цель связаны, отладка может начаться. Мы будем использовать драйвер PriorityBooster, который мы разработанный в главе 4, чтобы продемонстрировать полную отладку ядра.
• Установите (но не загружайте) драйвер на цель, как это было сделано в главе 4. Убедитесь, что вы копируете файл PDB драйвера вместе с самим файлом SYS драйвера. Это упрощает получение правильных символов для драйвера.
• Давайте установим точку останова в DriverEntry. Мы не можем загрузить драйвер, потому что это может привести к DriverEntry и мы упустим шанс установить точку останова там.
Так как драйвер еще не загружен, мы можем использовать команду bu (неразрешенная точка останова), чтобы установить будущую точку останова.
Войдите цель, если она в данный момент запущена, и введите следующую команду:
0: kd> bu prioritybooster!driverentry
0: kd> bl
0 e Disable Clear u 0001 (0001) (prioritybooster!driverentry)
Точка останова еще не установлена, так как наш модуль еще не загружен.
• Выполните команду g, чтобы позволить цели продолжить, и загрузите драйвер с sc start booster (при условии, что драйвер называется бустер). Если все идет хорошо, точка останова должна установится, и исходный файл должен загрузиться автоматически, показывая следующий вывод в командном окне:
0: kd> g
Breakpoint 0 hit
PriorityBooster!DriverEntry:
fffff801`358211d0 4889542410 mov qword ptr [rsp+10h],rdx
Рисунок 5-8 показывает снимок экрана с исходным окном WinDbg Preview.
На этом этапе вы можете перешагнуть строки исходного текста, посмотреть переменные в окне Locals и даже добавить выражения в окне просмотра. Вы также можете изменить значения, используя окно Locals, как это делается с другими отладчиками.
Окно команд по-прежнему доступно, как всегда, но некоторые операции с пользовательским интерфейсом становятся проще.
Например, настройки точек останова можно выполнить с помощью обычной команды bp, но вы можете просто открыть исходный файл (если он еще не открыт), перейдите к строке, где вы хотите установить точку останова, и нажмите F9 или нажмите соответствующую кнопку на панели инструментов.
В любом случае команда bp будет выполнена в командном окне.
Окно Breakpoints может служить кратким обзором текущей точки останова.
Введите команду k, чтобы увидеть, как вызывается DriverEntry:
Если точки останова, кажется, не в состоянии установить, это может быть проблема символов. Выполните команду .reload и посмотрите, решены ли проблемы.
Установка точек останова в пользовательском пространстве также возможна, но сначала выполните .reload / user.
Может случиться так, что точка останова должна срабатывать только тогда, когда конкретный процесс выполняет код.
Это можно сделать, добавив ключ / p к точке останова. В следующем примере точка останова устанавливается, только если это процесс explorer.exe:
Давайте установим точку останова в месте, показанным на скриншете ниже:
• Запустите тестовое приложение с некоторым идентификатором потока и приоритетом:
Booster 2000 30
Точка останова должна выполнится. Вы можете продолжить отладку в обычном режиме, используя комбинацию исходного кода.
Резюме
В этой главе мы рассмотрели основы отладки с помощью WinDbg. Это важный навык для разработки, так как программное обеспечение всех видов, включая драйверы ядра, могут содержать ошибки.
В следующей главе мы углубимся в некоторые механизмы ядра, с которыми нам необходимо ознакомиться, так как они часто возникают при разработке и отладке драйверов.
Отладка драйверов, в отличие от пользовательского режима, более сложный процесс.
Отладка драйвера по сути отладка всей системы, не просто конкретного процесса или процессов. Это требует другого мышления.
В этой главе будет обсуждаться отладка ядра с использованием отладчика WinDbg.
В этой главе:
• Инструменты отладки для Windows.
• Введение в WinDbg.
• Отладка ядра.
• Полная отладка ядра.
• Мануал по отладке драйверов ядра.
1) Инструменты отладки для Windows
Пакет средств отладки для Windows содержит набор отладчиков, инструментов и документации, сосредоточим внимание на отладчиках в пакете.
Этот пакет может быть установлен как часть Windows SDK или WDK, но никакой реальной «установки» нет.
Установка просто копирует файлы, но не трогает реестр, то есть пакет зависит только от собственных модулей и библиотеки DLL Windows.
Это позволяет легко копировать весь каталог в любой другой каталог, включая на съемный носитель.
Пакет содержит четыре отладчика: Cdb.exe, Ntsd.Exe, Kd.exe и WinDbg.exe.
Вот краткое изложение основных функциональных возможностей каждого отладчика:
• Cdb и Ntsd - пользовательский режим, консольные отладчики. Это означает, что они могут быть прикреплены к процессу, как и любой другой отладчик пользовательского режима.
Оба имеют консольный интерфейс - введите команду, получите ответ и повторить.
Единственная разница между ними заключается в том, что если запустить консольные окна, Cdb использует ту же консоль, тогда как Ntsd открывает новое консольное окно.
В остальном они идентичны.
• Kd - отладчик ядра с консольным пользовательским интерфейсом. Может подключаться к локальному ядру (Локальная отладка ядра, описанна в следующем разделе) или на другую машину.
• WinDbg - единственный отладчик с графическим интерфейсом пользователя. Может работать в режиме пользователя или в режиме отладки ядра, в зависимости от выбора, выполненного из его меню или аргументов командной строки, с которыми он был запущен.
Недавняя альтернатива классического WinDbg - Windbg Preview, доступный в магазине Microsoft.
Это римейк классического отладчика с гораздо лучшим пользовательским интерфейсом и возможностями.
Может быть установлен на Windows 10 версии 1607 или более поздней.
С функциональной точки зрения, это похоже на классический WinDbg. Но он проще в использовании из-за современного, удобного пользовательского интерфейса.
Все команды, которые мы увидим позже в этой главе должны работать одинаково с любым отладчиком.
Хотя эти отладчики могут отличаться друг от друга, на самом деле отладчики в режиме пользователя по сути те же, что и отладчики ядра.
Все они основаны на одном отладчике.
Движок реализован в виде DLL (DbgEng.Dll).
Различные отладчики могут использовать расширения DLL, которые обеспечивают большую часть функций отладчиков.
Механизм отладчика достаточно документирован в документации по средствам отладки для Windows, и так что можно написать новые отладчики, которые используют тот же движок.
Другие инструменты, которые являются частью пакета, включают (частичный список):
• Gflags.exe - инструмент Global Flags, который позволяет устанавливать некоторые флаги ядра и флаги образов.
• ADPlus.exe - создать файл дампа сбоя или зависания процесса.
• Kill.exe - простой инструмент для завершения процесса (ов) на основе идентификатора процесса, имени или шаблона.
• Dumpchk.exe - инструмент для общей проверки файлов дампа.
• TList.exe - список запущенных процессов в системе с различными параметрами.
• Umdh.exe - анализирует распределение кучи в процессах пользовательского режима.
• UsbView.exe - отображает иерархическое представление USB-устройств и концентраторов.
2) Введение в WinDbg
В этом разделе описываются основы WinDbg, но имейте в виду, что все по сути то же самое для консольных отладчиков, за исключением окон графического интерфейса.
WinDbg построен вокруг команд.
Пользователь вводит команду, а отладчик отвечает текстом, описывающим результаты команды.
С GUI некоторые из этих результатов изображены в выделенных окнах, таких как локальные пользователи, стек, потоки и т. д.
WinDbg поддерживает три типа команд:
• Внутренние команды - эти команды встроены в отладчик и работают на отлаживаемой цели.
• Метакоманды - эти команды начинаются с точки (.) И работают при отладке самого процессв, а не непосредственно на отлаживаемой цели.
• Bang (расширение) команды - эти команды начинаются с восклицательного знака (!). Обеспечивают большую часть возможности отладчика.
По умолчанию отладчик загружает набор предопределенных расширений DLL, но может и больше загружаться из каталога отладчика или из других источников.
Написание расширенных DLL-файлов возможно и полностью задокументировано в документации отладчика. На самом деле, многие такие DLL были созданы и могут быть загружены из соответствующего источника. Эти библиотеки предоставляют новые команды, которые улучшают отладку, часто нацеленные на конкретные сценарии.
3)Уроки. Основы отладки в пользовательском режиме
Если у вас есть опыт работы с WinDbg, вы можете смело пропустить этот раздел.
Этот учебник направлен на получение базового понимания WinDbg и как использовать его для пользовательского режима отладки.
Отладка ядра описана в следующем разделе.
Как правило, есть два способа инициировать отладку в пользовательском режиме - запустить исполняемый файл и прикрепить к нему, или присоединить к уже существующему процессу.
Мы будем использовать последний подход в этом уроке, но за исключением этого первого шага, все остальные операции идентичны.
• Запустите Блокнот.
• Запустите WinDbg (либо Preview, либо классический. На следующих снимках экрана).
• Выберите File / Attach To Process и найдите процесс Notepad в списке (см. Рисунок 5-1). затем нажмите «Прикрепить». Вы должны увидеть результат, похожий на рисунок 5-2.
Окно команд является основным интересующим окном - оно всегда должно быть открыто. Это окно показывает различные ответы на команд. Как правило, большая часть сеанса отладки проходит на взаимодействие с этим окном.
Процесс теперь приостановлен - мы находимся в точке останова, вызванной отладчиком.
• Первая команда, которую мы будем использовать, это shows, которая показывает информацию обо всех потоках в отлаженном процессе:
0:003> ~
0 Id:874c.18068 Suspend: 1 Teb: 00000001`2229d000 Unfrozen
1 Id:874c.46ac Suspend: 1 Teb: 00000001`222a5000 Unfrozen
2 Id:874c.152cc Suspend: 1 Teb: 00000001`222a7000 Unfrozen
3 Id:874c.bb08 Suspend: 1 Teb: 00000001`222ab000 Unfrozen
Точное количество потоков, которые вы увидите, может отличаться от показанного здесь.
Одна вещь, которая очень важна - это наличие правильных символов. Microsoft предоставляет общественности сервер символов, который может использоваться Microsoft для поиска символов для большинства модулей. Это важно в любой низкоуровневой отладке.
• Для быстрой установки символов введите команду .symfix.
• Лучший подход - настроить символы один раз и сделать их доступными для дальнейшей отладки.
Для этого добавьте системную переменную среды с именем _NT_SYMBOL_PATH и установите её:
Код:
SRV*c:\Symbols*http://msdl.microsoft.com/download/symbols
Средняя часть (между звездочками) - это локальный путь для кэширования символов на вашем локальном компьютере.
Вы можно выбрать любой путь, который вам нравится. Как только эта переменная окружения установлена, следующие вызовы отладчика автоматически найдет символы и при необходимости загрузит их с сервера символов Microsoft.
• Чтобы убедиться, что у вас есть правильные символы, введите команду lm (загруженные модули):
Список модулей показывает все модули (DLL и EXE), загруженные в отлаженный процесс на этом время.
Вы можете увидеть начальные и конечные виртуальные адреса, в которых загружен каждый модуль.
Наименование модуля позволяет увидеть статус символа этого модуля (в скобках).
Возможные значения включают в себя:
• deferred - символы для этого модуля никогда не были нужны в этом сеансе отладки и так что не загружаются в это время. Они будут загружены при необходимости.
• символы pdb - это означает, что были загружены правильные публичные символы. Локальный путь PDB.
• экспортировать символы - для этой DLL доступны только экспортированные символы. Это обычно означает, что там нет символов для этого модуля или они не были найдены.
• нет символов - пытались найти символы этого модуля, но ничего не было найдено, даже не экспортируемые символы (такие модули не имеют экспортированных символов, как в случае исполняемых файлов и файлов драйверов).
Вы можете принудительно загрузить символы модуля, используя команду .reload /f modulename.dll.
Это обеспечит окончательное подтверждение наличия символов для этого модуля.
Пути к символам также можно настроить в диалоговом окне настроек отладчика.
Откройте меню «Файл / Настройки» и найдите «Настройки отладки». Затем вы можете добавить больше путей для поиска символа. Это полезно при отладке собственного кода, искать можно в ваших каталогах, где могут быть найдены соответствующие файлы PDB (см. рисунок 5-3).
Вернитесь к списку потоков - обратите внимание, что один из потоков имеет точку перед своими данными. Это текущий поток. Это означает, что любая выданная команда, которая включает поток, где поток не указан, будет работать на этом потоке.
«Текущий поток» также показан в приглашении (0:003>) - число справа от двоеточия является текущим индексом потока (3 в этом примере).
• Введите команду k, которая показывает трассировку стека текущего потока:
Вы можете увидеть список вызовов этого потока (конечно, только в режиме пользователя).
Вершиной стека является функция DbgBreakPoint, расположенная в модуле ntdll.dll.
Общий формат адресов с символами имеет «modulename!functionname+offset» (Пример:ntdll!DbgUiRemoteBreakin+0x4b). Смещение не является обязательным и может быть нулевым, если это именно начало этой функции.
Также обратите внимание, что имя модуля не имеет расширения.
В приведенном выше выводе DbgBreakpoint был вызван DbgUiRemoteBreakIn, который был вызван BaseThreadInitThunk и так далее.
Этот поток, кстати, был введен отладчиком для принудительного проникновения в цель.
• Чтобы переключиться на другой поток, используйте следующую команду: ∼ ns, где n - индекс потока.
Давайте переключимся на поток 0 и затем отобразим его стек вызовов:
Это основной (первый) поток Блокнота. В верхней части стека показан поток, ожидающий сообщений пользовательского интерфейса.
• Альтернативный способ показать стек вызовов другого потока без переключения на него - это использовать тильду и номер потока перед фактической командой.
Следующий вывод для потока 1:
Обратите внимание, что точка переместилась в нить 0 (текущая нить), показывая хеш-знак (#) в нити 3.
Поток, помеченный как (#), вызвал последнюю точку останова (которая в нашем случае была наша начальное присоединение отладчика).
Основная информация для потока, предоставленная командой is, показана на рисунке 5-4.
Большинство чисел, сообщаемых WinDbg, являются шестнадцатеричными по умолчанию.
Чтобы преобразовать значение в десятичное, можно использовать «?».
• Введите следующее, чтобы получить десятичный идентификатор процесса (затем вы можете сравнить его с указанным PID в диспетчере задач):
0:000> ? 874c
Evaluate expression: 34636 = 00000000`0000874c
• Вы можете выражать десятичные числа с префиксом 0n, чтобы вы могли получить и обратный результат:
0:000> ? 0n34636
Evaluate expression: 34636 = 00000000`0000874c
• Вы можете проверить TEB потока с помощью команды «!teb».
Некоторые данные, показанные командой! Teb, относительно известны:
•StackBase и StackLimit - основа стека пользовательского режима и ограничение для потока.
•ClientId - идентификаторы процессов и потоков.
•LastErrorValue - последний код ошибки Win32 (GetLastError).
•TlsStorage - Массив локального хранилища потока (TLS) для этого потока (полное объяснение TLS выходит за рамки этой книги).
• Адрес PEB - адрес блока среды процесса (PEB), который можно просмотреть с помощью «!Peb».
• Команда !Teb (и аналогичные команды) показывает части реальной структуры, в данном случае _TEB. Вы всегда можете посмотреть на реальную структуру, используя «dt» (тип отображения):
Обратите внимание, что WinDbg не учитывает регистр символов. Также обратите внимание на название структуры, начинаются с подчеркивания; именно так, все структуры определены в Windows (пользовательский режим и режим ядра).
Использование имени typedef (без подчеркивания) может работать или не работать, поэтому всегда рекомендуется использовать подчеркивание.
Как узнать, какой модуль определяет структуру, которую вы хотите просмотреть? Если структура задокументирована, модуль будет указан в документации по структуре.
Вы также можете попробовать указать структуру без имени модуля, заставив отладчик искать.
Как правило, вы «знаете», где структура определяется с опытом, а иногда по контексту.
• Если вы прикрепите адрес к предыдущей команде, вы можете получить фактические значения данных:
Каждый элемент отображается со смещением от начала структуры, его именем и значением.
Простые значения отображаются напрямую, а структурные значения (такие как NtTib выше) обычно отображаются с гиперссылкой. Нажав на эту гиперссылку, вы получите подробную информацию о структуре.
• Нажмите на член NtTib выше, чтобы показать детали этого члена данных:
Отладчик использует более новую команду dx для просмотра данных.
Если вы не видите гиперссылки, возможно, вы используете очень старую WinDbg, где язык разметки отладчика (DML) не включен по умолчанию. Вы можете включить его с помощью команды .prefer_dml 1.
Теперь давайте обратим наше внимание на контрольные точки. Давайте установим точку останова, когда файл будет открыт блокнотом.
• Введите следующую команду, чтобы установить точку останова в функции API CreateFile:
0: 000> bp kernel32! Createfilew
Обратите внимание, что имя функции на самом деле CreateFileW, так как нет функции с именем CreateFile.
• Вы можете перечислить существующие точки останова с помощью команды bl:
Вы можете увидеть индекс точки останова (0), независимо от того, включен он или нет (e = включено, d = отключено) и Вы получаете гиперссылки, чтобы отключить (команда bd) и удалить (команда bc) точку останова.
Теперь давайте продолжим выполнение блокнота, пока не достигнет точки останова:
• Введите команду g или нажмите кнопку «Перейти» на панели инструментов, или нажмите F5:
Вы увидите, что отладчик показывает Busy в командной строке, а область команд показывает, что Debuggee, это означает, что вы не можете вводить команды до следующего перерыва.
• Блокнот теперь должен быть активным. Перейдите в меню «Файл» и выберите «Открыть».
Отладчик должен остановить выполнение блокнота:
• Мы достигли точки останова! Обратите внимание на поток, в котором это произошло. Давайте посмотрим, что вызов выглядит как стек (может потребоваться некоторое время, чтобы показать, нужно ли отладчику загружать символы:
Что мы можем сделать на этом этапе? Вы можете задаться вопросом, какой файл открывается.
Мы можем получить эту информацию, основанной на соглашении о вызовах функции CreateFileW.
Так как это 64-битный процесс (а процессор Intel/AMD), соглашение о вызовах гласит, что первое целое число/указатель аргументы передаются в регистрах RCX, RDX, R8 и R9.
Поскольку имя файла в CreateFileW, это первый аргумент, соответствующий регистр — RCX.
• Отобразите значение регистра RCX с помощью команды r (вы получите другое значение):
0:002> r rcx
rcx=00000001226fabf8
• Мы можем просматривать память, указанную RCX, с помощью различных команд d (отображения).
Команда db показывает память в байтах и символы ASCII справа.
Теперь понятно каково имя файла, но поскольку строка является Unicode, это не очень удобно для просмотра.
• Используйте команду du для более удобного просмотра строки Unicode:
0:002> du 00000001226fabf8
00000001`226fabf8 "C:\Windows\Microsoft.NET\Framewo"
00000001`226fac38 "rk64\\v2.0.50727\clr.dll"
• Вы можете использовать значение регистра напрямую, добавив к его имени префикс @:
0:002> du @rcx
00000001`226fabf8
00000001`226fac38
"C:\Windows\Microsoft.NET\Framewo"
"rk64\\v2.0.50727\clr.dll"
Теперь давайте установим другую точку останова в нативном API, который вызывается CreateFileW — NtCreateFile:
Обратите внимание, что нативный API никогда не использует W или A - он всегда работает со строками Unicode.
• Продолжите выполнение с помощью команды g.
Breakpoint 1 hit
ntdll!NtCreateFile:
00007ffc`20480120 4c8bd1 mov r10,rcx
• Check the call stack again:
•Перечислите следующие 8 инструкций, которые должны быть выполнены с помощью команды u (unassemble):
Обратите внимание, что значение 0x55 копируется в регистр EAX. Это сервисный номер системы для NtCreateFile, как описано в главе 1. Показанная инструкция syscall является той, которая вызывает переход к ядру, а затем выполнить саму системную службу NtCreateFile.
• Вы можете перейти к следующей инструкции с помощью команды p (шаг - нажмите F10 в качестве альтернативы). Вы может войти в функцию (в случае сборки это инструкция вызова) с помощью команды t (трассировка - нажмите F11 как альтернативу):
• Вход в системный вызов невозможен, так как мы находимся в режиме пользователя.
0:002> p
ntdll!NtCreateFile+0x14:
00007ffc`20480134 c3 ret
• Возвращаемое значение функций в соглашении о вызовах x64 сохраняется в EAX или RAX.
Для системы, это NTSTATUS, поэтому EAX содержит возвращенный статус:
0:002> r eax
eax=c0000034
• У нас есть код ошибки. Мы можем получить подробности с помощью команды! Error:
0:002> !error @eax
Error code: (NTSTATUS) 0xc0000034 (3221225524) - Object Name not found.
• Отключите все точки останова и дайте Notepad продолжить работу в обычном режиме:
0:002> bd *
0:002> g
Поскольку в настоящее время у нас нет точек останова, мы можем принудительно выполнить разрыв, нажав кнопку «Разрыв» на панель инструментов, или нажав Ctrl + Break на клавиатуре:
• Обратите внимание на номер потока в приглашении. Показать все текущие потоки:
Много потоков, верно? Они были фактически созданы/вызваны общим открытым диалогом.
• Продолжайте исследовать отладчик так, как хотите!
• Если вы закроете Блокнот, вы достигнете точки останова при завершении процесса:
• Вы можете использовать команду q для выхода из отладчика. Если процесс еще жив, он будет прерван.
Альтернативой является использование команды .detach для отключения от цели без ее уничтожения.
3) Отладка ядра
Отладка в пользовательском режиме включает в себя отладчик, присоединяющийся к процессу, устанавливающий точки останова и т.д. Отладка режима ядра с другой стороны, включает в себя управление всей машиной с помощью отладчика.
Это означает, что если точка останова установлена, то вся машина остановится.
Очевидно, что это не может быть достигнуто с помощью одной машины. В полном объеме отладка ядра, задействованы две машины: хост (где работает отладчик) и цель (То-что отлаживается).
Однако целью может быть виртуальная машина, размещенная на той же машине (хосте), где отладчик выполняется.
Рисунок 5-5 показывает хост и цель, подключенные через некоторую среду соединения.
Прежде чем мы перейдем к полной отладке ядра, мы рассмотрим его более простой родственник — локальная отладка адра.
Локальная отладка ядра
Локальная отладка ядра (LKD) позволяет просматривать системную память и другую системную информацию на местной машине.
Основное различие между локальной и полной отладкой ядра заключается в том, что с LKD нет никакого способа установить точки останова, что означает, что вы всегда смотрите на текущее состояние системы.
Это также означает, что все меняется, даже когда выполняются команды, поэтому некоторая информация может быть ненадежной. При полной отладке ядра команды можно вводить только во время, когда целевая система находится в точке останова, поэтому состояние системы не изменяется.
Чтобы настроить LKD, введите в командной строке с повышенными правами следующее и перезапустите систему:
bcdedit / debug on
После перезапуска системы запустите WinDbg с повышенными привилегиями. Выберите меню File/Attach To Kernel (WinDbg preview) или File/Kernel Debug (классический WinDbg). Выберите вкладку Local и нажмите ОК. Вы должны увидеть вывод, похожий на следующий:
Отладка локального ядра защищена безопасной загрузкой в Windows 10, Server 2016 и более поздних версиях.
Чтобы активировать LKD, вам необходимо отключить безопасную загрузку в настройках BIOS машины. Если по любой причине, это невозможно, есть альтернатива, использующая Sysinternals LiveKd инструмент.
Скопируйте LiveKd.exe в основной каталог Debugging Tools for Windows.
После запустите WinDbg с использованием LiveKd с помощью следующей команды: livekd -w.
Обратите внимание, что приглашение отображает lkd. Это означает, что локальная отладка ядра активна.
4)Мануал по отладке локального ядра
Если вы знакомы с командами отладки ядра, вы можете смело пропустить этот раздел.
• Вы можете отобразить основную информацию для всех процессов, запущенных в системе вместе с процессом 0 0 команда:
Для каждого процесса отображается следующая информация:
• Адрес, присоединенный к тексту PROCESS, является адресом процесса (в пространстве ядра, конечно).
• SessionId - сессия, в котором выполняется процесс.
• Cid - (идентификатор клиента) уникальный идентификатор процесса.
• Peb - адрес блока среды процесса (PEB). Этот адрес находится в пространстве пользователя, естественно.
• ParentCid - (идентификатор родительского процесса) идентификатор родительского процесса. Обратите внимание, что возможно родительский процесс больше не существует, и этот идентификатор можно использовать повторно.
• DirBase - физический адрес (без младших 12 бит) каталога главной страницы для этого процесса, используется в качестве основы для виртуальной трансляции адресов.
На x64 это известно как Page Map Level 4, а на x86 это таблица указателей страниц (PDPT).
• ObjectTable - указатель на приватную таблицу дескрипторов процесса.
• HandleCount - количество дескрипторов в этом процессе.
• Image - имя исполняемого файла или имя специального процесса для тех, кто не связан с исполняемым файлом (примеры: защищенная система, система сжатие памяти).
Команда !Process принимает как минимум два аргумента. Первый указывает на интересующий процесс используя его адрес EPROCESS, где ноль означает «все или любой процесс».
Второй аргумент - это уровень необходимых деталей, где ноль означает наименьшее количество деталей (битовая маска).
Третий аргумент может быть добавлен для поиска конкретного исполняемого файла.
• Перечислить все процессы, на которых запущен csrss.exe:
• Перечислить больше информации для определенного процесса, указав его адрес и более высокий уровень детализации:
Как видно из вышеприведенного вывода, отображается дополнительная информация о процессе.
Некоторые из этой информации является гиперссылкой, что облегчает дальнейшее изучение.
Job, частью которой является этот процесс является гиперссылкой.
• Нажмите на гиперссылку с адресом работы Job:
Job - это объект, который содержит один или несколько процессов, для которых могут применяться различные ограничения.
Подробное обсуждение Job выходит за рамки этой книги. Дополнительную информацию можно найти в книгах по внутренним компонентам Windows.
• Как обычно, такая команда, как! Job, скрывает некоторую информацию, доступную в реальной структуре данных.
В данном случае это EJOB. Используйте команду dt nt! _Ejob с адресом задания, чтобы увидеть все детали.
• Также можно просмотреть PEB процесса, щелкнув его гиперссылку. Это похоже на !Peb команду, которая используется в пользовательском режиме, но здесь должен обязательно быть задан правильный контекст процесса.
Нажмите на гиперссылку Peb. Вы должны увидеть что-то вроде этого:
Правильный контекст процесса задается мета-командой .process, а затем отображается PEB.
Это общая техника, которую вы должны использовать, чтобы показать информацию, которая находится в пространстве пользователя.
• Повторите команду! Process еще раз, но на этот раз без уровня детализации.
Будет показано больше информации для процесса:
Команда перечисляет все потоки в процессе. Каждый поток представлен своим адресом ETHREAD прилагается к тексту “THREAD”.
Стек вызовов также указан в списке - префикс модуля «nt» представляет ядро - нет необходимости использовать «настоящее» имя модуля ядра.
Одна из причин использования «nt» вместо явного указания имени модуля ядра состоит в том, что они различаются между 64 и 32-разрядными системами (ntoskrnl.exe на 64-разрядных и как минимум два варианта на 32 бита).
Символы пользовательского режима не загружаются по умолчанию, поэтому стеки потоков, которые охватывают пользовательский режим, показывают просто числовые адреса.
Вы можете явно загружать пользовательские символы с помощью .reload / user:
Информацию о потоке можно просмотреть отдельно с помощью команды! Thread и адреса потока.
Проверьте документацию отладчика, что-бы увидеть описания различных частей информации отображаемой этой командой.
Другие обычно полезные/интересные команды в режиме отладки ядра включают в себя:
•! Pcr - отображать область управления процессом (PCR) для процессора, указанного как дополнительный index(процессор 0 отображается по умолчанию, если индекс не указан).
•! Vm - отображать статистику памяти для системы и процессов.
•! Running - отображает информацию о потоках, запущенных на всех процессорах системы.
Мы рассмотрим более конкретные команды, полезные для отладки драйверов, в следующих главах.
5)Полная отладка ядра
Полная отладка ядра требует настройки на хосте и таргете. В этом разделе мы увидим, как настроить виртуальную машину как таргет для отладки ядра. Это рекомендуемое и наиболее удобная настройка для работы драйвера ядра (когда не разрабатываются драйверы устройств для оборудования).
Хорошо выполните шаги по настройке виртуальной машины Hyper-V (VM). Если вы используете другие технологии виртуализации (например, VMWare или VirtualBox), пожалуйста, обратитесь к документации этих программ.
Целевой и хост-компьютер должны обмениваться данными с использованием некоторого носителя подключения.
Есть несколько вариантов.
Лучший вариант - использовать сеть.
К сожалению, это требует хоста и таргета для запуска Windows 8 как минимум.
Поскольку Windows 7 по-прежнему является жизнеспособным таргетом, мы будем использовать другой вариант - COM (последовательный) порт.
Конечно, большинство машин больше не имеют последовательных портов, но в любом случае мы подключаемся к виртуальной машине, поэтому никаких реальных кабелей не требуется.
Все платформы виртуализации позволяют перенаправление виртуального последовательного порта на именованный канал на хосте; это конфигурация, которую мы будем использовать.
Конфигурирование таргета
Целевая виртуальная машина должна быть настроена для отладки ядра, аналогично локальной отладке ядра, но с добавленным носителем подключения установленой на виртуальный последовательный порт на этой машине.
Один из способов сделать это - использовать bcdedit в окне команд с повышенными правами:
bcdedit / debug on
bcdedit /dbgsettings serial debugport:1
baudrate:115200
Измените номер порта отладки в соответствии с действительным виртуальным серийным номером (обычно 1).
Виртуальная машина должна быть перезапущена, чтобы эти конфигурации вступили в силу. Прежде чем сделать это, мы можем сопоставить последовательный порт для именованного канала. Вот процедура для виртуальных машин Hyper-V:
• Если виртуальная машина Hyper-V относится к поколению 1 (старше), в настройках виртуальной машины имеется простой пользовательский интерфейс для конфигурации. используйте опцию Add Hardware, чтобы добавить последовательный порт, если он не определен.
Затем настройте последовательный порт для сопоставления с именованным портом по вашему выбору. Рисунок 5-6 показывает это.
• Для виртуальных машин поколения 2 в настоящее время пользовательский интерфейс отсутствует. Чтобы настроить это, убедитесь, что виртуальная машина выключена (хотя не обязательно в самых последних версиях Windows 10) и откройте расширенный PowerShell окно.
• Введите следующее, чтобы установить последовательный порт, сопоставленный с именованным каналом:
Set-VMComPort myvmname -Number 1 -Path \\.\pipe\debug
Измените имя виртуальной машины соответствующим образом и номер COM-порта, как было установлено внутри виртуальной машины ранее с помощью Bcdedit. Убедитесь, что путь pipe уникален.
Вы можете проверить, соответствуют ли настройки ожидаемым с помощью Get-VMComPort:
Get-VMComPort myvmname
VMName
------
myvmname
myvmname
Name Path
---- ----
COM 1 \\.\pipe\debug
COM 2
Вы можете загрузить виртуальную машину - цель готова.
Конфигурирование хоста
Отладчик ядра должен быть настроен для подключения к виртуальной машине через тот же последовательный порт, сопоставленный с одноименным pipe выставленным на хосте.
• Запустите отладчик ядра и выберите File / Attach To Kernel. Перейдите на вкладку COM. Рисунок 5-7 показывает, как выглядят эти настройки.
• Нажмите на Отладчик должен прикрепиться к цели. Если это не так, нажмите кнопку панели инструментов Break.
Вот типичный вывод:
Обратите внимание, что подсказка имеет индекс и слово kd. Индекс - текущий процессор, который вызвал изменения.
В этот момент целевая виртуальная машина полностью заморожена. Теперь вы можете отлаживать нормально.
6)Мануалл отладки драйверов
Как только хост и цель связаны, отладка может начаться. Мы будем использовать драйвер PriorityBooster, который мы разработанный в главе 4, чтобы продемонстрировать полную отладку ядра.
• Установите (но не загружайте) драйвер на цель, как это было сделано в главе 4. Убедитесь, что вы копируете файл PDB драйвера вместе с самим файлом SYS драйвера. Это упрощает получение правильных символов для драйвера.
• Давайте установим точку останова в DriverEntry. Мы не можем загрузить драйвер, потому что это может привести к DriverEntry и мы упустим шанс установить точку останова там.
Так как драйвер еще не загружен, мы можем использовать команду bu (неразрешенная точка останова), чтобы установить будущую точку останова.
Войдите цель, если она в данный момент запущена, и введите следующую команду:
0: kd> bu prioritybooster!driverentry
0: kd> bl
0 e Disable Clear u 0001 (0001) (prioritybooster!driverentry)
Точка останова еще не установлена, так как наш модуль еще не загружен.
• Выполните команду g, чтобы позволить цели продолжить, и загрузите драйвер с sc start booster (при условии, что драйвер называется бустер). Если все идет хорошо, точка останова должна установится, и исходный файл должен загрузиться автоматически, показывая следующий вывод в командном окне:
0: kd> g
Breakpoint 0 hit
PriorityBooster!DriverEntry:
fffff801`358211d0 4889542410 mov qword ptr [rsp+10h],rdx
Рисунок 5-8 показывает снимок экрана с исходным окном WinDbg Preview.
На этом этапе вы можете перешагнуть строки исходного текста, посмотреть переменные в окне Locals и даже добавить выражения в окне просмотра. Вы также можете изменить значения, используя окно Locals, как это делается с другими отладчиками.
Окно команд по-прежнему доступно, как всегда, но некоторые операции с пользовательским интерфейсом становятся проще.
Например, настройки точек останова можно выполнить с помощью обычной команды bp, но вы можете просто открыть исходный файл (если он еще не открыт), перейдите к строке, где вы хотите установить точку останова, и нажмите F9 или нажмите соответствующую кнопку на панели инструментов.
В любом случае команда bp будет выполнена в командном окне.
Окно Breakpoints может служить кратким обзором текущей точки останова.
Введите команду k, чтобы увидеть, как вызывается DriverEntry:
Если точки останова, кажется, не в состоянии установить, это может быть проблема символов. Выполните команду .reload и посмотрите, решены ли проблемы.
Установка точек останова в пользовательском пространстве также возможна, но сначала выполните .reload / user.
Может случиться так, что точка останова должна срабатывать только тогда, когда конкретный процесс выполняет код.
Это можно сделать, добавив ключ / p к точке останова. В следующем примере точка останова устанавливается, только если это процесс explorer.exe:
Давайте установим точку останова в месте, показанным на скриншете ниже:
• Запустите тестовое приложение с некоторым идентификатором потока и приоритетом:
Booster 2000 30
Точка останова должна выполнится. Вы можете продолжить отладку в обычном режиме, используя комбинацию исходного кода.
Резюме
В этой главе мы рассмотрели основы отладки с помощью WinDbg. Это важный навык для разработки, так как программное обеспечение всех видов, включая драйверы ядра, могут содержать ошибки.
В следующей главе мы углубимся в некоторые механизмы ядра, с которыми нам необходимо ознакомиться, так как они часто возникают при разработке и отладке драйверов.
Последнее редактирование: