Смекни!
smekni.com

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

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

Тип параметра Описание параметра
IN HANDLE FileHandle Дескриптор открытого или модифицированного файлового объекта
IN HANDLE Event Для драйверов устройств следует указывать NULL
IN PIO_APC_ROUTINE Для драйверов устройств следует указывать NULL
IN PVOID ApcContext Для драйверов устройств следует указывать NULL
OUT PIO_STATUS_BLOCK pioStatusBlock В поле pIoStatusBlock->Information по завершении вызова находится число реально записанных байт
IN PVOID Buffer Буфер с данными для записи
IN ULONG Length Размер записываемой порции данных
IN PLARGE_INTEGER pByteOffset Указатель на переменную где содержится смещение в файле от его начала, по которому следует производить запись
IN PULONG Key Для драйверов устройств следует указывать NULL

Для закрытия дескриптора объекта следует применять функцию ZwCloseKey.

Следует отметить, что функции работы с файлами могут работать только на уровне IRQL, равном PASSIVE_LEVEL. Это приводит к необходимости применения специальной методики при протоколировании обмена данными с USB‑накопителем.

1.9 Работа с реестром в режиме ядра

Работа с реестром из драйвера уровня ядра необходима, так как именно в системном реестре хранится информация о настройках протоколирования. Информация о настройках хранится в ключе реестра, связанном с устройством, к которому подключается драйвер-фильтр. Имя этого устройства соответствует шаблону HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\USB\XXX\XXX\DeviceParameters.

Доступ ключу устройства в реестре в драйвере предоставляется функцией IoOpenDeviceRegistryKey. Перечислим ее параметры:

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

Тип параметра Описание параметра
IN PDEVICE_OBJECT DeviceObject Указатель на объект физического устройства, ключ которого должен быть открыт
IN ULONG DevInstKeyType Параметр определяющий, связан ли открываемый ключ непосредственно с физ. устройством или его программным обеспечением
IN ACCESS_MASK DesiredAccess Этот параметр определяет права доступа к ключу
OUT PHANDLE DevInstRegKey Указатель на переменную, куда следует поместить дескриптор открытого ключа

Открыв основной ключ, следует получить доступ к вложенному в него ключу с параметрами протоколирования. Для этого используется функция ZwOpenKey. Перечислим ее параметры:

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

Тип параметра Описание параметра
OUT PHANDLE KeyHandle Указатель на переменную, куда следует поместить дескриптор открытого ключа
IN ACCESS_MASK DesiredAccess Этот параметр определяет права доступа к ключу
IN POBJECT_ATTRIBUTES pObjectAttributes Указатель на заполненную вызывающим кодом структуру данных, которая при использовании в данной функции должна содержать имя открываемого ключа

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

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

Тип параметра Описание параметра
IN HANDLE KeyHandle Дескриптор ключа, которому принадлежит считываемый параметр
IN PUNICODE_STRING ValueName Строка юникод-символов, содержащая имя параметра ключа
IN KEY_VALUE_INFORMATION_CLASSKeyValueInformationClass Этот параметр принимает одно их трех значений в зависимости от полноты информации о параметре:KeyValueBasicInformationKeyValueFullInformationKeyValuePartialInformation
OUT PVOID KeyInformation Указатель на буфер, выделенный вызывающим кодом, в который должна быть помещена запрашиваемая информация
IN ULONG Length Длина предоставленного буфера
OUT PULONG ResultLength Указатель на переменную, содержащую число реально записанных в KeyInformation байт

После того, как работа с ключом реестра закончена, его дескриптор следует освободить вызовом функции ZwClose.

1.10 MDL‑списки

MDL‑список – это структура, хранящая отображение блока виртуальной памяти на физическую память. MDL‑список используется в разрабатываемом драйвере для хранения информации из URB‑пакетов, связанных с вводом / выводом USB‑устройства. Кроме того, обмен информацией с USB‑устройством в режиме прямого доступа к памяти ведется именно посредством MDL‑списков.

Перед использовании MDL‑списка в драйвере необходимо провести ряд подготовительных действий:

· выделить область в страничной памяти с помощью вызова функции
ExAllocatePool;

· вызвать функцию MmCreateMdl, создающую и инициализирующую MDL‑список;

· выполнить фиксацию страниц, описанных в MDL‑списке, в физической памяти с помощью вызова функции MmProbeAndLockPages.

После завершения использования MDL‑списка его следует освободить:

· отменить фиксацию страниц страничной памяти в оперативной памяти вызовом функции MmUnlockPages;

· очистить MDL‑список, вызвав функцию IoFreeMdl;

· освободить выделенную под список страничную память вызовом
ExFreePool.

2. Конструкторский раздел

2.1 Точки входа разрабатываемого драйвера

Разрабатываемый драйвер является драйвером нижнего уровня. В стеке драйверов USB‑накопителя он находится непосредственно под драйвером устройства, если после его загрузки не произойдет установки какого-либо другого драйвера-фильтра нижнего уровня.

Разрабатываемый драйвер включает в себя следующие точки входа:

· DriverEntry;

· AddDevice;

· DriverUnload;

· Функции обработки IRP‑пакетов:

· обработка IRP‑пакетовскодами IRP_MJ_INTERNAL_DEVICE_CONTROL – функцияDispatchInternalDeviceControl;

· обработка IRP пакетов с прочими кодами – функция DispatchRoutine.

Рассмотрим каждую из них более подробно.

2.1.1 Функция DriverEntry

В этой функции происходит регистрация всех стандартных точек входа драйвера и обработчиков IRP‑пакетов. В разрабатываемом драйвере пакеты IRPc кодами, не равными IRP_MJ_INTERNAL_DEVICE_CONTROL обрабатываются функцией DispatchRoutine.

2.1.2 Функция AddDevice

Управление этой функции передается диспетчером ввода / вывода после того, как завершает свою работу DriverEntry. AddDevice создает функциональный объект устройства с помощью вызова IoCreateDevice и подключает его к стеку драйверов выбранного устройства (вызовом IoAttachDeviceToDeviceStack). Кроме того, в этой функции производятся действия по подготовке к протоколированию: считываются настройки из системного реестра, выделяется буфер для сбора протоколируемой информации, создается лог-файл.

2.1.3 Функция DriverUnload

Функция DriverUnload необходима для того, чтобы сделать драйвер выгружаемым. В унаследованных драйверах на эту функцию возложен весь процесс выгрузки драйвера: удаление символьных ссылок, объектов устройств драйвера, отключение прерываний от объектов, освобождение выделенной памяти. В WDM‑драйверах все эти действия возложены на функцию-обработчик пакетов с кодом IRP_MJ_PNP.

2.1.4 Функция DispatchRoutine

На эту функцию возложены обязанности по обработке IRP‑пакетов с различными кодами, хотя в разрабатываемом драйвере существует необходимость в обработке только двух типов запросов. Все запросы с кодом, отличным от IRP_MJ_PNP передаются по стеку драйверов без изменений. Запросы же IRP_MJ_PNP диспетчеризуются по суб-кодам в функции PnP_Dispatch. Необходимость диспетчеризации по суб-кодам запросов IRP_MJ_PNP вызвана тем, что драйвер не должен нарушать порядка работы операционной системы и обязан подчиняться PnP‑менеджеру, то есть в драйвере должны корректно обрабатываться события старта и удаления устройства.

2.1.5 Функция DispatchInternalDeviceControl

Запросы ввода / вывода к USB‑накопителю передаются в составе IRP‑пакетов с кодом IRP_MN_INTERNAL_DEVICE_CONTROL. Этот пакет содержит полную информацию о направлении и характере передаваемых данных. То есть для протоколирования обмена информацией с USB‑носителем следует перехватывать пакеты именно этого типа.

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

Для сохранения протоколируемой информации используется, как уже было сказано в разделе 1.10, MDL‑список. Этот MDL‑список создается в функции AddDevice. Объем памяти, выделяемой под список, совпадает с максимальным размером лог-файла, задаваемым в пользовательском приложении. После создания список фиксируется в страничной памяти, что предотвращает его выгрузку на жесткий диск во время работы драйвера. После этих подготовительных действий список используется в функции DispatchInternalDeviceControl– он заполняется перехватываемой информацией.

Запись накопленного буфера в лог-файл происходит при удалении устройства.

Такая методика выбрана из-за того, что функция DispatchInternalDeviceControl работает на уровне запроса прерываний, равном DISPATCH_LEVEL, что сильно затрудняет использование механизмов синхронизации, которые могли бы позволить перейти на уровень запроса прерываний, равный PASSIVE_LEVEL, где становятся доступными функции работы с файлами. Если бы это было достигнуто в разрабатываемом драйвере, то отпала бы необходимость выделения больших объемов нестраничной памяти для хранения протокола.

Запись файла на диск в момент удаления устройства возможна, так как это событие инициализируется PnP‑менеджером, запросы которого всегда происходят на уровне IRQL, равном PASSIVE_LEVEL.

2.2 Размещение кода драйвера в памяти