Программирование драйверов Windows

       

Работа с драйвером Example.sys


Как уже было сказано, из всех возможных способов инсталляции и запуска драйвера Example.sys, ниже будет использован способ тестирования с применением тестирующего консольного приложения, которое само будет выполнять инсталляцию и удаление драйвера (прибегая к вызовам SCM Менеджера). Для поэтапного ознакомления с процессом взаимодействия драйвера и обращающегося к нему приложения рекомендуется запустить программу ExampleTest под отладчиком (например, Visual Studio) в пошаговом режиме.

Перед запуском тестирующей программы ExampleTest рекомендуется загрузить программу DebugView, чтобы в ее рабочем окне наблюдать сообщения, поступающие непосредственно из кода драйвера Example.sys (отладочной сборки).

Однако прежде чем перейти к рассмотрению сообщений от драйвера, после выполнения установки и запуска драйвера (программным кодом консольного приложения ExampleTest в пошаговом режиме), прежде следует обратиться к программам WinObj и DeviceTree или аналогичным им программным средствам для того, чтобы удостовериться, присутствует ли в них информация об установленном драйвере (как, например, после инсталляции Мастером Установки нового оборудования.)

Как уже было сказано в главе 2, протокол полученных программой DebugView отладочных сообщений драйвера можно сохранить в файле для последующего анализа. Ниже приведена информация из такого файла, отражающая события в драйвере Example.sys с момента его загрузки и вызова процедуры DriverEntry до момента выгрузки и вызова процедуры UnloadRoutine. Рассмотрим содержание этого файла подробнее.

Сообщения, отправляемые драйвером из процедуры DriverEntry (первое число — номер сообщения, второе — относительное время, не имеющие большого значения в данном случае), выглядят следующим образом:

00000000 0.00000000 =Example= In DriverEntry. 00000001 0.00003743 =Example= RegistryPath = \REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\Example. 00000002 0.00012823 =Example= FDO FF919C68, DevExt=FF919D20. 00000003 0.00021176 =Example= DriverEntry successfully completed.


В переменной RegistryPath содержится поступающий от системы путь (в формате UNICODE) внутри Системного Реестра, где можно найти информацию о запускаемом драйвере. Видим также, что созданный драйвером функциональный объект устройства (FDO) имеет адрес 80E57BE0, а недалеко от него (а именно — внутри) находится структура расширения устройства, которая была определена в файле Driver.h.

Сообщения, отправляемые драйвером из функции Create_File_IRPprocessing, обработчика запросов Диспетчера ввода/вывода, которые тот делает к драйверу в результате обращения из тестирующего приложения с вызовом CreateFile.

00000004 0.00172536 -Example- Create File is

Далее тестирующее приложение делает серию вызовов функции DeviceIoControl, в результате чего драйвер реагирует на них сообщениями для DebugView. Приводятся шестнадцатеричные значения IOCTL кодов.

Реакция на запрос IOCTL_PRINT_DEBUG_MESS:

00000005 0.00178850 -Example- In DeviceControlRoutine (fdo= FF919C68)
00000006 0.00180554 -Example- DeviceIoControl: IOCTL 222004.
00000007 0.00182230 -Example- PASSIVE_LEVEL (val=0)
00000008 0.00183515 -Example- IOCTL_PRINT_DEBUG_MESS.
00000009 0.00184940 -Example- DeviceIoControl: 0 bytes written.



Реакция на запрос IOCTL_CHANGE_IRQL:

00000010 0.00187594 -Example- In DeviceControlRoutine (fdo= FF919C68)
00000011 0.00189074 -Example- DeviceIoControl: IOCTL 222008.
00000012 0.00190331 -Example- PASSIVE_LEVEL (val=0)
00000013 0.00191477 -Example- IOCTL_CHANGE_IRQL.
00000014 0.00193125 -Example- DISPATCH_LEVEL value =2
00000015 0.00194466 -Example- IRQLs are old=2 new=10
00000016 0.00196226 -Example- DeviceIoControl: 0 bytes written.

Интересно, что попытки установить в Windows XP текущее значение IRQL, равное 25 (в середине диапазона аппаратных прерывания) не дала результата: было позволено только значение 10, что ниже границы начала аппаратных прерываний. В то же время, в Windows 98 и Windows Server 2003 (для сравнения) в этом месте выводится значение 25.

Реакция на запрос IOCTL_TOUCH_PORT_378H выглядит следующим образом:



00000017 0.00198377 -Example- In DeviceControlRoutine (fdo= FF919C68)
00000018 0.00199858 -Example- DeviceIoControl: IOCTL 222010.
00000019 0.00201115 -Example- PASSIVE_LEVEL (val=0)
00000020 0.00202344 -Example- IOCTL_TOUCH_PORT_378H.

Реакция на запрос IOCTL_SEND_BYTE_TO_USER:

00000021 0.00204104 -Example- DeviceIoControl: 0 bytes written.
00000022 0.00206870 -Example- In DeviceControlRoutine (fdo= FF919C68)
00000023 0.00208378 -Example- DeviceIoControl: IOCTL 222014.
00000024 0.00209664 -Example- PASSIVE_LEVEL (val=0)
00000025 0.00210921 -Example- Buffer outlength 1
00000026 0.00212066 -Example- Method : BUFFERED.
00000027 0.00213575 -Example- Buffer address is FF978828
00000028 0.00214944 -Example- DeviceIoControl: 1 bytes written.

Как видно из сообщений, здесь используется метод буферизации METHOD_BUFFERED, что и было описано в файле Driver.h при создании данного кода IOCTL. В соответствии с данным методом буферизации драйвер получает адрес буфера с явно системным значением (80E87E78 — адрес в системном пуле), что и должно было произойти при данном методе буферизации. Если бы в файле Driver.h при описании данного IOCTL кода был задан метод METHOD_NEITHER, то драйвер получил бы точный адрес переменной xdata из приложения ExampleTest (с типичным для пользовательского приложения адресом, типа 0002EFA0). Подробнее о том, как передаются данные при разных методах буферизации в IOCTL запросах, будет рассказано позже, в главе 6.

Размер буфера для передачи данных пользовательскому приложению равен 1. Реально переданных данных 1 байт, в то время как при обработке остальных запросов никакой передачи данных не происходит. (Размер буферных областей и переданных данных всегда исчисляется в байтах.)

Сообщения, отправляемые драйвером из функции Close_File_IRPprocessing, обработчика запросов Диспетчера ввода/вывода, которые тот делает к драйверу в результате обращения из тестирующего приложения с вызовом CloseHandle

:

00000029 0.00263888 -Example- In Close handler.

Сообщения, отправляемые драйвером из функции UnloadRoutine, обработчика запросов Диспетчера ввода/вывода, которые тот делает к драйверу при необходимости выполнить выгрузку драйвера:



00000030 0.00447794 -Example- In Unload Routine.
00000031 0.00451985 -Example- Deleted device (0) : pointer to FDO = FF919C68.
00000032 0.00454220 -Example- Deleted symlink = \DosDevices\Example.

Выполнение IOCTL запроса с кодом IOCTL_MAKE_SYSTEM_CRASH, разумеется, здесь не приводится, потому что после такого действия система, например, Windows XP начинает перезагрузку (или получает управление отладчик SoftIce, если он установлен и активирован). Однако под Windows 98 данный вызов проходит как ни в чем не бывало, и драйвер выводит в рабочем окне DebugView сообщение о том, что ему удалось получить байт данных (равный, кстати, 0) по нулевому виртуальному адресу.

Чтобы ввести элемент интриги, можно добавить, что при устранении небольшого затруднения, мешающего перехвату обращения по нулевому виртуальному адресу, в Windows NT можно наблюдать, как это действительно срабатывает и переменная 'x' остается со своим значением 0xFF. Подробнее об этом можно узнать в 10 главе.


Содержание раздела