Смекни!
smekni.com

Протоколирование обмена информацией между компьютером и внешним запоминающим USB-устройством (стр. 4 из 7)

Рис. 1.5.1 Структура IRPпакета

Общение с USB‑накопителями в ОС WindowsNT 5 на уровне драйверов, как уже было сказано в разделе 1.3.1, происходит посредством передачи URB‑пакетов. Указатели на URB‑пакеты содержат ячейки стека IRP‑пакета, доступ к этим указателям осуществляется следующим образом:

PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);

PURB Urb = IrpSp->Parameters. Others. Argument1;

Приведем частичное объявление структуры из справочной документации Microsoft. Отметим только поля, использование которых необходимо в рамках данной курсовой работы:

typedef struct _URB {

union {

struct _URB_HEADER UrbHeader;

struct _URB_SELECT_INTERFACE UrbSelectInterface;

struct _URB_SELECT_CONFIGURATION UrbSelectConfiguration;

struct _URB_BULK_OR_INTERRUPT_TRANSFER UrbBulkOrInterruptTransfer;

}

} URB, *PURB;

Поле UrbHeader хранит информацию о коде URB‑пакета, по которому можно определить, какая операция запрашивается.

Поля UrbSelectInterface и UrbSelectConfiguration служат для запроса по выбору интерфейса и конфигурации устройства, которые будут использоваться при работе с устройством. Пакеты этой структуры отправляются хостом к устройству в начале его работы, при конфигурировании.

Поле UrbBulkOrInterruptTransfer несет наиболее важную в рамках данной курсовой работы информацию – указатели на блоки ввода / вывода USB‑устройства. Приведемописаниеструктуры_URB_BULK_OR_INTERRUPT_TRANSFER:

struct _URB_BULK_OR_INTERRUPT_TRANSFER {

struct _URB_HEADER Hdr;

USBD_PIPE_HANDLE PipeHandle;

ULONG TransferFlags;

ULONG TransferBufferLength;

PVOID TransferBuffer;

PMDLTransferBufferMDL;};

Поля этой структуры описаны в следующей таблице:

Таблица 1.5.3 Поляструктуры URB_BULK_OR_INTERRUPT_TRANSFER

Поле Описание
struct _URB_HEADER Hdr Стандартный заголовок URB‑пакета, содержащий код запроса
USBD_PIPE_HANDLE PipeHandle Дескриптор канала, на который передаются данные
ULONG TransferFlags Флаги, определяющие направление передачи данных и способ обработки ошибок
ULONG TransferBufferLength Длина передаваемого блока данных в байтах
PVOID TransferBuffer Указатель на передаваемый буфер. Буфер находится в нестраничной памяти
PMDL TransferBufferMDL Указатель на MDL‑список, несущий передаваемую информацию. Буфер находится в страничной памяти

Следует отметить, что один из указателей TransferBuffer или TransferBufferMDL равен NULL, то есть в пределах одного пакета передается только одна порция данных.

Задача протоколирования обмена информацией сводится к перехвату и сохранению буферов TransferBuffer и TransferBufferMDL.

1.6 Уровни запроса прерываний

В каждый момент времени центральный процессор находится на одном из уровней IRQL (InterruptRequestLevel– уровень запросов прерываний). Уровни IRQL располагаются в порядке убывания от HIGHEST_LEVEL до PASSIVE_LEVEL. Каждому из прерываний (прерывания от внешних устройств, системные часы, и т.д.) соответствует свой уровень IRQL. Специальным действиям операционной системы также назначены IRQL. Они отмечены в нижней части приведённой таблицы:

Таблица 1.6.1. Уровни запросов прерываний.

Уровень Назначение
HIGHEST_LEVEL Наивысший уровень. Все прерывания заблокированы
POWER_LEVEL Прерывания по отказу питания
IPI_LEVEL Межпроцессорное взаимодействие
CLOCK2_LEVEL Прерывание по системному таймеру 2
СLOCK1_LEVEL Прерывание по системному таймеру 1
PROFILE_LEVEL Прерывание по таймеру замера производительности
уровни DRQL Обычные прерывания устройств
DISPATCH_LEVEL Диспетчеризация потоков и выполнение отложенных процедур
APC_LEVEL Выполнение асинхронного вызова процедуры
PASSIVE_LEVEL Обычное исполнение кода потока

Общее правило обработки уровней запросов прерываний гласит, что прерывания с IRQL, меньшим, чем у выполняемого в данный момент кода, маскируются. Во время исполнения кода потока (пользовательского или системного) устанавливается наименьший IRQL = 0 (PASSIVE_LEVEL). Работа драйвера чаще всего выполняется на уровне IRQL = 2 (DISPATCH_LEVEL). Уровни, лежащие над ним, называются DIRQL (DeviceIRQL) и выставляются для обработчиков прерываний от внешних устройств (ISR– interruptserviceroutine). Даже во время выполнения ISR драйвера может произойти прерывание с большим IRQL, например, принадлежащее другому драйверу.

Чем выше текущий уровень IRQL исполняемого кода, тем меньше функций ему доступно. Так, например, диспетчер потоков работает на уровне
DISPATCH_LEVEL, и, следовательно, не будет вызываться, пока на процессоре с уровнем большим или равным DISPATCH_LEVEL исполняется другой код. Таким образом, на уровнях DISPATCH_LEVEL и выше отключается переключение потоков. Функции ожидания диспетчерских объектов (события, мьютексы, семафоры) с отличным от нуля временем, обращение к файлам, подкачка отсутствующих в физической памяти страниц – всё это также становится недоступным. Для корректного сохранения запросов в файле фильтр в таких случаях должен применять специальную методику.

1.7 Уведомление о завершении запросанижестоящим драйвером

При отслеживании обмена данными драйвер-фильтр может получать уведомления о том, что некоторый переданный запрос был завершён нижестоящим драйвером. Механизм уведомления заключается в том, что вызовом специальной функции IoSetCompletionRoutine фильтр обращается к стеку в пакете IRP. В позиции стека, следующей за текущей позицией, он устанавливает в специальном поле адрес функции завершения (completionroutine). Затем при передаче пакета по цепочке позиция стека увеличивается.

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

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

При третьем же варианте просмотр стека немедленно прекращается и запрос не будет завершён. Эта возможность реализована для того, чтобы драйвер-фильтр мог выполнить какие-либо действия над пакетом запроса после того, как тот будет обработан в нижестоящем драйвере. После такой «дополнительной обработки» пакет снова должен быть отправлен на завершение.

1.8 Работа с файлами в режиме ядра

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

Для открытия файла из драйвера режима ядра используется универсальная функция ZwCreateFile. Универсальность этой функции состоит в том, что с ее помощью производится и открытие существующих файлов, и создание новых.

Специфика системной функции ZwCreateFile состоит в том, что она имеет протокольных параметров даже больше, чем пользовательский вызов CreateFile. Существенная часть входной информации об открываемом объекте поступает внутри структуры OBJECT_ATTRIBURTES, которую следует предварительно создать и заполнить соответствующими конкретными данными. Для ведения учетной информации открытого объекта используется структура данных IO_STATUS_BLOCK, которую следует предоставить при вызове (инициализировать ее не следует).

Представим основные параметры функции ZwCreateFile в следующей таблице:

Таблица 1.8.1. Параметры функции ZwCreateFile

Тип параметра Описание параметра
OUT PHANDLE pHandle Указатель на переменную, куда следует поместить дескриптор открытого объекта
IN ACCESS_MASK DesiredAccess Характеристика доступа к объекту. Для фалов чаще всего используются значенияGENERIC_READ или GENERIC_WRITE
IN POBJECT_ATTRIBUTESpObjAttributes Указатель на заполненную вызывающим кодом структуру данных, которая описывает имя, местоположение и некоторые другие характеристики открываемого объекта
OUT PIO_STATUS_BLOCK pIOStatus Указатель на буфер, в котором будет размещена информация об открытом объекте в формате структуры IO_STATUS_BLOCK
IN PLARGE_INTEGER AllocationSize Начальный размер файла в байтах. Ненулевое значение принимается во внимание только при создании и перезаписи файла
IN ULONG FileAttributes Атрибуты открываемого файла. Типовым является значение FILE_ATTRIBUTE_NORMAL
IN ULONG SharedAccessFlags Описывает, разрешен ли совместный доступ, например, FILE_SHARE_READ – для чтения
IN ULONG CreateDispositionFlags Способ открытия файла, например, FILE_OPEN_IF – если не существует, создать
IN ULONG CreateOptions Комбинация флагов создания, например, FILE_SYNCHONOUS_IO_NONALERT – все операции над файлом выполняются как синхронные (DesiredAccess должен включать флаг SYNCHRONIZE)
IN PVOID EaBuffer Для драйверов устройств следует указывать NULL
IN ULONG EaLength Для драйверов устройств следует указывать 0

Для заполнения структуры атрибутов объекта используется функция
InitializeObjectAttributes. Опишем ее параметры в следующей таблице:

Таблица 1.8.2. Параметры функции InitializeObjectAttributes

Тип параметра Описание параметра
OUT POBJECT_ATTRIBUTES pObjAttributes Указатель на переменную, куда следует поместить атрибуты объекта
IN PUNICODE_STRING ObjectName Имя объекта, HANDLE которого создается
IN ULONG Attributes Флаги атрибутов объекта, при открытии файла как правило используются флаги OBJ_CASE_INSENSITIVE и OBJ_KERNEL_HANDLE
IN HANDLE RootDirectory Дескриптор корневой директории для объекта, описатель атрибутов которого создается. Если ObjectName полностью описывает путь к объекту, то значению RootDirectory присваивается NULL
IN PSECURITY_DESCRIPTOR SecurityDescriptor Дескриптор безопасности. Если указано NULL, то применяется стандартный дескриптор

Запись в файл выполняется системной функцией ZwWriteFile: