Иллюстрированный самоучитель по программированию систем защиты

Многоуровневая модель драйверов

Ранее в качестве одной из характеристик подсистемы ввода/вывода упоминалась ее многоуровневость. Что это такое?

NT позволяет выстраивать драйверы в соответствии с некоторой функциональной иерархией. При этом, например, одни драйверы имеют своей единственной целью обмен данными с некоторым физическим устройством. Что это за данные и что с ними делать, такие драйвера не знают. Другие драйвера выполняют обработку данных, но не знают в точности, как эти данные получены и как будут отправлены. Такая концепция разделения полномочий драйверов носит название многоуровневой (или послойной) модели драйверов (layered driver model), а сами драйвера – уровневыми драйверами (layered drivers).

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

В Win2000 все драйвера, считающиеся родными, будут уровневыми (для того, чтобы драйвер считался родным для Win2000, он должен как минимум поддерживать управление питанием, а для этого он должен быть уровневым). Большинство драйверов, которые в NT4 мы считали монолитными, в Win2000 будут по своей сути уровневыми. Будем выделять следующие типы драйверов:

  1. драйверы, представляющие некоторый уровень в многоуровневой архитектуре. Далее именно эти драйверы мы будем называть уровневыми драйверами;
  2. драйверы-фильтры;
  3. драйверы файловой системы (File System Driver, FSD);
  4. мини-драйверы (mini-driver).

Для каждого типа драйверов существует свой протокол реализации многоуровневой структуры. Мы рассмотрим только уровневые драйверы и драйверы-фильтры.

Реализация уровневых драйверов

Стек драйверов обычно создается самими драйверами. Корректное создание стека зависит от правильной последовательности и момента загрузки каждого драйвера из стека. Первыми должны грузиться драйвера самого нижнего уровня и т.д.

При рассмотрении организации стека драйверов необходимо понимание трех моментов:

  • объединение драйверов в стек;
  • обработка запросов IRP стеком;
  • освобождение драйверов стека.

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

Для объединения драйверов в стек обычно используется функция loGetDeviceObject Pointer(). Функция вызывается драйвером вышележащего уровня для получения указателя на объект-устройство драйвера нижележащего уровня по его имени.

Функция имеет следующий прототип:

NTSTATUS loGetDeviceObjectPointer(
IN PUNICODE_STRING ObjectName,
IN ACCESS_MASK DesiredAccess,
OUT PFILE_OBJECT FileObject,
OUT PDEVICE_OBJECT DeviceObjct);

Где:

  • ObjectName – Имя требуемого Объекта-устройства;
  • DesiredAccess – Требуемый доступ к указанному Объекту-устройству;
  • FileObject – Указатель на Объект-файл, который будет использоваться для обращения к устройству;
  • DeviceObject – Указатель на Объект-устройство с именем ObjectName.

Функция IoGetDeviceObjectPointer() принимает имя Объекта-устройства, и возвращает указатель на Объект-устройство с этим именем. Функция работает, посылая запрос CREATE на названное устройство. Этот запрос будет неудачным, если никакого устройства по имени ObjectName не существует, или вызывающая программа не может предоставить доступ, указанный в параметре DesiredAccess. Если запрос CREATE успешен, создается Объект-файл, что увеличивает счетчик ссылок Объекта-устройства, с которым связан Объект-файл.

Затем Диспетчер ввода/вывода искусственно увеличивает счетчик ссылок на Объект-файл на единицу, и посылает на устройство запрос CLOSE. В результате всего этого процесса, Объект-устройство (чей указатель возвращен в DeviceObject) не может быть удален, пока не обнулится счетчик ссылок соответствующего ему Объекта-файла. Таким образом, Объект-устройство нижнего уровня не может быть удален, в то время как драйвер вышележащего уровня имеет указатель на него.

Выделим из всего вышесказанного, что функция предусматривает в качестве выходного параметра указатель на Объект-файл специально для того, чтобы при выгрузке стека драйверов освободить устройство нижележащего уровня. Это должно быть сделано в функции Unload драйвера вышележащего уровня с помощью вызова функции ObDereferenceObject (FileObject).

После получения указателя на объект-устройство драйвера нижележащего уровня, драйвер вышележащего уровня должен установить корректное значение полей Characteristics, StackSize, Flags и AlignmentRequirement своего объекта-устройства. Поля Characteristics, Flags и AlignmentRequirement объектов-устройств всех драйверов в стеке должны совпадать, а значение поля StackSize вышележащего устройства должно быть на 1 больше значения этого поля у нижележащего устройства.

Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.