Некоторые устройства, которые мы разрабатываем, требуют написания драйвера устройства для ОС Windows или Linux. Написание драйвера устройства – это не совсем формат нашего сайта, но возможно эта статья будет кому-то полезна. Речь пойдет даже не о написании драйвера, а об отладке драйвера в ОС Windows. Я вот уже две недели как погрузился в отладку драйвера одного устройства.. Не очень простое дело..
Итак, предположим, нам захотелось заглянуть внутрь ядра Windows, посмотреть, как оно работает. Ну или допустим мы написали драйвер нашего устройства, а он не работает или работает неправильно. Нужно посмотреть отладчиком ядра, что происходит.
Прежде всего нужно подготовить «среду» для отладки.
Понадобится 2 компьютера с ОС Windows: первый компьютер – это тот который будет подвержен отладке, второй компьютер – это тот, с помощью которого будет вестись отладка. В терминологии Microsoft первый компьютер называется Target, а второй – это Host.
Эти два компьютера нужно соединить между собой для передачи отладочной информации. Есть несколько способов:
- использовать специальный майкрософтовский отладочный USB кабель. Не думаю, что вы его легко найдете.
- использовать кабель FireWire. Это уже проще. Некоторые компьютеры имеют такой разъем, ну или можно найти PCI плату с разъемом FireWire. У меня сейчас в ПК есть такой разъем на материнской плате, а вот в ноутбуке – нет. Так что этот способ так же отпадает.
- самый простой способ – последовательный порт. Не очень хорошо в смысле скорости передачи, зато просто организовать.
Вот мое рабочее место для отладки:
Слева – Target, инспектируемый компьютер.
Справа – Host, ноутбук отладчика.
В ноутбук подключен кабель USB и программатор MBFTDI. В этом случае мы его будем использовать просто как переходник USB2COM. То есть для ноутбука это как последовательный порт. Правда есть ньюанс – выходные уровни программатора MBFTDI не соответствуют стандартным в последовательном порту. Поэтому я еще подключил преобразователь уровней, на микросхеме MAX232 (нашел его среди старых железок, у нас для них целый ящик в офисе).
Теперь нужно настроить Target. У меня здесь Windows 7 64х битная.
Запускаем окно командной строки CMD от имени администратора и в нем выполняем команды:
>bcdedit /debug on
>bcdedit /dbgsettings serial debugport:n baudrate:rate
У меня debugport:COM1 и baudrate:115200
Это в общем и есть вся настройка инспектируемого компьютера.
Теперь на нем нужно просто выполнить перезагрузку.
Далее настроим хост – у меня это ноутбук.
Здесь нужно установить программу отладчика WinDbg.
Программа отладчика есть в составе Windows Driver Kit (WDK) или в составе Microsoft Software Developer Kit. Все это можно взять с сайта Microsoft https://msdn.microsoft.com/en-us/windows/hardware/hh852365 вполне легально и бесплатно.
У меня на ноутбуке так же Windows 7 x64. Я установил WDK и там в составе есть нужный мне отладчик.
Запускаю WindDbg. Выбираем пункт меню File -> Kernel Debug и появляется окошко:
Выбираю скорость передачи 115200 и имя последовательного порта. В принципе все готово.
На ноутбуке Host в программе WinDbg есть командная консоль, достапная через меню View -> Command. Появляется командная строка отладчика.
Любая высокая технология для наблюдателя со стороны мало отличима от магии..
На хосте в программе WinDbg нажимаю Ctrl+Break и компьютер Target останавливается! То есть полностью стоят все процессы и потоки Windows. Можно попить чайку.
В консоли отладчика можно выполнять различные команды. Команд много, у них много параметров, конечно я не смогу их все описать. В конце концов для этого есть вполне вменяемая инструкция-help самой программы WinDbg.
Самые простые команды:
>u – показать дизассемблированный код в месте, где произошла остановка процессора. Ну или “u <adr>” – посмотреть код по адресу.
>d <adr/reg> – показать дамп памяти по адресу или по регистру.
>r <reg> - показать содержимое регистра процессора.
>t – выполнить одну инструкцию процессора.
>p – выполнить инструкцию процессора или целую процедуру, если инструкция call.
Более того, в отладчике конечно можно установить точки останова различного типа.
Самый простой пример:
>bp <adr> - остановка ядра, когда процессор достигнет указанного адреса.
Еще можно установить точку останова по записи или чтению заданной ячейки памяти или порта ввода вывода.
Отмена всех точек останова – команда «bc *»
Тут нужно еще сказать про символическую информацию.
Конечно, рассматривать голый ассемблерный код удовольствие не очень приятное. Нужно подключить еще отладочную информацию.
Например, мы написали драйвер для ОС Windows. Скомпилировали его debug версию. Вместе с файлом драйвера mydrv.sys компилятор генерирует еще файл с соответствующей символьной информацией mydrv.pdb.
Зайдем в меню отладчика File -> Symbol File Path.. и в диалоговом окне добавим путь к нашему файлу PDB.
Теперь, когда драйвер загружен в память ядра ОС Windows уже проще производить отладку с символьной информацией. Нам теперь не нужно знать абсолютные адреса в нашем драйвере. Установить точку останова можно по имени функции:
>bp mydrv!DriverEntry – остановить ядро, когда произойдет вызов функции DriverEntry нашего драйвера mydrv.
Кроме этого, очень полезно подключить к отладчику еще и символьную информацию самого ядра Windows. Конечно, версий виндовсов много, есть разные сборки и где найти символьную информацию именно соответствующую вашей Target ОС Windows?
Проще всего, в командной строке отладчика выполнить команду
> .sympath SRV*c:\localsymbols*https://msdl.microsoft.com/download/symbols
При этом, нужные файлы отладки (именно нужной версии) будут выкачаны через интернет к вам на диск в папку c:\localsymbols прямо с сервера Microsoft.
Теперь, можно уже более осмысленно дебажить и само ядро.
Хотите посмотреть, как выглядит, например, функция USBPORTSVC_CompleteIsoTransfer драйвера usbport.sys? Нет проблем:
Можно поставить точку останова и далее по шагам исполнить все инструкции. Символьная информация помогает понять если не детали, то хотя бы общий смысл исполняемого кода чужого драйвера.
Например, очень многие системные структуры данных в ядре Windows снабжаются «сигнатурой» - специальной строкой, обычно из 4 символов. Таким образом, функции ядра имеют возможность легко проверить переданные им указатели на структуры являются верными или нет. Вот в коде на картике выше есть вызов функции USBPORT_AssertSig. Уже по названию функции становится примерно понятно, что она делает – проверяет указатель, действительно ли он указывает на структуру с нужной сигнатурой.
Вот еще что. При вызовах функций ядра Windows обычно первые четыре параметра функций передаются в регистрах RCX, RDX, R8 и R9. Похоже остальные параметры, если их больше четырех, передаются функциям в стеке.
Отладка собственного драйвера может быть еще проще, так как имеются исходные тексты самого драйвера. Укажите к ним путь в диалоговом окне отладчика Source Search Path и можно будет выполнять по шагам не отдельные команды процессора, а целые строки программы C/C++. Так же становятся доступны для просмотра локальные переменные функций и прочая отладочная информация.
Вообще отладчик WinDbg дает широкие возможности для отладки своих драйверов, а так же возможность для изучения вообще ядра ОС Windows.
Подробнее...