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

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

Обработка запросов IRP стеком драйверов

Запрос ввода/вывода приходит в виде пакета IRP самому верхнему драйверу в стеке драйверов (драйверу верхнего уровня). При этом возможны следующие варианты обработки IRP:

  1. Обработка IRP полностью в драйвере верхнего уровня.
  2. После выполнения своей части обработки IRP драйвер отправляет первоначальный пакет IRP драйверу нижележащего уровня.
  3. После выполнения своей части обработки IRP драйвер создает один или несколько новых пакетов IRP и отправляет их драйверу нижележащего уровня.
  4. После выполнения своей части обработки IRP драйвер создает один или несколько новых пакетов IRP, ассоциированных с первоначальным пакетом IRP, и отправляет их драйверу нижележащего уровня.

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

Самостоятельная обработка IRP драйвером.
Если драйвер может завершить обработку пакета IRP самостоятельно, он так и должен сделать. При этом обработка может быть завершена либо сразу при поступлении запроса ввода/вывода, либо после постановки запроса ввода/вывода в очередь.

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

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

Прототип этой функции:

NTSTATUS loCallDriver (IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);

Где:

  • DeviceObject – указатель на объект-устройство, принадлежащий драйверу нижележащего уровня в стеке драйверов, которому должен быть послан запрос;
  • IRP – указатель на IRP, который будет послан драйверу нижележащего уровня.

При вызове функции IoCallDriver() Диспетчер ввода/вывода напрямую вызывает соответствующую диспетчерскую функцию требуемого драйвера. Это означает, что loCallDriver() завершится только после завершения соответствующей диспетчерской функции.

Создание новых пакетов IRP для передачи драйверу нижележащего уровня.
Вариантом передачи IRP драйверу нижележащего уровня является создание одного или нескольких новых пакетов IRP и передача этих IRP драйверу нижележащего уровня. Пакет IRP может быть создан драйвером различными способами, наиболее общим из которых является вызов функции IoAllocateIrp().

PIRP loAllocatelrp (IN CCHAR StackSize, IN BOOLEAN ChargeQuota);

Где:

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

Функция loAllocatelrp() возвращает частично инициализированный пакет IRP. По умолчанию, loAllocatelrp() предполагает, что вызванный драйвер не хочет иметь собственный Стек размещения Ввода/вывода. Драйвер, распределяющий IRP, может факультативно просить loAllocatelrp() создать IRP с достаточным количеством Стеков Размещения Ввода/вывода, которые он мог иметь один. В этом случае, драйвер должен вызвать IoSetNextIrpStackLocation() (макрокоманда в NTDDK.H), чтобы установить Стек Размещения. Драйвер может затем вызвать функцию loGetCurrentlrpStack Location().

Причина, по которой драйверу может потребоваться свой собственный Стек Размещения Ввода /вывода состоит в том, что драйвер может использовать стек для передачи информации к подпрограмме завершения. Подпрограмма Завершения (Completion Routine) обсуждается позже в этом разделе.

Как только IRP создан, драйвер может вызывать IoGetNextIrpStackLocation(), чтобы получить указатель на Стек размещения для драйвера нижележащего уровня в стеке драйверов. Затем драйвер заполняет параметры для Стека Ввода/вывода. Если для запроса требуется буфер данных, драйвер должен или установить MDL или SystemBuffer, как того требует используемый нижележащим драйвером способ передачи буферов в пакете IRP. IRP посылается нижележащему драйверу с помощью функции IoCallDriver(), как описано выше.

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