Написание Hello World драйвера Windows (KMDF)
В этой статье описывается, как написать небольшой универсальный драйвер Windows с помощью Kernel-Mode Driver Framework (KMDF), а затем развернуть и установить драйвер на отдельном компьютере.
Прежде чем продолжить, выполните действия по установке, перечисленные в разделе Скачивание пакета драйверов Windows (WDK).
Средства отладки для Windows включаются при установке WDK.
Создание и сборка драйвера
Откройте среду Microsoft Visual Studio. В меню Файл выберите Пункт Создать > проект.
В диалоговом окне Создание проекта выберите C++ в раскрывающемся списке слева, в среднем раскрывающемся списке выберите Windows , а в раскрывающемся списке справа выберите Драйвер .
Выберите Драйвер режима ядра, пустой (KMDF) из списка типов проектов. Выберите Далее.
В диалоговом окне Настройка нового проекта введите "KmdfHelloWorld" в поле Имя проекта .
Примечание
При создании нового драйвера KMDF или UMDF необходимо выбрать имя драйвера, которое содержит не более 32 символов. Это ограничение длины определяется в wdfglobals.h.
В поле Расположение введите каталог, в котором вы хотите создать проект.
Установите флажок Разместить решение и проект в одном каталоге и выберите Создать.
Visual Studio создает один проект и решение. Вы можете просмотреть их в обозревателе решений. (Если окно Обозреватель решений не отображается, выберите Обозреватель решений в меню Вид.) Решение содержит проект драйвера с именем KmdfHelloWorld.
В окне Обозреватель решений выберите и удерживайте (или щелкните правой кнопкой мыши) решение KmdfHelloWorld и выберите Configuration Manager. Выберите конфигурацию и платформу для проекта драйвера. Например, выберите Отладка и x64.
В окне Обозреватель решений снова выберите и удерживайте (или щелкните правой кнопкой мыши) проект KmdfHelloWorld, выберите Добавить, а затем новый элемент.
В диалоговом окне Добавление нового элемента выберите Файл C++. В поле Имя введите Driver.c.
Примечание
Расширение имени файла — .c, а не .cpp.
Выберите Добавить. Файл Driver.c будет добавлен в раздел Исходные файлы, как показано здесь.
Написание первого кода драйвера
Теперь, когда вы создали пустой проект Hello World и добавили исходный файл Driver.c, вы напишете самый простой код, необходимый для запуска драйвера, реализовав две основные функции обратного вызова событий.
В Driver.c начните с включения следующих заголовков:
#include <ntddk.h> #include <wdf.h>
Совет
Если вы не можете добавить
Ntddk.h
, откройте раздел Конфигурация —> C/C++ —> Общие —> Дополнительные каталоги включения и добавьтеC:\Program Files (x86)\Windows Kits\10\Include\<build#>\km
, заменив<build#>
соответствующим каталогом в установке WDK.Ntddk.h содержит основные определения ядра Windows для всех драйверов, а Wdf.h — определения драйверов на основе Платформы драйверов Windows (WDF).
Затем укажите объявления для двух используемых обратных вызовов:
DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd;
Используйте следующий код для написания DriverEntry:
NTSTATUS DriverEntry( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { // NTSTATUS variable to record success or failure NTSTATUS status = STATUS_SUCCESS; // Allocate the driver configuration object WDF_DRIVER_CONFIG config; // Print "Hello World" for DriverEntry KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: DriverEntry\n" )); // Initialize the driver configuration object to register the // entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAdd WDF_DRIVER_CONFIG_INIT(&config, KmdfHelloWorldEvtDeviceAdd ); // Finally, create the driver object status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE ); return status; }
DriverEntry — это точка входа для всех драйверов, как
Main()
и для многих приложений в пользовательском режиме. Задача DriverEntry — инициализировать структуры и ресурсы на уровне драйвера. В этом примере вы напечатали "Hello World" для DriverEntry, настроили объект driver для регистрации точки обратного вызова EvtDeviceAdd, а затем создали объект драйвера и вернули его.Объект драйвера выступает в качестве родительского объекта для всех других объектов платформы, которые можно создать в драйвере, включая объекты устройств, очереди ввода-вывода, таймеры, спин-блокировки и многое другое. Дополнительные сведения об объектах платформы см. в статье Общие сведения об объектах платформы.
Совет
Для DriverEntry настоятельно рекомендуется оставить имя DriverEntry, чтобы упростить анализ кода и отладку.
Затем используйте следующий код для записи kmdfHelloWorldEvtDeviceAdd:
NTSTATUS KmdfHelloWorldEvtDeviceAdd( _In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit ) { // We're not using the driver object, // so we need to mark it as unreferenced UNREFERENCED_PARAMETER(Driver); NTSTATUS status; // Allocate the device object WDFDEVICE hDevice; // Print "Hello World" KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd\n" )); // Create the device object status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &hDevice ); return status; }
Функция EvtDeviceAdd вызывается системой, когда обнаруживает, что устройство прибыло. Его задача заключается в инициализации структур и ресурсов для этого устройства. В этом примере вы просто распечатали сообщение "Hello World" для EvtDeviceAdd, создали объект устройства и вернули его. В других записываемых вами драйверах можно создавать очереди ввода-вывода для оборудования, настраивать хранилище контекста устройства для сведений, относящихся к устройству, или выполнять другие задачи, необходимые для подготовки устройства.
Совет
Чтобы добавить обратный вызов устройства, обратите внимание, как вы назвали его с именем драйвера в качестве префикса (KmdfHelloWorldEvtDeviceAdd). Как правило, рекомендуется именовать функции драйвера таким образом, чтобы отличать их от функций других драйверов. DriverEntry — это единственное имя.
Полный файл Driver.c теперь выглядит следующим образом:
#include <ntddk.h> #include <wdf.h> DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_DEVICE_ADD KmdfHelloWorldEvtDeviceAdd; NTSTATUS DriverEntry( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { // NTSTATUS variable to record success or failure NTSTATUS status = STATUS_SUCCESS; // Allocate the driver configuration object WDF_DRIVER_CONFIG config; // Print "Hello World" for DriverEntry KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: DriverEntry\n" )); // Initialize the driver configuration object to register the // entry point for the EvtDeviceAdd callback, KmdfHelloWorldEvtDeviceAdd WDF_DRIVER_CONFIG_INIT(&config, KmdfHelloWorldEvtDeviceAdd ); // Finally, create the driver object status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE ); return status; } NTSTATUS KmdfHelloWorldEvtDeviceAdd( _In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit ) { // We're not using the driver object, // so we need to mark it as unreferenced UNREFERENCED_PARAMETER(Driver); NTSTATUS status; // Allocate the device object WDFDEVICE hDevice; // Print "Hello World" KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfHelloWorld: KmdfHelloWorldEvtDeviceAdd\n" )); // Create the device object status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &hDevice ); return status; }
Сохраните Driver.c.
В этом примере показана основная концепция драйверов: это "коллекция обратных вызовов", которые после инициализации находятся и ожидают, пока система вызовет их, когда ей что-то нужно. Системный вызов может быть событием поступления нового устройства, запросом ввода-вывода из приложения пользовательского режима, событием выключения системы питания, запросом от другого драйвера или событием неожиданного удаления, когда пользователь неожиданно отключает устройство. К счастью, чтобы сказать "Hello World", вам нужно только беспокоиться о создании драйвера и устройства.
Далее вы создадите драйвер.
Сборка драйвера
В окне Обозреватель решений выберите и удерживайте (или щелкните правой кнопкой мыши) Решение KmdfHelloWorld (1 проект) и выберите Configuration Manager. Выберите конфигурацию и платформу для проекта драйвера. Для этого упражнения мы выбираем Отладка и x64.
В окне Обозреватель решений выберите и удерживайте (или щелкните правой кнопкой мыши) KmdfHelloWorld и выберите Свойства. В разделе Параметры трассировки > Wpp установите для параметра Запустить трассировку Wpp значение Нет. Нажмите кнопку Apply (Применить), а затем — ОК.
Чтобы выполнить сборку драйвера, выберите в меню Сборкапункт Сборка решения. Visual Studio отображает ход выполнения сборки в окне Вывода . (Если окно Вывод не отображается, выберите Вывод в меню Вид .) Убедився, что решение успешно создано, можно закрыть Visual Studio.
Чтобы просмотреть встроенный драйвер, в проводник перейдите в папку KmdfHelloWorld, а затем в папку x64\Debug\KmdfHelloWorld. В папку входят:
- KmdfHelloWorld.sys — файл драйвера в режиме ядра.
- KmdfHelloWorld.inf — информационный файл, который Windows использует при установке драйвера.
- KmdfHelloWorld.cat — файл каталога, используемый установщиком для проверки тестовой сигнатуры драйвера.
Совет
Если вы видите DriverVer set to a date in the future
при сборке драйвера, измените параметры проекта драйвера, чтобы Inf2Cat установил ./uselocaltime
Для этого используйте свойства конфигурации-Inf2Cat-General-Use>>> Local Time. Теперь и Stampinf , и Inf2Cat используют местное время.
Развертывание драйвера
Обычно при тестировании и отладке драйвера отладчик и драйвер выполняются на разных компьютерах. Компьютер, на котором выполняется отладчик, называется главным компьютером, а компьютер, на котором выполняется драйвер, — конечным компьютером. Целевой компьютер также называется тестовый компьютер.
До сих пор вы использовали Visual Studio для создания драйвера на хост-компьютере. Теперь необходимо настроить целевой компьютер.
Следуйте инструкциям в статье Подготовка компьютера для развертывания и тестирования драйвера (WDK 10).
Совет
При выполнении действий по автоматической подготовке целевого компьютера с помощью сетевого кабеля запишите порт и ключ. Они будут использоваться позже на этапе отладки. В этом примере мы будем использовать 50000 в качестве порта и 1.2.3.4 в качестве ключа.
В сценариях отладки реальных драйверов рекомендуется использовать ключ, созданный KDNET. Дополнительные сведения об использовании KDNET для создания случайного ключа см. в разделе Пошаговая лабораторная работа драйверов отладки (режим ядра Sysvad).
На хост-компьютере откройте решение в Visual Studio. Вы можете дважды щелкнуть файл решения KmdfHelloWorld.sln в папке KmdfHelloWorld.
В окне Обозреватель решений выберите и удерживайте (или щелкните правой кнопкой мыши) проект KmdfHelloWorld и выберите Свойства.
В окне Страницы свойств KmdfHelloWorld перейдите к разделу Свойства > конфигурации Установка развертывания драйвера>, как показано ниже.
Установите флажок Удалить предыдущие версии драйверов перед развертыванием.
В поле Имя целевого устройства выберите имя компьютера, настроенного для тестирования и отладки. В этом упражнении мы используем компьютер с именем MyTestComputer.
Выберите Hardware ID Driver Update (Обновление драйвера и введите идентификатор оборудования для драйвера). В этом упражнении используется идентификатор оборудования Root\KmdfHelloWorld. Щелкните ОК.
Примечание
В этом упражнении идентификатор оборудования не идентифицирует реальную часть оборудования. Он идентифицирует мнимое устройство, которому будет предоставлено место в дереве устройств в качестве дочернего элемента корневого узла. Для реального оборудования не выбирайте обновление драйвера идентификатора оборудования; Вместо этого выберите Установить и проверить. Вы увидите идентификатор оборудования в файле сведений о драйвере (INF). В окне Обозреватель решений перейдите в раздел Файлы драйверов KmdfHelloWorld >и дважды щелкните файл KmdfHelloWorld.inf. Идентификатор оборудования находится в разделе [Standard.NT$ARCH$].
[Standard.NT$ARCH$] %KmdfHelloWorld.DeviceDesc%=KmdfHelloWorld_Device, Root\KmdfHelloWorld
В меню Сборка выберите Развернуть решение. Visual Studio автоматически копирует файлы, необходимые для установки и запуска драйвера, на целевой компьютер. Развертывание может занять одну или две минуты.
При развертывании драйвера файлы драйвера копируются в папку %Systemdrive%\drivertest\drivers на тестовом компьютере. Если во время развертывания что-то пойдет не так, можно проверка, чтобы узнать, копируются ли файлы на тестовый компьютер. Убедитесь, что файлы INF, CAT, test cert и .sys, а также все необходимые файлы находятся в папке %systemdrive%\drivertest\drivers.
Дополнительные сведения о развертывании драйверов см. в разделе Развертывание драйвера на тестовом компьютере.
Установка драйвера
Теперь, когда драйвер Hello World развернут на целевом компьютере, вы установите драйвер. Когда вы ранее подготовили целевой компьютер с помощью Visual Studio с помощью автоматического параметра, Visual Studio настроила целевой компьютер для запуска тестовых подписанных драйверов в рамках процесса подготовки. Теперь необходимо просто установить драйвер с помощью средства DevCon.
На хост-компьютере перейдите в папку Сервис в установке WDK и найдите средство DevCon. Например, найдите следующую папку:
C:\Program Files (x86)\Windows Kits\10\Tools\x64\devcon.exe
Скопируйте средство DevCon на удаленный компьютер.
На целевом компьютере установите драйвер, перейдя в папку, содержащую файлы драйверов, а затем запустив средство DevCon.
Ниже приведен общий синтаксис средства devcon, который будет использоваться для установки драйвера:
Devcon install <INF file><hardware ID>
Для установки этого драйвера требуется INF-файл KmdfHelloWorld.inf. INF-файл содержит идентификатор оборудования для установки двоичного файла драйвера ,KmdfHelloWorld.sys. Помните, что идентификатор оборудования, расположенный в INF-файле, имеет значение Root\KmdfHelloWorld.
Откройте окно командной строки от имени администратора. Перейдите в папку, содержащую файл встроенного драйвера .sys, и введите следующую команду:
devcon install kmdfhelloworld.inf root\kmdfhelloworld
Если появляется сообщение об ошибке о том, что devcon не распознается, попробуйте добавить путь к средству devcon . Например, если вы скопировали его в папку на целевом компьютере C:\Tools, попробуйте использовать следующую команду:
c:\tools\devcon install kmdfhelloworld.inf root\kmdfhelloworld
Появится диалоговое окно, указывающее, что тестовый драйвер является неподписанным драйвером. Выберите Установить этот драйвер в любом случае , чтобы продолжить.
Отладка драйвера
Теперь, когда вы установили драйвер KmdfHelloWorld на целевом компьютере, вы подключите отладчик удаленно с главного компьютера.
На хост-компьютере откройте окно командной строки с правами администратора. Перейдите в каталог WinDbg.exe. Мы будем использовать x64version WinDbg.exe из комплекта драйверов Windows (WDK), который был установлен в рамках установки комплекта Windows. Ниже приведен путь по умолчанию к WinDbg.exe:
C:\Program Files (x86)\Windows Kits\10\Debuggers\x64
Запустите WinDbg, чтобы подключиться к сеансу отладки ядра на целевом компьютере с помощью следующей команды. Значения порта и ключа должны совпадать с значениями, которые использовались для подготовки целевого компьютера. Мы будем использовать 50000 для порта и 1.2.3.4 для ключа, значения, которые использовались на этапе развертывания. Флаг k указывает, что это сеанс отладки ядра.
WinDbg -k net:port=50000,key=1.2.3.4
В меню Отладка выберите Пункт Прервать. Отладчик на хост-компьютере войдет в целевой компьютер. В окне Команда отладчика отображается командная строка отладки ядра: kd>.
На этом этапе можно поэкспериментировать с отладчиком, введя команды в командной строке kd> . Например, можно попробовать следующие команды:
Чтобы снова запустить целевой компьютер, выберите Перейти в меню Отладка или нажмите клавишу "g", а затем нажмите клавишу "ВВОД".
Чтобы остановить сеанс отладки, выберите команду Отсоединить отладчик в меню Отладка .
Важно!
Убедитесь, что вы используете команду go, чтобы позволить целевому компьютеру снова запуститься перед выходом из отладчика, иначе целевой компьютер не будет отвечать на ввод с помощью мыши и клавиатуры, так как он по-прежнему взаимодействует с отладчиком.
Подробное пошаговое руководство по процессу отладки драйверов см. в разделе Отладка универсальных драйверов — пошаговая лабораторная работа (режим ядра Echo).
Дополнительные сведения об удаленной отладке см. в статье Удаленная отладка с помощью WinDbg.
Похожие статьи
Отладка универсальных драйверов — пошаговая лабораторная работа (режим ядра Echo)