РПЗ (774931), страница 4
Текст из файла (страница 4)
При третьем же варианте просмотр стека немедленно прекращается и запрос не будет завершён. Эта возможность реализована для того, чтобы драйвер-фильтр мог выполнить какие-либо действия над пакетом запроса после того, как тот будет обработан в нижестоящем драйвере. После такой «дополнительной обработки» пакет снова должен быть отправлен на завершение.
Работа с файлами в режиме ядра
Поскольку протоколируемая информация должна сохраняться в файле на диске, следует рассмотреть основные функции уровня ядра, используемые при работе с файлами.
Для открытия файла из драйвера режима ядра используется универсальная функция ZwCreateFile. Универсальность этой функции состоит в том, что с ее помощью производится и открытие существующих файлов, и создание новых.
Специфика системной функции ZwCreateFile состоит в том, что она имеет протокольных параметров даже больше, чем пользовательский вызов CreateFile. Существенная часть входной информации об открываемом объекте поступает внутри структуры OBJECT_ATTRIBURTES, которую следует предварительно создать и заполнить соответствующими конкретными данными. Для ведения учетной информации открытого объекта используется структура данных IO_STATUS_BLOCK, которую следует предоставить при вызове (инициализировать ее не следует).
Представим основные параметры функции ZwCreateFile в следующей таблице:
Параметры функции ZwCreateFile.
Тип параметра | Описание параметра |
OUT PHANDLE pHandle | Указатель на переменную, куда следует поместить дескриптор открытого объекта |
IN ACCESS_MASK DesiredAccess | Характеристика доступа к объекту. Для фалов чаще всего используются значения GENERIC_READ или GENERIC_WRITE |
IN POBJECT_ATTRIBUTES pObjAttributes | Указатель на заполненную вызывающим кодом структуру данных, которая описывает имя, местоположение и некоторые другие характеристики открываемого объекта |
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. Опишем ее параметры в следующей таблице:
Параметры функции InitializeObjectAttributes.
Тип параметра | Описание параметра |
OUT POBJECT_ATTRIBUTES | Указатель на переменную, куда следует поместить атрибуты объекта |
IN PUNICODE_STRING ObjectName | Имя объекта, HANDLE которого создается |
IN ULONG Attributes | Флаги атрибутов объекта, при открытии файла как правило используются флаги OBJ_CASE_INSENSITIVE и OBJ_KERNEL_HANDLE |
IN HANDLE RootDirectory | Дескриптор корневой директории для объекта, описатель атрибутов которого создается. Если ObjectName полностью описывает путь к объекту, то значению RootDirectory присваивается NULL |
IN PSECURITY_DESCRIPTOR | Дескриптор безопасности. Если указано NULL, то применяется стандартный дескриптор |
Запись в файл выполняется системной функцией ZwWriteFile:
Параметры функции 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-накопителем.
Работа с реестром в режиме ядра
Работа с реестром из драйвера уровня ядра необходима, так как именно в системном реестре хранится информация о настройках протоколирования. Информация о настройках хранится в ключе реестра, связанном с устройством, к которому подключается драйвер-фильтр. Имя этого устройства соответствует шаблону HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\USB\XXX\XXX\DeviceParameters.
Доступ ключу устройства в реестре в драйвере предоставляется функцией
IoOpenDeviceRegistryKey. Перечислим ее параметры:
Параметры функции IoOpenDeviceRegistry.
Тип параметра | Описание параметра |
IN PDEVICE_OBJECT DeviceObject | Указатель на объект физического устройства, ключ которого должен быть открыт |
IN ULONG DevInstKeyType | Параметр определяющий, связан ли открываемый ключ непосредственно с физ. устройством или его программным обеспечением |
IN ACCESS_MASK DesiredAccess | Этот параметр определяет права доступа к ключу |
OUT PHANDLE DevInstRegKey | Указатель на переменную, куда следует поместить дескриптор открытого ключа |
Открыв основной ключ, следует получить доступ к вложенному в него ключу с параметрами протоколирования. Для этого используется функция ZwOpenKey. Перечислим ее параметры:
Параметры функции 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_CLASS KeyValueInformationClass | Этот параметр принимает одно их трех значений в зависимости от полноты информации о параметре: KeyValueBasicInformation KeyValueFullInformation KeyValuePartialInformation |
OUT PVOID KeyInformation | Указатель на буфер, выделенный вызывающим кодом, в который должна быть помещена запрашиваемая информация |
IN ULONG Length | Длина предоставленного буфера |
OUT PULONG ResultLength | Указатель на переменную, содержащую число реально записанных в KeyInformation байт |
После того, как работа с ключом реестра закончена, его дескриптор следует освободить вызовом функции ZwClose.
MDL-списки
MDL-список – это структура, хранящая отображение блока виртуальной памяти на физическую память. MDL-список используется в разрабатываемом драйвере для хранения информации из URB-пакетов, связанных с вводом/выводом USB-устройства. Кроме того, обмен информацией с USB-устройством в режиме прямого доступа к памяти ведется именно посредством MDL-списков.
Перед использовании MDL-списка в драйвере необходимо провести ряд подготовительных действий:
-
выделить область в страничной памяти с помощью вызова функции
ExAllocatePool; -
вызвать функцию MmCreateMdl, создающую и инициализирующую MDL-список;
-
выполнить фиксацию страниц, описанных в MDL-списке, в физической памяти с помощью вызова функции MmProbeAndLockPages.
После завершения использования MDL-списка его следует освободить:
-
отменить фиксацию страниц страничной памяти в оперативной памяти вызовом функции MmUnlockPages;
-
очистить MDL-список, вызвав функцию IoFreeMdl;
-
освободить выделенную под список страничную память вызовом
ExFreePool.
Конструкторский раздел
Точки входа разрабатываемого драйвера
Разрабатываемый драйвер является драйвером нижнего уровня. В стеке драйверов USB-накопителя он находится непосредственно под драйвером устройства, если после его загрузки не произойдет установки какого-либо другого драйвера-фильтра нижнего уровня.
Разрабатываемый драйвер включает в себя следующие точки входа:
-
DriverEntry;
-
AddDevice;
-
DriverUnload;
-
Функции обработки IRP-пакетов:
-
обработка IRP-пакетов с кодами IRP_MJ_INTERNAL_DEVICE_CONTROL – функция DispatchInternalDeviceControl;
-
обработка IRP пакетов с прочими кодами – функция DispatchRoutine.
Рассмотрим каждую из них более подробно.
Описание функций
Функция DriverEntry