- •Лабораторная работа №4 Цель работы
- •Драйвер №6
- •Функция DriverEntry
- •Подключение фильтра к устройству:
- •Отключение фильтра от устройства
- •Обработка запроса в/в устройством-фильтром
- •Установка функции завершения в/в (CompletionRoutine)
- •Упрощенная схема организации устройств в драйвере в случае двух активных фильтров
- •Модификация диалоговой прикладной программы управления драйверами для тестирования драйвера №6
Лабораторная работа №4 Цель работы
Реализация драйвера-фильтра и управляющей программы.
Создание драйвера будет заключаться в модификации драйвера №3 (simple3) из л/р №1 и драйвера №5 (devioctl) из л/р №3 на основании информации, изложенной в лекции №9.
Создание прикладной программы будет заключаться в модификации диалоговой прикладной программы управления драйверами из л/р №2.
Драйвер №6
Имя проекта unifilt
Универсальный драйвер-фильтр.
Принцип реализации:
При загрузке драйвер должен создать эксклюзивное устройство GuiDevice, которое будет обрабатывать 2 кода управления – подключение фильтра к указанному устройству по его имени с возвращением в прикладную программу адреса объекта-устройства фильтра, и отключение фильтра по адресу объекта-устройства фильтра.
Устройство должно иметь расширение устройства (в IoCreateDevice() указывается размер структурыDeviceExtension). СтруктураDeviceExtensionопределяется разработчиком драйвера.
В нашем случае
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT TargetDeviceObject;
PFILE_OBJECT FileObject;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
Функция DriverEntry
Особенности реализации функции DriverEntryследующие:
Для того, чтобы наш фильтр успешно обрабатывал все запросы к фильтруемому устройству, он должен иметь обработчики для всех возможных диспетчерских функций. Даже если фильтр не знает, что за запрос к нему пришел, он обязан передать этот запрос фильтруемому устройству с помощью вызова IoCallDriver(). Так как мы заранее не знаем, какой набор диспетчерских функций будет у фильтруемых устройств, мы должны установить все обработчики. Сделать это можно в цикле следующим образом (см. файл ntddk.hдля понимания того, как определены константыIRP_MJ_xxx):
for(i=0; i<=IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = FilterDrvDispatch;
}
Внутри функции DriverEntry мы создаем объект-устройство (GuiDevice) для взаимодействия с нашей управляющей прикладной программой. Это устройство должно обрабатывать только коды управления в/в для создания нового устройства-фильтра, и удаления существующего устройства-фильтра. Соответственно, каждое устройство-фильтр должно обрабатывать любой пришедший к нему запрос. Обработка всех запросов и дляGuiDevice, и для устройств-фильтров, производится внутри одной и той же диспетчерской функции драйвера FilterDrvDispatch. Встает вопрос: как нам отличить запросы, предназначенные фильтруемому устройству, от запросов, предназначенных устройствуGuiDevice? Для этого мы можем в глобальной переменнойpGuiDeviceхранить адрес управляющего устройства, а в диспетчерской функции сравнивать его с адресом устройства-получателя запроса:
NTSTATUS FilterDrvDispatch(PDEVICE_OBJECT pDevObj, PIRP irp)
{
NTSTATUS ntStatus;
if(pGuiDevice==pDevObj)
/* Это наше интерфейсное устройство (GUI) */
ntStatus = GuiDevDispatch(DeviceObject, Irp);
else
/* Это устройство-фильтр */
ntStatus = FilterDevDispatch(DeviceObject, Irp);
}
Подключение фильтра к устройству:
При получении запроса от прикладной программы на подключение фильтра к устройству запрос обрабатывается устройством GuiDeviceследующим образом:
Получаем указатель на объект-устройство, к которому нужно подключить фильтр, с помощью вызова IoGetDeviceObjectPointer()
Получаем тип объекта-устройства, к которому нужно подключить фильтр (поле DeviceTypeв структуреDEVICE_OBJECT)
С помощью вызова IoCreateDevice() создаем неэксклюзивный объект-устройство, которое будет фильтром. Тип устройства указываем тот же, что и для фильтруемого устройства.
Устанавливаем для устройства-фильтра метод передачи буфера таким же, что и у фильтруемого устройства (поле Flagsв структуреDEVICE_OBJECT). При этом важно не изменить значения других уже установленных флагов в этом поле.
В поле флагов объекта-устройства фильтра сбросить флаг DO_DEVICE_INITIALIZING. Это необходимо делать всегда, если мы создаем любой объект-устройство вне функции DriverEntry. Сброс флага осуществляется с помощью строкиFilterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
Подключить объект-устройство фильтр к фильтруемому объекту-устройство с помощью вызова IoAttachDeviceToDeviceStack(). Указатель на объект-устройство, который вернет эта функция, необходимо сохранить в структуре DeviceExtension объекта-устройства фильтра.
Сохранить в структуре DeviceExtension объекта-устройства фильтра указатель на объект-файл, полученный ранее при вызове IoGetDeviceObjectPointer().