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

  1. Печатные издания.

  2. Официальная документация DDK от Microsoft.

  3. Open-source проекты.

Сложно поспорить с тем, что порог вхождения в область разработки драйверов достаточно высокий, однако желание погрузиться в это направление пропадает еще на этапе написания HelloWorld. Начинающему программисту сложно найти ответы на казалось бы банальные вопросы:

  1. Как запустить драйвер?

  2. Как понять, что драйвер работает?

  3. Как отлаживать драйвер?

В этой статье я постараюсь максимально подробно описать несколько (вообще их гораздо больше) способов установки и отладки драйвера, удобные по моему субъективному мнению, на примере реализации простого драйвера, выполняющего единственное действие - вывод при запуске отладочного сообщения "Hello from Windows kernel mode!!".

Предварительные условия

Разрабатывать драйвер я буду в Microsoft Visual Studio 2019 (на момент публикации Microsoft предлагает WDK для Visual Studio 2022, совместимый только с Windows 11, переходить на которую пока не планирую). Для разработки драйвера необходимо скачать установить пакет DDK с официального сайта Microsoft.

Microsoft предлагает несколько версий (соответствуют сборкам самой операционной системы), однако пакеты WDK для Windows 10 имеют обратную совместимость, то есть самая свежая версия WDK позволяет реализовать драйвер для младших сборок Windows. Однако, как обычно, есть один нюанс, касающийся настройки тестовой системы (об этом будет написано ниже), поэтому также стоит скачать пакет WDK для вашей тестовой системы. Посмотреть номер сборки можно утилитой winver, (Win+R -> winver -> Enter).

В моем случае самая свежая версия WDK - это WDK для Windows 10 версии 1903 (статья написана сильно раньше как пособие для студентов, на момент публикации доступна версия 2004) на виртуальную машину будет установлена Windows 10 build 1709, поэтому я также скачиваю (но не устанавливаю) WDK для Windows 10 версии 1709.

Для запуска и отладки драйвера будет использована виртуальная машина с ОС Windows 10 x64 на VMWare Workstation.

Для просмотра отладочных сообщений от драйвера используется утилита DbgView из пакета Sysinternals.

Создание проекта

Сначала предлагается реализовать сам драйвер. Так как по плану он должен обладать минимальным функционалом, то этот процесс займет небольшое количество времени.

Шаг 1. Запустить Microsoft Visual Studio.

Шаг 2. Приступить к созданию нового проекта KMDF Driver.

Нажать на кнопку создания нового проекта (Create a new project), в списке типов проекта выбрать KMDF Driver, Empty, нажать Next.

Окно создания нового проекта
Окно создания нового проекта

Шаг 3. Назначить имя проекта и его расположение.

Я назову проект HelloWorldDriver.

Настройки при создании проекта
Настройки при создании проекта

Шаг 4. Добавить файл исходного кода драйвера.

Добавить файл исходного кода Project->Add new Item->C++ File (.cpp). Дать название файлу, например, HelloWorldDriver.c

При вводе имени файла укажите расширение *.c. Visual Studio определяет целевой язык по расширению файла, таким образом исходный код, расположенный в файлах с расширением *.c воспринимается как код на языке Си.

Добавление файла в проект
Добавление файла в проект

Шаг 5. Написать исходный код драйвера.

Далее приведен исходный код нашего учебного драйвера с короткими комментариями.

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

Также не является обязательным примение макроса UNREFERENCED_PARAMETER. По умолчанию (и это правильно) проекты типа Windows Driver компилируются с флагом /WX - Treat Warnings As Errors, что не позволяет скомпилировать драйвер даже с предупреждениями, одним из которых является C4100: unreferenced formal parameter -
неиспользуемый параметр
. Можно либо отключить этот флаг в настройках проекта Project->Properties->C/C++->General, параметр Threat Warnings As Errors, однако применение макроса более предпочтительно, так как заставляет разработчика в большей степени отдавать отчет своим действиям.

// Основной файл WDK
#include <ntddk.h>

// Объявление функции выгрузки драйвера
VOID DriverUnload(PDRIVER_OBJECT driverObject);

// Точка входа в драйвер (аналог функции main)
NTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath)
{
  // Отметка параметра неиспользуемым
  UNREFERENCED_PARAMETER(registryPath);

  // Печать отладочного сообщения
  DbgPrint("Hello from Windows kernel mode!!");

  // Регистрация функции выгрузки драйвера
  driverObject->DriverUnload = DriverUnload;

  return STATUS_SUCCESS;
}

// Функция выгрузки драйвера
VOID DriverUnload(IN PDRIVER_OBJECT driverObject)
{
  // Отметка параметра неиспользуемым
  UNREFERENCED_PARAMETER(driverObject);
  // Вывод отладочного сообщения
  DbgPrint("Goodbye!");
}

Шаг 6. Собрать драйвер.

Теперь можно скомпилировать драйвер Build->Build Solution и посмотреть, какие файлы сгенерированы. Необходимо учитывать платформу тестовой системы и осуществлять сборку правильно. У меня в качестве тестовой системы будет 64-разрядная версия Windows, поэтому мне нужно изменить конфигурацию на x64.

Выбор платформы
Выбор платформы

В выходной директории (по умолчанию это $SolutionDir\x64\Debug) появились файлы. Краткое описание каждого из них:

  1. HelloWorldDriver.cer - сертификат.

    Каждый драйвер должен иметь цифровую подпись (начиная с 2017 года все драйверы должны быть подписаны самой Microsoft, обязательное условие - прохождение тестов WHQL), хотя в сегодняшнем примере сертификат не нужен совсем. Вопросы сертификации выходят за рамки данной статьи.

  2. HelloWorldDriver.inf - вспомогательный файл для установки драйвера с
    использованием Мастера установки Windows.

  3. HelloWorldDriver.pdb - отладочные символы.

  4. HelloWorldDriver.sys - сам драйвер.

Драйвер написан, собран и готов к установке на целевую систему.

Установка драйвера

Можно предложить два основных способа установки драйвера: с помощью мастера установки и вручную, с использованием утилиты SC (вообще говоря можно даже реализовать программу, которая используя функции OpenSCManager, CreateService, OpenService и другие из Windows API позволит управлять драйверами и службами). Однако мастер установки предполагает установку драйверов для устройств, а написанный драйвер таковым не являтеся, поэтому установка будет произведена с помощью утилиты SC.

Перед установкой драйвера (точнее перед первым запуском) необходимо включить тестовый режим, позволяющий устанавливать неподписанные драйверы. Сделать это можно выполнив в командной строке от имени администратора bcdedit /set testsigning ON, после чего необходимо перезагрузить систему. Хотя при сборке проекта был создан самоподписанный сертификат, ОС Windows 10 не позволит запустить драйвер, подписанный таким сертификатом (как было сказано выше, в новых версиях ОС драйвер должны иметь подпись от Microsoft).

Далее необходимо выполнить следующие действия:

Шаг 1. Скопировать файл HelloWorldDriver.sys на целевую систему.
Скопированный на целевую систему драйвер
Скопированный на целевую систему драйвер

Шаг 2. Открыть командную строку от имени администратора.
Командная строка
Командная строка

Шаг 3. Выполнить команду установки драйвера.

Для установки драйвера при помощи системной утилиты SC нужно исполнить команду SC CREATE <название_драйвера> binPath= <путь_до_sys_файла> type=kernel

Установка драйвера
Установка драйвера

Драйвер установлен, но не запущен. Информацию о текущем состоянии драйвера можно получить командой SC QUERY <название_драйвера>

Драйвер установлен и остановлен
Драйвер установлен и остановлен

Перед первым запуском предлагается запустить утилиту DbgView и убедиться, что драйвер действительно выводит отладочное сообщение. Утилиту необходимо запустить от имени администратора, а после запуска включить захват сообщения из ядра нажатием Capture->Capture Kernel и включить подробный режим вывода нажатием Capture->Enable Verbose Kernel Output. WDK предлагает расширенную версию функции отладочной печати - DbgPrintEx, которая позволяет задать уровень важности сообщения, это позволяет фильтровать сообщения с уровнем важности меньше заданной.

Утилита DbgView
Утилита DbgView

Запустить драйвер можно командой SC START <название_драйвера>, в окне DbgView должно появиться заданное в исходном коде сообщение.

Полученное от драйвера сообщение
Полученное от драйвера сообщение

Может случиться, что после запуска драйвера сообщение в DbgView не появится. В таком случае нужно попробовать перезагрузить систему. Скорее всего, настройки DbgView, касающиеся включения подробного режима, применяются не сразу.

Остановить драйвер можно командой SC STOP <название_драйвера>, а в DbgView снова появится отладочная строка.

Остановка драйвера
Остановка драйвера

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

Отладка драйвера

Очень сложно писать программы без ошибок, если это не "Hello world", конечно. Процесс разработки включает внесение ошибок в код, обнаружение наличия ошибок, поиск источников ошибок (конкретных мест в исходном коде) и их устранение. Сложно поспорить с тем, что самым трудоемким процессом из вышеперечисленных является именно поиск, потому как вносятся ошибки чаще всего прозрачно для программиста (то есть случайно), проявляют они себя тоже сами (или их обнаруживают тестировщики, а еще хуже - пользователи).

Отладка программы может осуществляться различными способами. Например, часть моих студентов в курсе, посвященному языку PHP, отлаживают программы путем вставки в определенные участки кода конструкций типа echo ("I'm here42") или print("Var1 = $var1"). Почему так? Ответ на этот вопрос во многом подтолкнул меня к созданию этой статьи. Всё дело в том, что для PHP подключение привычного отладчика (а студенты сначала изучали язык C++ в IDE, где вполне успешно использовали "коробочные" возможности установки точек остановки, пошагового исполнения, окон watch и memory) подразумевает выполнение некоторого обряда: загрузка дополнительной библиотеки для php (xdebug, например), редактирование php.ini, установка расширения в браузере. И это при том, что в сети достаточно источников, описывающих этот процесс. Материалов по отладке драйверов гораздо меньше.

Я выделяю два основных способа отладки драйвера для ОС Windows: нормальный, с помощью утилиты WinDbg, и удобный - средствами Visual Studio.

Подготовка виртуальной машины

Пошаговая отладка драйвера подразумевает остановку его выполнения. Однако в архитектуре на уровне ядра ОС Windows, грубо говоря, деление на различные драйверы достаточно условно, можно представить все ядро как одну программу, где различные компоненты (драйверы) - это библиотеки. Остановка в исполнении любого драйвера подразумевает остановку всей системы. Поэтому отладка должна производиться на удаленном компьютере (хотя методом вставки строк кода с конструкцией DbgPrint("...") можно отлаживать и локально). Удобно заменить удаленный компьютер виртуальной машиной, как это и делается в подавляющем большинстве случаев.

Так как драйвер исполняется на удаленном компьютере, то связь с отладчиком осуществляется по физическому интерфейсу: COM, Ethernet, USB, FireWire. Наибольшую популярность получила отладка по COM-интерфейсу. Признаюсь, вообще не видел примеров с иными.

На виртуальную машину необходимо добавить последовательный интерфейс, выполнив следующую последовательность действий:

Шаг 1. Открыть настройки виртуальной машины.

Запустить VMWare, открыть настройки виртуальной машины (нажатием Edit virtual machine settings).

Главное окно VMWare
Главное окно VMWare

Шаг 2. Добавить последовательный интерфейс.

В окне настроек виртуальной машины нажать кнопку Add.

Окно настроек
Окно настроек

В появившемся окне выбрать Serial Port, нажать Finish.

Окно редактирования устройств
Окно редактирования устройств

Шаг 3. Настроить последовательный порт

Для добавленного последовательного порта задать следующие настройки: Connection: Use named pipe, в поле для ввода ввести имя канала: \\.\pipe\<pipename>, а в выпадающих списка выбрать соответственно This end is the server и The other end is an application.

Настройки последовательного порта
Настройки последовательного порта

Отладка с использованием WinDbg

Необходимо перевести тестовую систему в отладочный режим. Для этого нужно запустить ее и от имени администратора исполнить команду bcdedit /debug on, а также настроить тип отладки. В примере используется отладка по COM-порту, поэтому в командной строке необходимо выполнить команду bcdedit /dbgsettings serial debugport:2 (значение параметра debugport должно совпадать с номером последовательного порта в настроках виртуальной машины. Скорее всего это будет Serial Port 2). После перезагрузки системы активируется отладочный режим.

После активации режима отладки система может "зависать" при включении и выключении. Я так и не смог разобраться, в каких случаях это проиcходит, а в каких нет. Если при включении или выключении системы она "зависла", то достаточно просто запустить отладчик (об этом ниже) и загрузка продолжится.

WinDbg внешне выглядит не очень современно. Особенно это заметно на фоне привычной разработчикам программ для Windows среды Visual Studio. На момент написания статьи вышла в свет новая версия утилиты отладки - WinDbg Preview, однако она доступна только в Microsoft Store, а у меня Disable Windows Spying отключил возможность установки приложений из магазина. Хотя скриншоты на портале Microsoft выглядят многообещающе.

Скриншот новой версии отладчика WinDbg Preview
Скриншот новой версии отладчика WinDbg Preview
Стартовое окно классического WinDbg
Стартовое окно классического WinDbg

Теперь необходимо настроить WinDbg. Наиболее подробное описание WinDbg представлено на сайте Microsoft, мы же рассмотрим лишь основы. К основным настройкам WinDbg можно отнести следующие:

  1. Тип подключения к отлаживаемой системе, её адрес.

  2. Расположение исходных кодов отлаживаемых драйверов.

  3. Расположение символьной информации.

WinDbg позволяет задать все эти опции в виде аргументов командной строки, поэтому удобно (я делаю так) создать необходимое количество ярлыков исполняемого файла (сам исполняемый файл WinDbg имеет путь Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe), в свойствах ярлыка задать необходимые параметры:

  1. -k. Обозначает режим отладки ядра.

  2. com:port=\\.\pipe\Win10,pipe. Обозначает порт отладки (именование канала должно совпадать с тем, которое было указано в настройках виртуальной машины).

  3. -y srv*C:\ProgramData\Microsoft\Symbols*http://msdl.microsoft.com/download/symbols. Определяет расположение символьной информации. Если отладочные символы не найдены в локальном хранилище, то они будут загружены из указанного репозитория.

  4. -srcpath C:\dev. Определяет расположение файлов исходного кода (можно указывать не директорию конкретного проекта, а директории выше в иерархии).

  5. -WF Dark.WEW. Определяет файл с сохраненным рабочим пространством (WorkSpace). В рабочее пространство входит набор и расположение окно, шрифты, цвета шрифтов и фона. Мне показалось очень удобным однажды настроить WinDbg, а далее использовать эти настройки. За основу своего WorkSpace я взял найденную давно темную тему, далее удобно расположил окна, перенастроил некоторые шрифты.

Свойства ярлыка
Свойства ярлыка

Мне показались удобными такие расцветка и расположение окон:

Возможная настройка рабочего пространства
Возможная настройка рабочего пространства

Теперь давайте отладим драйвер. Для того, чтобы выполнение остановилось, необходимо добавить инструкцию int 3, в исходном коде драйвера это можно сделать, вставив макрос DbgBreakPoint();. Предлагается установить точку останова в первой строке функции DriverEntry и попытаться запустить драйвер. Нет большой разницы, когда запускать WinDbg. В определенные моменты выполнение инструкций прекращается, визуально система "зависает" в ожидании подключения отладчика. То есть можно сначала запустить драйвер, а только потом WinDbg, а можно и наоборот.

Я запустил WinDbg, после чего запустил драйвер. Отладчик подключился к тестовой системе, WinDbg автоматически загрузил исходный код драйвера.

Установленный breakpoint
Установленный breakpoint

WinDbg позволяет производить привычные действия отладки: шаг с заходом, шаг без захода, исполнение до курсора, а также предоставляет возможность наблюдать за участком памяти, значениями регистров.

Отладка средствами Visual Studio

В новых версиях Visual Studio (начиная с 2013) также появилась возможность отладки драйверов. Кроме непосредственно отладчика Visual Studio предлагает широкие возможности по установке драйвера на удаленную систему, что позволяет производить отладку драйвера практически идентично отладке обычной программы (Сборка->расстановка точек останова->запуск отладки).

Для использования функций Visual Studio также необходимо обеспечить отладочное соединение с тестовой системой, в нашем случае это COM-порт, остальные действия не требуются (включать тестовый и отладочный режимы не нужно). Visual Studio позволяет автоматически настроить удаленную систему. Для этого на тестовую систему нужно установить пакет WDK Test Target Setup, который находится в директории %Program Files (x86)%\Windows Kits\10\Remote\%Платформа%. Этот .msi файл нужно скопировать на тестовую систему и установить.

Теперь пошагово сконфигурируем тестовую систему.

Шаг 1. Запустить Visual Studio.
Окно Visual Studio
Окно Visual Studio

Шаг 2. Открыть конфигуратор тестовых устройств.

Открыть конфигуратор тестовых устройств можно нажатием кнопки меню Extensions->Driver->Test->Configure Devices.

Запуск конфигуратора устройств
Запуск конфигуратора устройств

Шаг 3. Добавить и сконфигурировать тестовое устройство.

В первую очередь необходимо открыть окно конфигуратора нажатием кнопки Add New Device.

Окно конфигуратора устройств
Окно конфигуратора устройств

В окне ввести отображаемое имя тестовой системы, сетевое имя (можно ввести просто IP-адрес, либо включить сетевое обнаружение на тестовой системе), в Provisioning Options выбрать пункт Provision device and choose debugger settings, нажать Next.

Окно конфигуратора устройств
Окно конфигуратора устройств

В следующем окне раскрыть вкладку Windows Debugger - Kernel Mode и настроить опции:

  1. Connection Type: Serial

  2. Baud Rate: 115200

  3. Pipe: Checked (отметить)

  4. Reconnect: Checked (отметить)

  5. Pipe name: \\.\pipe\Win10

  6. Target Port: com2 (или иной, должен совпадать с номером в настройках
    виртуальной машины)

Нажать Next.

Настройка отладчика
Настройка отладчика

Подождать выполнения настройки системы. В виртуальной машине вы можете наблюдать процессы создания нового пользователя, установку пакетов, выполнение скриптов. Это нормально. По окончании настройки виртуальная машина будет перегружена, а в Visual Studio появится сообщение об окончании настройки. В моем случае возникла одна ошибка при создании точки восстановления системы. Это можно проигнорировать. Нажать Finish.

Процесс настройки тестовой системы
Процесс настройки тестовой системы

Настройка тестовой системы почти закончена. На нее установлены средства TAEF - Test Authoring and Execution Framework и WDTF - Windows Driver Test Framework, позволяющие Visual Studio автоматически удалять старую версию драйвера, устанавливать новую, а также проводить автоматическое тестирование (вопрос автоматических тестов выходит за рамки данного материала).

ВНИМАНИЕ!!! В самом начале статьи я рекомендовал выяснить версию Windows на тестовой машине. Если она совпадает с версией установленного пакета WDK, то данный абзац можно не читать. WDK имеет обратную совместимость, то есть установленная WDK 1903 позволяет разрабатывать драйверы для предыдущих сборок (можно найти этот параметр в свойствах проекта, хотя я не сталкивался с ситуацией, когда драйвер не работает на некоторой сборке), однако пакет WDK - это не только набор заголовочных файлов, расширение для Visual Studio, но еще и набор вспомогательных утилит для отладки (тот же WinDbg) и тестирования. В том числе в пакет WDK входит и названный выше WDTF. И он обратной совместимости не имеет (или, возможно, не имеет для некоторых сборок). Для проверки наличия WDTF необходимо проверить на тестовой системе содержимое директории C:\Program Files (x86)\Windows Kits\10\Testing\Runtimes. Если там содержатся поддиректории TAEF и WDTF, то все в порядке, а если только TAEF, как в моем случае, то нужно отдельно установить WDTF из пакета WDK версии, совпадающей с версией тестовой системы (напомню, у меня это Windows 10 1709). На тестовую систему из директории WDK\Installers необходимо скопировать файл Windows Driver Testing Framework (WDTF) Runtime Libraries соответствующей разрядности и попытаться установить его командой msiexec /i "Windows Driver Testing Framework ....". Установщик выдаст ошибку отсутсвия .cab файла. Найдите соответствующий файл в той же директории Installers, скопируйте на тестовую машину и повторите установку. Рядом с директорией TAEF должна появиться WDTF.

Тестовая система готова к загрузке и отладке драйвера. Visual Studio предлагает различные варианты установки (Deploy) драйвера на тестовую систему, настройки доступны в свойствах проекта во вкладке Driver Install->Deployment. В первую очередь нужно выбрать тестовую машину из выпадающего списка. Выше она уже была сконфигурирована и теперь доступна.

Настройки установки драйвера
Настройки установки драйвера

Подробно с различными вариантами можно ознакомиться на портале Microsoft. Мне показались наиболее понятными два из них: Install/Reinstall and Verify и Custom Command Line. Стоит отметить, что не совсем корректно работают точки остановки. Даже при их установке выполнение не прерывается. Нужно вставить как минимум одну конструкцию DbgBreakPoint(); (я это делаю первой же строкой функции DriverEntry), дальше отладку можно производить привычным для пользователя Visual Studio образом, пошаговая отладка нормально работает, установленные точки остановки тоже.

При опции Install/Reinstall and Verify драйвер будет установлен (переустановлен) и запущен, однако система будет перезагружена (это долго), а драйвер будет установлен с опцией старта при запуске системы. Для большинства случаев это не должно быть проблемой, однако если для вас важен момент запуска драйвера, то этот способ не подходит. Я устанавливаю конструкцию прерывания в первой строке DriverEntry, а также устанавливаю точку остановки на строке с выводом отладочного сообщения и возвратом из функции и нажимаю привычное F5.

Готовность к отладке
Готовность к отладке

На виртуальной машине можно увидеть исполнение скриптов. Также могут появляться сообщения об ошибках, однако даже с некоторыми ошибками все работает. В моем случае по неизвестным мне причинам установка драйвера не всегда удается с первого раза (в Visual Studio вы увидите сообщение о том, что программа завершила работу с ненулевым кодом). Но со второй-третьей попытки все начинает работать. После исполнения скриптом система перезагрузится, драйвер начнет загружаться, исполнение остановится на конструкции DbgBreakPoint();. Отладчик Visual Studio должен подключиться к виртуальной машине, отобразить в окне с исходным кодом текущую строку и нормальным образом реагировать на пошаговое исполнение, показывать значения переменных, отображать содержимое памяти, и, что важно, останавливаться на установленных средствами Visual Studio точках остановки.

Окно Debugger Immediate Window точно такое же, как и в WinDbg. Точнее говоря, это одно и то же. Поэтому Visual Studio через него поддерживает все команды WinDbg, а среди них много полезных.

Подключенный отладчик
Подключенный отладчик
Содержимое окон Locals и memory
Содержимое окон Locals и memory

Еще один способ установки драйвера, который я использую - это Custom Command Line. Можно создать простой bat-скрипт, содержащий команды по установке и запуску драйвера. Visual Stuio позволяет указать дополнительные файлы, которые нужно скопировать на тестовую систему, однако эта функция не работает. Поэтому создаем файл createandstart.bat на диске C, который содержит две строки: SC CREATE HelloWorldDriver binPath= C:\DriverTest\Drivers\HelloWorldDriver.sys type=kernel и SC START HelloWorldDriver

Скрипт установки и запуска
Скрипт установки и запуска

А в поле ввода под опцией Custom Command Line указать C:\createandstart.bat

Настрока установки
Настрока установки

При нажатии F5 драйвер установится, запустится и Visual Studio укажет на строку с вызовом макроса DbgBreakPoint();

Простая отладка средствами Visual Studio

Однако все возможности Visual Studio Deployment раскрываются при создании тестов. Для простой отладки все это лишнее, поэтому, возможно, будет гораздо проще настроить тестовую систему для отладки самостоятельно (утилитой bcdedit), далее вручную запускать драйвер, а отлаживать его средствами Visual Studio. Такой подход представляет собой что-то среднее между рассмотренными выше. Для подготовки к отладке необходимо выполнить следующие шаги:

Шаг 1. Подготовить виртуальную машину.

На виртуальной машине нужно включить тестовый и отладочный режимы, а также установть драйвер командой SC CREATE.

Шаг 2. Сконфигурировать Visual Studio.

В Visual Studio открыть конфигуратор тестовых устройств, однако на первом шаге выбрать вариант Manual configure debuggers and do not provision, нажать Next, в следующем окне настроить все идентично, как на рисунке.

Ручная настройка
Ручная настройка
Настройка отладчика
Настройка отладчика

Теперь на тестовой системе можно запустить драйвер командой SC START. Выполнение драйвера остановится на строке DbgBreakPoint();, а виртуальная машина будет ожидать подключения отладчика. В Visual Studio нужно нажать Debug->Attach To Process, в появившемся окне в поле Connection type выбрать Windows Kernel Mode Debugger, в поле Connection target — имя настроенной выше тестовой машины, нажать Attach.

Подключение отладчика к процессу
Подключение отладчика к процессу

Очень вероятно, что вы получите ошибку Frame is not in module:

Несоответствие драйвера исходному коду
Несоответствие драйвера исходному коду

Введите в поле ввода окна Debugger Immediate Window команду .reload и переключитесь на файл с исходным кодом. Если и это не помогло, скорее всего, вы что-то изменили в исходном коде, скомпилировали, но забыли скопировать новую версию драйвера. Пересоберите (для надежности) проект, скопируйте его на тестовую машину, запустите драйвер, подключие отладчик снова.

Я считаю этот способ самым удобным при отладке драйвера. Он позволяет использовать более комфортную и привычную Visual Studio, при этом не тратить целые минуты на установку новой версии драйвера на тестовую систему.

Заключение

Разработка драйверов для ОС Windows — достаточно увлекательный процесс. Реализация даже мелких и простых драйверов позволяет глубже понять внутреннее устройство Windows, что весьма полезно. Однако начинающих системных программистов отпугивает не столько сложность написания непосредственно кода (как минимум, есть множество примеров), сколько сложность установки и отладки. Надеюсь, что данный материал поможет кому-то не потерять интерес в первом же проекте.

Upd

В комментариях были выражены опасения, что инструкция по установке драйвера некорректная в части отключения подписи драйверов. В результате эксперимента установлено, что все нормально и работает ровно так, как описано. Также попутно выяснилось, что можно смело качать последнюю версию WDK (для 22H2) и SDK (если у вас Windows 10, а не Windows 11) и тогда разработку можно вести в Visual Studio 2022.

Комментарии (52)


  1. MichaelBorisov
    24.07.2022 12:28
    +11

    Спасибо за статью, очень интересная тема!

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

    Мне пришлось однажды по работе заниматься драйвером. Не с нуля разработать, а портировать под 64 бит. Это оказалось достаточно легко, но потом… Просто режим TestSigning работал в Windows 7, но в Windows 10 всё гораздо сложнее. Необходимо сначала особым образом вызвать перезагрузку системы, тогда при старте появится специальное меню. И там, в глубине, есть один пункт, который позволяет снять требование сертификации загружаемых драйверов. На один раз. При следующей перезагрузке процедуру необходимо повторить заново.

    В конце концов всё заработало, и мы попытались связаться с Microsoft для выяснения стоимости и условий сертификации. Даже дозвониться до службы поддержки проблема — телефоны открыто не публикуются, а веб-интерфейс стремится перенаправить человека на форумы и т.д., где цены не публикуются, а условия указаны расплывчато. Ожидаемо, сотрудники телефонной поддержки даже не знали, что это такое — сертификация драйверов. После открытия бесчисленного количества тикетов и перенаправлений к другим сотрудникам в другие офисы и другие страны, наконец-то, удалось поговорить с кем-то, кто хоть чуть-чуть в курсе дела. Они обещали помочь, но… Перезвонили и сообщили, что «из соображений безопасности» до того, как наша фирма получит EV сертификат (который стоит около 2000$ и сам по себе не связан с Microsoft или сертификацией драйверов) с нами вообще не будут разговаривать, в том числе не назовут цену за получение ЭЦП драйвера от Microsoft.

    Так и похоронили этот вопрос.

    А у вас есть какая-либо информация о том, как подписать драйвер в Microsoft? Сколько это стоит, что для этого нужно? Правда ли, что нужно направлять железо, к которому разрабатывается драйвер, на тестирование в Microsoft?


    1. DSarovsky Автор
      24.07.2022 12:41
      +3

      Спасибо за оценку и развернутый комментарий!
      Да, в Win10 есть режим (среди особых вариантов загрузки F8) «Отключить требование подписи драйверов», но у меня на указанной системе работало и просто после включения TestSigning (возможно, работает только в связке с отладочным режимом, честно говоря, не проверял).
      По поводу сертификации ничего не могу сказать, никогда не было задачи разработать драйвер «в продакшн», но насколько я понял, главное условие — прохождение их тестов WHQL, про железо не слышал (странное требование, разработчик же может заглушку в железку прошить и толку от нее для MS примерно 0).


      1. RalphMirebs
        24.07.2022 14:24

        Я не очень понял: если я (в теории) напишу драйвер для некого своего самодельного оборудования PCIex, он будет работать на моей операционнке (Win 7) без всяких подписанных сертификатов?


        1. VBKesha
          24.07.2022 14:46
          +3

          Если отключить проверку подписи то будет.


          1. DSarovsky Автор
            24.07.2022 15:50
            +2

            На Windows 7 даже не надо отключать проверку подписи, можно добавить сертификат в список доверенных и всё будет работать.


      1. MichaelBorisov
        24.07.2022 14:47

        главное условие — прохождение их тестов WHQL

        Тут вопрос, где они будут запускать эти тесты. Правила сертификации за последние годы несколько раз менялись в сторону ужесточения. Многие веб-страницы с информацией по теме (в том числе на сайте Microsoft) устарели.

        Если я правильно понял результаты своего последнего поиска — то тесты должны исполняться на компьютере Microsoft. А поскольку драйвер не работает без железа — то в Microsoft необходимо выслать по почте железо, с которым будет работать драйвер. Может быть, вместе с компьютером. Там в тексте были слова «Submit your hardware». Может, их можно толковать как-то иначе, но бесплатно разъяснять сотрудники Microsoft отказались.

        Мы бы и не против выслать в Microsoft железо. Но связанные с этим манипуляции со стороны Microsoft (вставить карточку в компьютер; включить; рисковать поломкой компьютера) требуют довольно высокой квалификации персонала и могут стоить немалых денег. Знать бы, какого порядка сумма. У нас просто решалась судьба проекта: сколько надо в него вложить денег для того, чтобы были дрова под новые версии Windows; и окупились ли бы эти вложения.


        1. emusic
          25.07.2022 12:51

          Если я правильно понял результаты своего последнего поиска — то тесты должны исполняться на компьютере Microsoft

          Нет, все тесты исполняются у разработчика. Тестовая система собирает результаты, их потом нужно отправить в MS для сертификации.


          1. DSarovsky Автор
            25.07.2022 12:55

            Интересно, то есть MS каким-то образом проверяют, что драйвер действительно прошел тесты, а не разработчик результаты подменил?


            1. emusic
              25.07.2022 13:37

              Скорее всего, там есть какая-то базовая защита от подмены, но вряд ли серьезная. Большинство современных разработчиков драйверов не имеет ни хакерских способностей, ни желания ковыряться в чем-то без документации. Они просто более-менее изучили ядерные API и правила разработки, и пишут код так, чтобы он более-менее работал, даже не слишком понимая тонкости взаимодействия софта и железа. До типичного разработчика 80-90-х, который на все руки мастер, им очень далеко.


              1. El_corona
                25.07.2022 13:59

                submission package подписывается с помощью ключа, привязанного к компании на dev portal


                1. emusic
                  26.07.2022 06:06

                  Да, только это не гарантирует подлинности результатов тестов.


                  1. El_corona
                    26.07.2022 11:03

                    Вы правы, так и есть. Это всё на веру, что драйвер приложенный в пакет и драйвер в результатах тестов один и тот же. Там были версии и хэши, насколько я помню, но их совпадения никто не проверял. Впрочем, нужно помнить, что hardware dashboard помнит все ваши отправки, а в cat вшиваются данные о вендоре, так что в теории MS легко может отследить факт подмены


                    1. emusic
                      27.07.2022 09:22

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


        1. emusic
          25.07.2022 12:54

          Кстати, сейчас с подписыванием новая проблема - MS отключила многим (если не всем) разработчикам, зарегистрированным, как российские, доступ к Hardware Portal. Я с их поддержкой на эту тему бодаюсь уже месяц. Неделю назад получил последний ответ - "this issue it still investigated, you will be informed".


          1. Moraiatw
            25.07.2022 15:39
            -1

            Зарегистрироваться как американец?


            1. emusic
              26.07.2022 06:01

              Лет десять назад такой совет еще худо-бедно котировался, а сейчас он выглядит, как минимум, недалеким.


              1. Moraiatw
                26.07.2022 08:27

                Как по вашему малварщики получают EV сертификаты? Явно не на свой российский паспорт.


                1. DSarovsky Автор
                  26.07.2022 08:35
                  +1

                  А они получают? Я так понял, что с этим теперь проблема, по крайней мере в Windows 10. Так что приходится им, наверно, таскать с собой легальный драйвер с известной уязвимостью, сначала запускать его, а потом передавать управление на себя.


                  1. Moraiatw
                    26.07.2022 16:20

                    Юзермодные бинарники вполне подписывают. А ring 0 мальварь сейчас уже не делают. Сертификаты продаются за $2k-$3k.


                    1. DSarovsky Автор
                      26.07.2022 16:25
                      +1

                      Но мы все-таки в первую очередь про ядро. Для подписи usermode программ необязательно привлекать Microsoft, использовать можно хоть Let's Encrypt, условно говоря (они, правда, code-signing не поддерживают и не собираются, но есть GlobalSign, DigiCert).


                1. emusic
                  26.07.2022 18:30

                  И где/как я могу по поддельным иностранным документам получить EV-сертификат, который затем пройдет проверку в MS Hardware Portal?

                  Собственно, EV-сертификат у меня есть, но просто интересно.


      1. dartraiden
        25.07.2022 18:19

        «Отключить требование подписи драйверов»
        Это, вроде, и есть testsigning on, просто однократный, до перезагрузки.


        1. emusic
          26.07.2022 06:04

          Эти варианты не эквивалентны. "testsigning on" разрешает установку драйвера с самоподписанным CAT-файлом (если он есть). Отключение проверки подписи, кроме этого, разрешает загрузку. В ядерном загрузчике своя политика проверки подписей.


          1. dartraiden
            26.07.2022 09:03

            В документации написано, что «testsigning on» также разрешает загрузку:

            By default, Windows does not load test-signed kernel-mode drivers. To change this behavior and enable test-signed drivers to load, use the boot configuration data editor, BCDEdit.exe, to enable or disable TESTSIGNING, a boot configuration option.

            Корректнее сказать, что загрузка системы с «отключением проверки подписи» снимает требование подписи у драйвера вовсе, а «testsigning on» лишь ослабляет требования, разрешая загрузку самоподписанного драйвера (драйвер с отсутствующей подписью не будет загружен). В этом я был неправ, сказав, что это одно и то же.

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


    1. gdt
      24.07.2022 15:23
      +16

      У меня был опыт сертификации. Без опыта это боль от начала и до конца, такая статья очень пригодилась бы в то время.

      Как подписать:
      1. По инструкциям с сайта MS развертываете у себя HLK (VHLK), минимум две машины — одна контроллер, на другой будут проходить тесты
      2. Убеждаетесь что машины из п. 1 видят друг друга и тд
      3. На ведомую машину (машины) ставите драйвер (и устройство, если оно не 100% софтверное)
      4. Точно не помню — либо в самом HLK выбираете набор тестов, соответствующий вашему устройству, либо качаете нужный плейлист с сайта MS
      5. Также бывают некорректные тесты, если есть errata — лучше об этом узнать и применить до запуска тестов
      6. Запускаете тесты, идете спать
      7. С утра материтесь, добавляете всякие ASLR и DEP, меняете memset на RtlZeroMemory и тд
      8. Повторяете пункты 6-7 до успешного результата
      9. Дальше, идете в партнерский центр MS, там есть какой-то типа hardware dashboard
      10. Там по определенным правилам формируете submission package
      11. Он валидируется какое-то время, если все ок — можете скачать уже сертифицированный драйвер
      12. По-моему, это бесплатно. Но нужно регистрироваться как партнер MS вроде как, подтверждать бизнес и тд. Железо не нужно отправлять, только сам драйвер, и результаты тестов.


      1. El_corona
        24.07.2022 17:59
        +3

        Согласен, сертификация - вещь в себе, тоже не хватало ресурсов в своё время. Есть еще несколько нюансов - в HCK/HLK студии есть автоматические фильтры, или автоматические эрраты - если тесты упали, не спешите расстраиваться, где-то в студии была кнопка вроде "apply filters", которая может "позеленить" их. Такие вещи иногда добавляются и поставляются на клиенты оригинальным образом - нужно с hardware dashboard выкачать архив в сиквел запросами и прокатать их в HCK/HLK используя соответствующую тулзу. Так что если вам выпишут такой фильтр (нам выписывали через premier support), то он не применится пока не примените скрипт. А еще в WLK (это древность, сейчас только для Windows Server 2008 R2 обоснованно для лого) есть тесты которые только красные и проходят только с ручной эрратой. Ручная эррата это Word документ, где описан номер эрраты и падучий тест - его надо приложить в результирующий пакет и сотрудник майкрософт через недельку вам его согласует.

        В целом я бы не постеснялся сказать, что у меня огромный опыт в WLK/HCK/HLK для NDIS, ELAM и фильтров ФС. В своё время даже автоматизировали эти тесты, с горем пополам, но ручной по прогонам фактически ушёл. Могу помочь с вопросами по теме


      1. emusic
        25.07.2022 12:50

        Чтобы драйвер загружался в настольных (desktop) версиях Windows, достаточно attestation-signing через Hardware Portal, проводить тесты HLK не требуется. Но их всегда полезно прогнать (хотя бы в ручном режиме, выдрав нужные из MSI, чтобы не устанавливать всю эту громоздкую хрень целиком), чтобы не искать ошибки/глюки наугад.


        1. gdt
          25.07.2022 12:56

          К сожалению, это работает только для Windows 10+. И вроде не для всех драйверов, например, для ELAM все равно нужны тесты.


          1. El_corona
            25.07.2022 14:03

            ELAM это супер специфика, конечно, у них и подпись другая, нежели остальные от майкрософт


          1. emusic
            25.07.2022 18:42

            Это работает для всех версий, начиная с висты.


            1. gdt
              25.07.2022 18:45

              Как прокомментируете то, что написано здесь? docs.microsoft.com/en-us/windows-hardware/drivers/dashboard/code-signing-reqs#windows-10-attestation-signed-drivers


              1. emusic
                26.07.2022 06:21

                У них давно уже путаница везде. Я с прошлого года, как они отменили кросс-сертификаты, делаю SYS только с attestation-signing, плюс отдельный CAT под 6.x, подписанный моим EV-сертификатом.


                1. gdt
                  26.07.2022 10:36

                  Я задам только ещё один вопрос - подпись у вас SHA1, или SHA2? Насколько я помню, SHA1 больше не делают (по крайней мере там, где делали мы), а SHA2 поддерживается нативно только начиная с восьмёрки, на семёрку нужен патч, который, как показывает практика - есть примерно ни у кого.


                  1. emusic
                    27.07.2022 09:19

                    У меня SHA2, SHA1 уже давно не выдают, а EV-сертификаты, насколько я помню, изначально выпускались только с SHA2.

                    Если кто-то сидит на семерке и не ставит обновление для поддержки SHA2 - не сам ли он себе злобный буратина?


                    1. gdt
                      27.07.2022 09:21

                      Это да, но эти несчастные проценты юзеров тоже могут заплатить, к сожалению, им нельзя так сказать. Я к тому, что как у вас на семерке работает, если она даже не может проверить подпись?


                      1. emusic
                        27.07.2022 10:01

                        Не понял - почему нельзя? У меня такие периодически покупают, жалуются, что не устанавливается, я им отвечаю, что нужно поставить обновление. Рутинная процедура, что в ней сложного или необычного?


      1. emusic
        25.07.2022 13:41

        развертываете у себя HLK (VHLK), минимум две машины — одна контроллер, на другой будут проходить тесты

        А могут обе машины быть виртуалками? Когда я несколько лет назад изучал этот вопрос, контроллер требовалось ставить непременно на железную машину, причем непременно с серверной виндой. Но у меня так и не дошли руки все это полноценно развернуть - ограничился выдиранием отдельных тестов из MSI, и получением attestation signature у MS.


        1. gdt
          25.07.2022 13:49
          +1

          Я знаком с людьми, которые утверждают, что можно — естественно, если драйвера полностью софтверные, без железной части. Но это нужно на 100% знать, что делаешь — у меня тоже не хватило сил добить этот вопрос в свое время. Правда там были проблемы с нодами — у виртуальных машин порой свои представления про энергосбережение и т. д. Есть и другие моменты — например, если драйвер сетевой, нужен роутер обязательно с IPv6, и таких моментов много :)


          1. El_corona
            25.07.2022 14:02
            +1

            может быть всё на виртуалках - WLK/HCK/HLK, хотя формально только для HLK вроде бы заявлена поддержка виртуальных машин и даже поставлялся образ вм. У студий есть жёсткая завязка на имя хоста, если его поменять - всё развалится. Вероятно такие кейсы есть еще и поэтому не рекомендуют виртуалки. Но в целом всё работает без проблем


      1. El_corona
        25.07.2022 14:35
        +3

        Еще вспомнились нюансы:

        • тесты есть натурально бажные. Какой-то тест на фильтры фс падал если на машине больше двух гигов памяти, воркараундилось зарезанием через bcd

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

        • есть тесты, которые проходят только на конкретной редакции оси, например на server core. Если у вас какие-то завязки на GUI - придётся один тест пускать там, остальные - на обычной редакции, затем сливать пакеты проектов в один

        • для WPF приходилось писать тулзы, чтобы драйверу можно было надиктовать правила фильтрации трафика. Также эти тесты примечательны тем, что там на лету нужно делать ini файл конфига для агента студии

        • агенты порой теряют связь до студии и машина вываливается из доступных к прогону. Решалось сбрасыванием статуса машины на студии и молитвами

        • некоторые тесты читают лог агента для ассертов, если его грохнуть - тест может повиснуть или упасть, точно помню так кто-то проверял критерий произведенного ребута, что можно было элегантно костылять перезапуском службы

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

        • какой-то тест падал при наличии в системе русской локали, имя не помню, тоже что-то связанное с фильтрами фс


        1. emusic
          26.07.2022 06:09

          А уж насколько бажные там тесты звуковых драйверов... Я еще в середине 2000-х, когда пошла мода на Windows Logo, удивлялся количеству сертифицированных MS звуковых драйверов, которые глючили и падали на корректных запросах, выдавали некорректные результаты и т.п. И в десяточных HLK до сих пор есть несколько багов, не исправленных уже несколько лет, хотя я их трудолюбиво репортил.


    1. dartraiden
      25.07.2022 18:13

      Просто режим TestSigning работал в Windows 7, но в Windows 10 всё гораздо сложнее. Необходимо сначала особым образом вызвать перезагрузку системы, тогда при старте появится специальное меню. И там, в глубине, есть один пункт, который позволяет снять требование сертификации загружаемых драйверов. На один раз. При следующей перезагрузке процедуру необходимо повторить заново.

      На постоянной основе это выполняется ровно так же, как в предыдущих версиях Windows:

      bcdedit /set testsigning on

      Перед этим придётся выключить Secure Boot, о чём система любезно подскажет при попытке выполнить команду.


    1. DSarovsky Автор
      26.07.2022 10:08

      Все-таки решил перепроверить и Вы оказались правы, если делать ровно так, как написано в статье, то будет ошибка 577. Разверну сегодня стенд, разберусь (надеюсь) и внесу правки. Скорее всего надо либо все-таки сертификат ставить с включенным режимом testsigning, либо запускать систему в особом режиме с отключенным требованием подписи драйверов. Спасибо!


      1. DSarovsky Автор
        26.07.2022 10:46

        Перепроверил на достаточно свежей 2004, все нормально, достаточно включения режима testsigning, то есть если делать как написано, то проблем не возникнет.


  1. dyadyaSerezha
    24.07.2022 18:02
    +3

    Эх, напомнило, как я в каком-то лохматом 89 или 90 году декомпилироаал драйвер клавиатуры Windows 3.1, модифицировал его, чтобы он мог печатать на русском, отладил и мы потом даже продавали его, как русификатор Windows)

    Да, и все это примерно с нулем документации по драйверам.


    1. DSarovsky Автор
      25.07.2022 00:09

      Документация — это самая боль, да. В студенчестве достаточно плотно работал с Windows, даже с учетом того, что на официальный API документация была приличная, значительную часть информации приходилось получать или путем дизассемблирования/декомпиляции, или из открытых проектов.
      Кстати, исходники ReactOS очень помогали. Хотя где-то на хабре видел комментарий одного из участников проекта о том, что им нельзя смотреть в утекшие исходники Windows, почему-то мне кажется, что настолько одинаково думать люди вряд ли умеют.


      1. ProgMiner
        26.07.2022 16:59

        Как говорится, нельзя, но если очень хочется, то можно. Публично они, конечно, заявляют, что никуда не смотрели, но даже на Хабре тема поднималась и была речь о том, что среди разработчиков там есть люди, имевшие доступ к сорцам вин хп или около того.


  1. emusic
    25.07.2022 12:47

    С VirtualKD удобно отлаживаться и в WinDbg. Заодно очень сильно возрастает скорость обмена (если драйвер выводит плотный поток отладочных сообщений, нужно извлекать из VM большие области памяти и т.п.).


    1. DSarovsky Автор
      25.07.2022 12:58

      Спасибо за наводку, бегло по описанию пробежался, если верно понял, то VirtualKD — это инструмент уровня даже не ОС, а гипервизора? То есть можно Kernel Patch Protection пытаться отладить?


      1. emusic
        25.07.2022 19:02

        Со стороны Windows он использует свой KD-модуль (ksbazis.dll), а его вроде как ядро активирует уже под гипервизором. Код VMM патчится, насколько я понимаю, только для передачи наружу событий из KD-модуля.


  1. JTG
    26.07.2022 02:18

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


    1. emusic
      26.07.2022 06:24

      "Ковыряться" в смысле "смотреть" можно тем же LiveKD, который делает моментальный снимок памяти. А чтобы трассировать собственное ядро, нужен полностью автономный отладчик вроде SoftICE, затраты на поддержку которого еще много лет назад были признаны не оправдывающими кажущихся преимуществ.