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

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

Создание новых ассоциированных пакетов IRP для передачи драйверу нижележащего уровня.
Немного другой подход к созданию нового пакета IRP для передачи драйверу нижележащего уровня состоит в создании ассоциированного пакета IRP с помощью функции loMsakeAssociatedlrp():

PIRP loMakeAssociatedlrp (IN PIRP Masterlrp, IN CCHAR StackSize);

Где:

  • StackSize – Число Стеков размещения Ввода/вывода, требуемых в IRP;
  • Masterlrp – Указатель на IRP, с которым должен быть связан создаваемый пакет IRP.

Функция loMakeAssociatedlrp() позволяет создавать IRP, которые "связаны" с некоторым "главным" IRP. Драйвер, который вызывает loMakeAssociatedlrp(), должен вручную инициализировать поле Irp.IrpCount главного IRP счетчиком ассоциированных с ним IRP, которые созданы до вызова loMakeAssociatedlrp. Ассоциированные IRP являются особым видом пакетов IRP, при завершении которых значение поля IrpCount в главном IRP уменьшается. Когда значение поля IrpCount в главном IRP становится равным нулю, Диспетчер ввода/вывода автоматически завершает главный IRP.

Ассоциированные пакеты IRP могут создаваться только драйвером высшего уровня. Драйвер высшего уровня, использующий ассоциированные пакеты IRP, может вернуть управление диспетчеру ввода/вывода после вызова IoCallDriver() для каждого из ассоциированных IRP и вызова IoMarklrpPending() для главного IRP. Если такой драйвер устанавливает процедуру завершения для созданного им ассоциированного IRP (с помощью вызова loSetCompletion Routine(), описанного ниже), Диспетчер ввода/вывода не завершит автоматически главный IRP. В этом случае процедура завершения должна напрямую завершить главный IRP посредством вызова IoCompleteRequest().

Получение драйвером вышележащего уровня уведомления о завершении обработки IRP драйвером нижележащего уровня.
В некоторых случаях драйвер вышележащего уровня может захотеть получить уведомление о завершении обработки переданного им запроса ввода/вывода драйвером нижележащего уровня(CompletionNotification). Это может быть сделано с помощью вызова функции IoSetCompletionRoutine() перед передачей пакета IRP драйверу нижележащего уровня любым указанным выше способом.

VOID loSetCompletionRoutine(IN PIRP Irp,
IN PIO_COMPLETION_ROUTINE CompletionRoutine,
INPVOID Context,
IN BOOLEAN InvokeOnSuccess,
IN BOOLEAN InvokeOnError,
IN BOOLEAN InvokeOnCahcel);

Где:

  • Irp – Указатель на IRP, при завершении которого вызывается точка входа CompletionRoutine;
  • CompletionRoutine – Указатель на точку входа драйвера, вызываемую при завершении IRP;
  • Context – определенное драйвером значение, которое нужно передать как третий параметр для точки входа CompletionRoutine;
  • InvokeOnSuccess, InvokeOnError, IwokeOnCancel – Параметры, которые, указывают должна ли точка входа CompletionRoutine быть вызвана при завершении IRP с указанным состоянием.

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

Прототип функции – точки входа драйвера:

NTSTATUS CompletionRoutine(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context);

Где:

  • DeviceObject – Указатель на объект-устройство, которому предназначался закончившийся пакет IRP;
  • IRP – Указатель на закончившийся пакет IRP;
  • Context – Определенное драйвером значение контекста, переданное, когда была вызвана функция IoSetCompletionRoutine().

При вызове IoSetCompletionRoutine() указатель на функцию завершения сохраняется в IRP в следующем после текущего Стеке Размещения ввода/вывода (то есть в Стеке Размещения ввода/вывода нижележащего драйвера). Из этого следуют два важных вывода:

  • Если драйвер установит функцию завершения для некоторого IRP и завершит этот IRP, функция завершения не будет вызвана.
  • Драйвер низшего уровня (либо монолитный драйвер) не может устанавливать функции завершения, так как, во-первых, это бессмысленно (см. предыдущий вывод), а во-вторых, это ошибка, так как следующего (за текущим) стека размещения ввода/вывода для таких драйверов не существует.

Функция завершения вызывается при том же уровне IRQL, при котором нижележащим драйвером была вызвана функция завершения обработки IRP – loComplete Request(). Это может быть любой уровень IRQL меньший либо равный IRQL_ DISPATCH_LEVEL.

Если драйвер вышележащего уровня создавал новые пакеты IRP для передачи драйверу нижележащего уровня, он обязан использовать функцию завершения для этих IRP, причем параметры InvokeOnSuccess, InvokeOnError, IwokeOnCancel должны быть установлены в TRUE. В этих случаях функция завершения должна освободить созданные драйвером IRP с помощью функции IoFreeIrp() и завершить первоначальный пакет IRP.

Требования к реализации функции завершения достаточно сложные. Эти требования можно найти в [Developing Windows NT Device Drivers, pages 481-485].

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