Драйверы режима ядра: Часть 11 : Базовая техника: Каталоги и файлы — Архив WASM.RU
Обеспечение возможности работы с файлами является важнейшей задачей любой операционной системы. Посмотрим, какие возможности предоставляют нам операционные системы семейства NT.
11.1 Таблица описателей ядра
Прежде чем мы перейдем непосредственно к теме статьи, обсудим один важный момент, которому прежде не было уделено должного внимания. Для получения описателя объекта требуется заполнить структуру OBJECT_ATTRIBUTES - это мы делали уже много раз примерно таким образом:
Код (Text):
InitializeObjectAttributes addr oa, addr g_usName, OBJ_CASE_INSENSITIVE, NULL, NULLИнициализировав структуру OBJECT_ATTRIBUTES, мы вызывали функцию создания/открытия объекта и получали его описатель (hande). Но описатель этот попадал в таблицу описателей того процесса, в контексте которого был получен. Т.к. таблица описателей специфична для процесса, то использовать такой описатель можно только в контексте того же самого процесса. Например, попытка закрыть этот описатель в контексте другого процесса, в лучшем случае, завершится неудачно. А в худшем, если в таблице описателей этого процесса окажется описатель с таким же значением, ведь описатель это просто 32-битное число (точнее битовая структура), возможно, будет закрыт описатель совершенно другого объекта. Даже если описатель получен драйвером, но в контексте пользовательского процесса, он попадет в таблицу описателей этого процесса и по нему можно будет обратиться к объекту из режима пользователя, преднамеренно или случайно. Бывают ситуации, когда именно это и требуется, но в общем случае это не то, что вы хотите. Именно поэтому компоненты ядра, и драйверы в частности, не "любят" пользоваться описателями, а предпочитают использовать ссылки на объекты (reference to object), которые являются простыми указателями на структуру объекта в памяти ядра. Для учета предоставленных ссылок на объект в заголовке каждого объекта хранится счетчик ссылок (reference count). Если же для доступа к объекту нужен именно его описатель, как в этом и предыдущем примере, и вы планируете обращаться к нему из разных контекстов, нужно заставить систему поместить описатель в так называемую таблицу описателей ядра (kernel handle table).
Начиная с Windows 2000, в системе появилась специальная таблица описателей ядра. Описатели в этой таблице доступны только в режиме ядра в контексте любого процесса и отличаются от описателей специфичных для процесса установленным старшим битом.
Даже если получить описатель в контексте процесса System, например, в процедуре DriverEntry, то обратиться по нему к объекту в контексте пользовательского процесса нельзя. Т.е. процесс System использует свою собственную таблицу описателей, отличную от таблицы описателей ядра.
Для того чтобы описатель попал в таблицу описателей ядра, нужно явно указать флаг OBJ_KERNEL_HANDLE в вызове макроса InitializeObjectAttributes таким образом:
Код (Text):
InitializeObjectAttributes addr oa, addr g_usName, OBJ_KERNEL_HANDLE, NULL, NULL
11.2 Исходный текст драйвера FileWorks
Также как и в предыдущем примере, код драйвера состоит из нескольких автономных процедур: CreateDirectory, CreateFile, WriteFile, MarkAsReadOnly, ReadFile, UnmarkAsReadOnly, AppendFile, TruncateFile, DeleteFile, DeleteDirectory и EnumerateFiles. Все они роботают почти независимо друг от друга.
Код (Text):
;@echo off ;goto make ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ; FileWorks - Пример различных операций с файлами. ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .386 .model flat, stdcall option casemap:none ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; В К Л Ю Ч А Е М Ы Е Ф А Й Л Ы ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: include \masm32\include\w2k\ntstatus.inc include \masm32\include\w2k\ntifs.inc include \masm32\include\w2k\ntoskrnl.inc includelib \masm32\lib\w2k\ntoskrnl.lib include \masm32\Macros\Strings.mac ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; Н Е И З М Е Н Я Е М Ы Е Д А Н Н Ы Е ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .const CCOUNTED_UNICODE_STRING "\\??\\c:\\FileWorks\\test.txt", g_usFileName, 4 CCOUNTED_UNICODE_STRING "\\??\\c:\\FileWorks", g_usDirName, 4 ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; К О Д ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: .code ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; CreateDirectory ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: CreateDirectory proc local oa:OBJECT_ATTRIBUTES local iosb:IO_STATUS_BLOCK local hDirectory:HANDLE ; Помни, что коды форматирования Unicode (%C, %S, %lc, %ls, %wc, %ws, %wZ), передаваемые в ; функцию DbgPrint могут быть использованы только на IRQL = PASSIVE_LEVEL! invoke DbgPrint, $CTA0("\nFileWorks: Creating %ws directory\n"), g_usDirName.Buffer InitializeObjectAttributes addr oa, addr g_usDirName, \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL invoke ZwCreateFile, addr hDirectory, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \ 0, FILE_OPEN_IF, FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 .if eax == STATUS_SUCCESS .if iosb.Information == FILE_CREATED invoke DbgPrint, $CTA0("FileWorks: Directory created\n") .elseif iosb.Information == FILE_OPENED invoke DbgPrint, $CTA0("FileWorks: Directory exists and was opened\n") .endif invoke ZwClose, hDirectory .else invoke DbgPrint, $CTA0("FileWorks: Can't create directory. Status: %08X\n"), eax .endif ret CreateDirectory endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; CreateFile ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: CreateFile proc local oa:OBJECT_ATTRIBUTES local iosb:IO_STATUS_BLOCK local hFile:HANDLE ; Помни, что коды форматирования Unicode (%C, %S, %lc, %ls, %wc, %ws, %wZ), передаваемые в ; функцию DbgPrint могут быть использованы только на IRQL = PASSIVE_LEVEL! invoke DbgPrint, $CTA0("\nFileWorks: Creating %ws file\n"), g_usFileName.Buffer InitializeObjectAttributes addr oa, addr g_usFileName, \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL invoke ZwCreateFile, addr hFile, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \ 0, FILE_CREATE, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File created\n") invoke ZwClose, hFile .else invoke DbgPrint, $CTA0("FileWorks: Can't create file. Status: %08X\n"), eax .endif ret CreateFile endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; WriteFile ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: WriteFile proc local oa:OBJECT_ATTRIBUTES local iosb:IO_STATUS_BLOCK local hFile:HANDLE invoke DbgPrint, $CTA0("\nFileWorks: Opening file for writing\n") InitializeObjectAttributes addr oa, addr g_usFileName, \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL invoke ZwCreateFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \ 0, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File openeded\n") CTA0 "Data can be written to an open file", g_szData, 4 invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \ addr g_szData, sizeof g_szData - 1, NULL, NULL .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File was written\n") .else invoke DbgPrint, $CTA0("FileWorks: Can't write to the file. Status: %08X\n"), eax .endif invoke ZwClose, hFile .else invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax .endif ret WriteFile endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; MarkAsReadOnly ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: MarkAsReadOnly proc local oa:OBJECT_ATTRIBUTES local iosb:IO_STATUS_BLOCK local hFile:HANDLE local fbi:FILE_BASIC_INFORMATION invoke DbgPrint, $CTA0("\nFileWorks: Opening file for changing attributes\n") InitializeObjectAttributes addr oa, addr g_usFileName, \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL invoke ZwCreateFile, addr hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES + SYNCHRONIZE, \ addr oa, addr iosb, 0, 0, FILE_SHARE_READ, \ FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File openeded\n") invoke ZwQueryInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File attributes were: %08X\n"), fbi.FileAttributes or fbi.FileAttributes, FILE_ATTRIBUTE_READONLY invoke ZwSetInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: Now file marked as read-only\n") .else invoke DbgPrint, $CTA0("FileWorks: Can't change file attributes. Status: %08X\n"), eax .endif .else invoke DbgPrint, $CTA0("FileWorks: Can't query file attributes. Status: %08X\n"), eax .endif invoke ZwClose, hFile .else invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax .endif ret MarkAsReadOnly endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ReadFile ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ReadFile proc local oa:OBJECT_ATTRIBUTES local iosb:IO_STATUS_BLOCK local hFile:HANDLE local p:PVOID local cb:DWORD local fsi:FILE_STANDARD_INFORMATION invoke DbgPrint, $CTA0("\nFileWorks: Opening file for reading\n") InitializeObjectAttributes addr oa, addr g_usFileName, \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL invoke ZwOpenFile, addr hFile, FILE_READ_DATA + SYNCHRONIZE, addr oa, addr iosb, \ FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File openeded\n") invoke ZwQueryInformationFile, hFile, addr iosb, addr fsi, sizeof fsi, FileStandardInformation .if eax == STATUS_SUCCESS mov eax, fsi.EndOfFile.LowPart inc eax mov cb, eax invoke ExAllocatePool, PagedPool, cb .if eax != NULL mov p, eax invoke RtlZeroMemory, p, cb invoke ZwReadFile, hFile, 0, NULL, NULL, addr iosb, p, cb, 0, NULL .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File content: \=%s\=\n"), p .else invoke DbgPrint, $CTA0("FileWorks: Can't read from the file. Status: %08X\n"), eax .endif invoke ExFreePool, p .else invoke DbgPrint, $CTA0("FileWorks: Can't allocate memory. Status: %08X\n"), eax .endif .else invoke DbgPrint, $CTA0("FileWorks: Can't query file size. Status: %08X\n"), eax .endif invoke ZwClose, hFile .else invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax .endif ret ReadFile endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; UnmarkAsReadOnly ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: UnmarkAsReadOnly proc local oa:OBJECT_ATTRIBUTES local iosb:IO_STATUS_BLOCK local hFile:HANDLE local fbi:FILE_BASIC_INFORMATION invoke DbgPrint, $CTA0("\nFileWorks: Opening file for changing attributes\n") InitializeObjectAttributes addr oa, addr g_usFileName, \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL invoke ZwCreateFile, addr hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES + SYNCHRONIZE, \ addr oa, addr iosb, 0, 0, FILE_SHARE_READ, FILE_OPEN, \ FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File openeded\n") invoke ZwQueryInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File attributes were: %08X\n"), fbi.FileAttributes and fbi.FileAttributes, not FILE_ATTRIBUTE_READONLY invoke ZwSetInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: Now file can be written or deleted\n") .else invoke DbgPrint, $CTA0("FileWorks: Can't change file attributes. Status: %08X\n"), eax .endif .else invoke DbgPrint, $CTA0("FileWorks: Can't query file attributes. Status: %08X\n"), eax .endif invoke ZwClose, hFile .else invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax .endif ret UnmarkAsReadOnly endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; AppendFile ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: AppendFile proc local oa:OBJECT_ATTRIBUTES local iosb:IO_STATUS_BLOCK local hFile:HANDLE invoke DbgPrint, $CTA0("\nFileWorks: Opening file to append data\n") InitializeObjectAttributes addr oa, addr g_usFileName, \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL invoke ZwOpenFile, addr hFile, FILE_APPEND_DATA + SYNCHRONIZE, addr oa, addr iosb, \ FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File openeded\n") CTA0 " using ZwWriteFile", g_szDataToAppend, 4 invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \ addr g_szDataToAppend, sizeof g_szDataToAppend - 1, NULL, NULL .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: Data appended to the file\n") .else invoke DbgPrint, $CTA0("FileWorks: Can't append data to file. Status: %08X\n"), eax .endif invoke ZwClose, hFile .else invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax .endif ret AppendFile endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; TruncateFile ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: TruncateFile proc local oa:OBJECT_ATTRIBUTES local iosb:IO_STATUS_BLOCK local hFile:HANDLE local fsi:FILE_STANDARD_INFORMATION local feofi:FILE_END_OF_FILE_INFORMATION invoke DbgPrint, $CTA0("\nFileWorks: Opening file to truncate\n") InitializeObjectAttributes addr oa, addr g_usFileName, \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL invoke ZwOpenFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \ FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File openeded\n") invoke ZwQueryInformationFile, hFile, addr iosb, \ addr fsi, sizeof fsi, FileStandardInformation .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: EOF was: %08X\n"), fsi.EndOfFile.LowPart and feofi.EndOfFile.HighPart, 0 mov eax, fsi.EndOfFile.LowPart shr eax, 1 mov feofi.EndOfFile.LowPart, eax invoke ZwSetInformationFile, hFile, addr iosb, \ addr feofi, sizeof feofi, FileEndOfFileInformation .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File truncated to its half size\n") .else invoke DbgPrint, $CTA0("FileWorks: Can't truncate file. Status: %08X\n"), eax .endif .else invoke DbgPrint, $CTA0("FileWorks: Can't query file info. Status: %08X\n"), eax .endif invoke ZwClose, hFile .else invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax .endif ret TruncateFile endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DeleteFile ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DeleteFile proc local oa:OBJECT_ATTRIBUTES local iosb:IO_STATUS_BLOCK local hFile:HANDLE local fdi:FILE_DISPOSITION_INFORMATION invoke DbgPrint, $CTA0("\nFileWorks: Opening file for deletion") InitializeObjectAttributes addr oa, addr g_usFileName, \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL invoke ZwCreateFile, addr hFile, DELETE + SYNCHRONIZE, addr oa, addr iosb, \ 0, 0, FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File openeded\n") mov fdi.DeleteFile, TRUE invoke ZwSetInformationFile, hFile, addr iosb, addr fdi, sizeof fdi, FileDispositionInformation .if eax == STATUS_SUCCESS ; The file has been marked for deletion. Do nothing with the file handle except closing it. invoke DbgPrint, $CTA0("FileWorks: File has been marked for deletion\n") invoke DbgPrint, $CTA0("FileWorks: It should be deleted when the last open handle is closed\n") .else invoke DbgPrint, $CTA0("FileWorks: Can't mark file for deletion. Status: %08X\n"), eax .endif invoke ZwClose, hFile .else invoke DbgPrint, $CTA0("FileWorks: Can't open file. Status: %08X\n"), eax .endif ret DeleteFile endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DeleteDirectory ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DeleteDirectory proc local oa:OBJECT_ATTRIBUTES local iosb:IO_STATUS_BLOCK local hDirectory:HANDLE InitializeObjectAttributes addr oa, addr g_usDirName, \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL invoke ZwDeleteFile, addr oa .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("\nFileWorks: Directory should be deleted\n") .else invoke DbgPrint, $CTA0("\nFileWorks: Can't delete directory. Status: %08X\n"), eax .endif ret DeleteDirectory endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; EnumerateFiles ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: EnumerateFiles proc uses esi local status:NTSTATUS local oa:OBJECT_ATTRIBUTES local hSystemRootDirectory:HANDLE local hDriversDirectory:HANDLE local as:ANSI_STRING local us:UNICODE_STRING local iosb:IO_STATUS_BLOCK local tf:TIME_FIELDS local cb:DWORD local pfdi:PFILE_DIRECTORY_INFORMATION invoke DbgPrint, $CTA0("\nFileWorks: Opening directory to enumerate files\n") InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("\\SystemRoot"), \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL invoke ZwOpenFile, addr hSystemRootDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \ addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \ FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT .if eax == STATUS_SUCCESS InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("system32\\drivers"), \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, hSystemRootDirectory, NULL invoke ZwOpenFile, addr hDriversDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \ addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \ FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT .if eax == STATUS_SUCCESS mov cb, sizeof FILE_DIRECTORY_INFORMATION + 256 invoke ExAllocatePool, PagedPool, cb .if eax != NULL mov pfdi, eax mov esi, eax assume esi:ptr FILE_DIRECTORY_INFORMATION invoke DbgPrint, \ $CTA0("\nFileWorks: ---------- Starting enumerate files ----------\n") invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \ esi, cb, FileDirectoryInformation, \ TRUE, $CCOUNTED_UNICODE_STRING("c*"), TRUE .while eax != STATUS_NO_MORE_FILES .if ( eax == STATUS_SUCCESS ) mov eax, [esi].FileNameLength mov us._Length, ax mov us.MaximumLength, ax lea eax, [esi].FileName mov us.Buffer, eax invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE .if eax == STATUS_SUCCESS invoke RtlTimeToTimeFields, addr [esi].CreationTime, addr tf movzx eax, tf.Day movzx ecx, tf.Month movzx edx, tf.Year invoke DbgPrint, $CTA0(" %s size=%d created on %d.%02d.%04d\n"), \ as.Buffer, [esi].EndOfFile.LowPart, eax, ecx, edx invoke RtlFreeAnsiString, addr as .endif .endif invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \ esi, cb, FileDirectoryInformation, \ TRUE, NULL, FALSE .endw invoke DbgPrint, \ $CTA0("FileWorks: ------------------------------------------------\n") assume esi:nothing invoke ExFreePool, pfdi .endif invoke ZwClose, hDriversDirectory .else invoke DbgPrint, $CTA0("FileWorks: Can't open drivers directory. Status: %08X\n"), eax .endif invoke ZwClose, hSystemRootDirectory .else invoke DbgPrint, $CTA0("FileWorks: Can't open system root directory. Status: %08X\n"), eax .endif ret EnumerateFiles endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; DriverEntry ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING invoke DbgPrint, $CTA0("\nFileWorks: Entering DriverEntry\n") invoke CreateDirectory invoke CreateFile invoke WriteFile invoke MarkAsReadOnly invoke ReadFile invoke UnmarkAsReadOnly invoke AppendFile invoke ReadFile invoke TruncateFile invoke ReadFile invoke DeleteFile invoke DeleteDirectory invoke EnumerateFiles invoke DbgPrint, $CTA0("\nFileWorks: Leaving DriverEntry\n") mov eax, STATUS_DEVICE_CONFIGURATION_ERROR ret DriverEntry endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ; ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: end DriverEntry :make set drv=FileWorks \masm32\bin\ml /nologo /c /coff %drv%.bat \masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj del %drv%.obj echo. pauseВ ntddk.inc не входят некоторые нужные нам константы и структуры.
Код (Text):
include \masm32\include\w2k\ntifs.incВ Installable File System (IFS) Kit, предназначенный для разработки драйверов файловых систем, входит заголовочный файл ntifs.h, аналогом которого и является включаемый файл ntifs.inc. Этот файл входит в состав KmdKit начиная с версии 1.5.
11.3 Создание каталога и файла
Код (Text):
InitializeObjectAttributes addr oa, addr g_usDirName, \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULLЗаполняем структуру OBJECT_ATTRIBUTES, не забывая про флаг OBJ_KERNEL_HANDLE. Хочу подчеркнуть, что в данном примере это необязательно, т.к. мы не собираемся использовать ни один из описателей в контексте какого-либо другого процесса. Но поскольку процедуры драйвера FileWorks легко могут быть использованы в других проектах в контексте любого процесса, я решил сделать именно так. Если в ваши задачи входит совместное использование описателя драйвером и пользовательским процессом, то флаг OBJ_KERNEL_HANDLE использовать не следует. Если обращения к объекту будут происходить в контексте одного и того же процесса, флаг OBJ_KERNEL_HANDLE не нужен.
Для создания, как каталога, так и файла используется функция ZwCreateFile. С точки зрения системы, каталоги являются теми же файлами, поэтому принципиальных различий в процедуре создания файла и каталога нет. Поэтому, все сказанное ниже в отношении создания каталога в равной степени относится и к файлам, за исключением флага FILE_DIRECTORY_FILE.
Код (Text):
invoke ZwCreateFile, addr hDirectory, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \ 0, FILE_OPEN_IF, FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0У функции ZwCreateFile довольно много параметров. Поэтому, я приведу её протопип. И вынужден сделать это, используя синтаксис языка си, чтобы были видны входные (IN) выходные (OUT) и необязательные (OPTIONAL) параметры.
Код (Text):
NTSTATUS ZwCreateFile( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER AllocationSize OPTIONAL, IN ULONG FileAttributes, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN PVOID EaBuffer OPTIONAL, IN ULONG EaLength );При успешном завершении функции, параметр FileHandle получает описатель созданного каталога. DesiredAccess определяет тип запрашиваемого доступа к создаваемому каталогу. Мы передаем только флаг SYNCHRONIZE, значение которого станет ясно чуть позже. Что такое ObjectAttributes вы уже знаете. После завершения функции из параметра IoStatusBlock, являющегося указателем на структуру IO_STATUS_BLOCK можно извлечь кое-какую дополнительную информацию. Параметр FileAttributes определяет атрибуты (только для чтения, скрытый и т.п.) создаваемого каталога. Мы используем FILE_ATTRIBUTE_NORMAL, т.к. придавать какие-то особые свойства каталогу, в данном случае, не требуется. FILE_ATTRIBUTE_NORMAL используется по умолчанию, поэтому, можно просто передать в этом параметре 0. Нулевое значение в необязательном параметре AllocationSize определяет, что будет создан файл нулевого размера. В случае каталога это вполне естественно. Параметр ShareAccess определяет, может ли какой-то другой код открыть описатель каталога и с какими правами доступа. В данном случае, мы запрещаем кому-либо доступ к каталогу, установив этот параметр в 0. Параметр CreateDisposition определяет действия системы, в случае если такой каталог уже существует или наоборот - если такого файла нет. Мы используем флаг FILE_OPEN_IF, который означает, что если такой каталог уже существует, он будет открыт. В параметре CreateOptions передаем комбинацию флагов FILE_DIRECTORY_FILE и FILE_SYNCHRONOUS_IO_NONALERT. Первый в особых пояснениях не нуждается и означает, что будет создан каталог, а не файл. FILE_SYNCHRONOUS_IO_NONALERT определяет (не уверен, кстати, что в случае каталога этот флаг имеет значение), что все операции с файлом будут проводиться синхронно, т.е. например, вызов ZwReadFile не вернет управление до тех пор, пока данные из файла не будут фактически прочитаны. При этом диспетчер ввода-вывода поддерживает для файла так называемую текущую позицию указателя файла (file position context). Если флаг FILE_SYNCHRONOUS_IO_NONALERT установлен, то в параметре DesiredAccess так же должен быть определен флаг SYNCHRONIZE. Последние два параметра не используются драйверами.
Код (Text):
.if eax == STATUS_SUCCESS .if iosb.Information == FILE_CREATED .elseif iosb.Information == FILE_OPENED .endifКак я только что сказал, в структуре IO_STATUS_BLOCK будет находиться дополнительная информация.
Вспомните, как мы завершаем обработку запроса ввода-вывода (см. предыдущие части). Например, в драйвере SharingMemory (часть 9) обработка IRP_MJ_CREATE и IRP_MJ_CLOSE завершается так:
Код (Text):
mov eax, pIrp mov (_IRP PTR [eax]).IoStatus.Status, STATUS_SUCCESS and (_IRP PTR [eax]).IoStatus.Information, 0 fastcall IofCompleteRequest, pIrp, IO_NO_INCREMENTПримерно то же самое делает драйвер, завершающий запрос создания файла. Только значения, помещаемые в поля структуры IO_STATUS_BLOCK, будут зависеть от типа запроса.
Т.к. мы определили флаг FILE_OPEN_IF, система может поступить двояко. По значению iosb.Information мы узнаем, что же произошло: был ли создан новый каталог или такой каталог уже существовал и поэтому был открыт.
Код (Text):
invoke ZwClose, hDirectory .endifЛишний раз повторю, что в ядре необходимо явно закрывать все открытые описатели.
Код (Text):
invoke ZwCreateFile, addr hFile, SYNCHRONIZE, addr oa, addr iosb, 0, FILE_ATTRIBUTE_NORMAL, \ 0, FILE_CREATE, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0Как видите, файл создается практически аналогично, нужно только убрать флаг FILE_DIRECTORY_FILE. А флаг FILE_CREATE я использую для разнообразия. Он означает, что файл может быть только создан. Если такой файл уже существует, вызов ZwCreateFile окончится неудачей.
11.4 Объект "файл"
Каждому открытому описателю файла соответствует объект "файл" (file object), представляющийся в памяти ядра структурой FILE_OBJECT.
Код (Text):
FILE_OBJECT STRUCT ; sizeof = 070h _Type SWORD ? ; 0000h IO_TYPE_FILE _Size SWORD ? ; 0002h DeviceObject PVOID ? ; 0004h PTR DEVICE_OBJECT Vpb PVOID ? ; 0008h PTR VPB FsContext PVOID ? ; 000Ch FsContext2 PVOID ? ; 0010h SectionObjectPointer PVOID ? ; 0014h PTR SECTION_OBJECT_POINTERS PrivateCacheMap PVOID ? ; 0018h FinalStatus SDWORD ? ; 001Ch RelatedFileObject PVOID ? ; 0020h PTR FILE_OBJECT LockOperation BYTE ? ; 0024h BOOLEAN DeletePending BYTE ? ; 0025h BOOLEAN ReadAccess BYTE ? ; 0026h BOOLEAN WriteAccess BYTE ? ; 0027h BOOLEAN DeleteAccess BYTE ? ; 0028h BOOLEAN SharedRead BYTE ? ; 0029h BOOLEAN SharedWrite BYTE ? ; 002Ah BOOLEAN SharedDelete BYTE ? ; 002Bh BOOLEAN Flags DWORD ? ; 002Ch FileName UNICODE_STRING ; 0030h CurrentByteOffset LARGE_INTEGER ; 0038h Waiters DWORD ? ; 0040h Busy DWORD ? ; 0044h LastLock PVOID ? ; 0048h _Lock KEVENT ; 004Ch Event KEVENT ; 005Ch CompletionContext PVOID ? ; 006Ch PTR IO_COMPLETION_CONTEXT FILE_OBJECT ENDS PFILE_OBJECT typedef ptr FILE_OBJECTНапример, мы можем два раза открыть один и тот же файл, но запросить разные права доступа: чтение (FILE_READ_DATA) в первом случае и запись (FILE_WRITE_DATA) - во втором. В результате в ядре будет создано две структуры FILE_OBJECT, каждая из которых будет соответствовать своему описателю файла. Но оба описателя и соответственно обе структуры соответствуют одному и тому же файлу на диске. В первой структуре FILE_OBJECT будет установлено поле ReadAccess, во второй - WriteAccess. Например, при попытке записи в файл по первому описателю, система увидит, что поле WriteAccess = FALSE и запрос будет отклонен.
Поле DeviceObject будет содержать указатель на объект "устройство" \Device\HarddiskVolume1, т.к. именно на это устройство ссылается символьная ссылка \??\c:, которую мы используем в имени создаваемого файла.
Можете после прочтения статьи поэкспериментировать с файлами и посмотреть, как система заполняет и управляет структурой FILE_OBJECT. Если возникнут проблемы с локализацией её в памяти, можно использовать функцию ObReferenceObjectByHandle примерно таким образом:
Код (Text):
local pFileObject:PFILE_OBJECT . . . invoke ObReferenceObjectByHandle, hFile, FILE_READ_DATA, NULL, KernelMode, addr pFileObject, NULL .if eax == STATUS_SUCCESS ; pFileObject указывает на структуру FILE_OBJECT соответствующую описателю hFile fastcall ObfDereferenceObject, pFileObject .endifObReferenceObjectByHandle вернет в переменной pFileObject указатель на структуру FILE_OBJECT, соответствующую описателю файла. При этом счетчик ссылок будет увеличен на единицу. Вызовом ObfDereferenceObject необходимо вернуть ему старое значение.
11.5 Запись в файл
К этому моменту у нас уже есть каталог и файл нулевого размера. Пора в него что-нибудь записать - это будет строка символов.
Код (Text):
invoke ZwCreateFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \ 0, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0С помощью функции ZwCreateFile можно не только создавать файлы, но и открывать существующие (и, кстати, не только файлы, а и некоторые другие объекты). Для этого надо указать флаг FILE_OPEN.
Для записи в файл нужен соответствующий доступ - используем флаг FILE_WRITE_DATA.
Во включаемых файлах определены несколько констант для общего доступа к файлам. Например, FILE_GENERIC_WRITE позволит не только писать данные в файл, но и изменять его свойства, а также добавлять данные.
Код (Text):
FILE_GENERIC_WRITE equ (STANDARD_RIGHTS_WRITE or FILE_WRITE_DATA or FILE_WRITE_ATTRIBUTES or FILE_WRITE_EA or FILE_APPEND_DATA or SYNCHRONIZE)Но я везде буду запрашивать только минимально необходимые, на данный момент, права доступа, т.к. использовать, например, FILE_ALL_ACCESS везде, где надо и не надо - не очень хороший прием программирования.
Очевидно, что пока мы будем писать в файл никто другой не должен делать того же. Используем флаг FILE_SHARE_READ - больше никто не сможет открыть наш файл ни для записи, ни для удаления, а сможет только получить доступ на чтение из файла.
Код (Text):
.if eax == STATUS_SUCCESS CTA0 "Data can be written to an open file", g_szData, 4 invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \ addr g_szData, sizeof g_szData - 1, NULL, NULLНазвание функции ZwWriteFile говорит само за себя.
Код (Text):
NTSTATUS ZwWriteFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, IN ULONG Length, IN PLARGE_INTEGER ByteOffset OPTIONAL, IN PULONG Key OPTIONAL );Минимально необходимым набором параметров для функции ZwWriteFile являются описатель файла, указатель на структуру IO_STATUS_BLOCK, в которую будет помещена дополнительная информация (в частности, количество фактически записанных в файл байт), указатель на данные, которые требуется записать в файл и их размер.
11.6 Изменение свойств файла
Предположим, нам потребовалось предотвратить удаление файла.
Код (Text):
invoke ZwCreateFile, addr hFile, FILE_READ_ATTRIBUTES + FILE_WRITE_ATTRIBUTES + SYNCHRONIZE, \ addr oa, addr iosb, 0, 0, FILE_SHARE_READ, \ FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0Флаги FILE_READ_ATTRIBUTES и FILE_WRITE_ATTRIBUTES требуются для получения и изменения свойств файла соответственно.
Код (Text):
.if eax == STATUS_SUCCESS invoke ZwQueryInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation .if eax == STATUS_SUCCESSНам нужно установить атрибут "только чтение", но поскольку у файла имеются и другие атрибуты их нужно оставить без изменений, а для этого их нужно знать.
Код (Text):
or fbi.FileAttributes, FILE_ATTRIBUTE_READONLY invoke ZwSetInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformationДобавляем к атрибутам файла необходимый нам флаг. Когда понадобиться произвести изменения в файле мы сбросим этот атрибут в процедуре UnmarkAsReadOnly так:
Код (Text):
and fbi.FileAttributes, not FILE_ATTRIBUTE_READONLY
11.7 Чтение из файла
Теперь, для разнообразия, откроем файл для чтения с помощью функции ZwOpenFile, специально предназначенной для этих целей. Назначение параметров совпадает с аналогичными параметрами функции ZwCreateFile.
Код (Text):
NTSTATUS ZwOpenFile( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG OpenOptions );
Код (Text):
invoke ZwOpenFile, addr hFile, FILE_READ_DATA + SYNCHRONIZE, addr oa, addr iosb, \ FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERTFILE_READ_DATA - в данный момент нам будет достаточно только доступа для чтения данных из файла. Комбинация флагов FILE_SHARE_READ, FILE_SHARE_WRITE и FILE_SHARE_DELETE позволит всем остальным получать полный доступ к файлу.
Код (Text):
.if eax == STATUS_SUCCESS invoke ZwQueryInformationFile, hFile, addr iosb, addr fsi, sizeof fsi, FileStandardInformation .if eax == STATUS_SUCCESSМы собираемся прочитать все содержимое файла, но для этого требуется буфер, размер которого зависит от размера файла. Размер файла можно узнать вызвав функцию ZwQueryInformationFile с информационным классом FileStandardInformation, передав ей указатель на структуру FILE_BASIC_INFORMATION.
Код (Text):
mov eax, fsi.EndOfFile.LowPart inc eax mov cb, eaxДобавим один байт на завершающий ноль.
Код (Text):
invoke ExAllocatePool, PagedPool, cb .if eax != NULL mov p, eax invoke RtlZeroMemory, p, cb invoke ZwReadFile, hFile, 0, NULL, NULL, addr iosb, p, cb, 0, NULL .if eax == STATUS_SUCCESS invoke DbgPrint, $CTA0("FileWorks: File content: \=%s\=\n"), p .endif invoke ExFreePool, p .endifВыделим буфер нужного размера, обнулим его и передадим в функцию ZwReadFile. Прототип этой функции совпадает с прототипом функции ZwWriteFile, только буфер является не источником данных, а приемником. Т.к. перед использованием мы обнулили буфер и его размер на один байт больше строки, содержащейся в файле, то никаких проблем с выводом его содержимого в отладочном сообщении у нас не возникнет.
11.8 Добавление данных в файл
Есть несколько путей добавления данных в файл. Можно открыть его с флагом FILE_WRITE_DATA, установить текущую позицию указателя файла на его конец, передав смещение в параметре ByteOffset функции ZwWriteFile, и писать данные. Текущая позиция указателя файла, кстати, храниться в FILE_OBJECT.CurrentByteOffset. А можно открыть файл в флагом FILE_APPEND_DATA и текущая позиция указателя файла будет автоматически установлена на его конец. Так мы и поступим.
Код (Text):
invoke ZwOpenFile, addr hFile, FILE_APPEND_DATA + SYNCHRONIZE, addr oa, addr iosb, \ FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT .if eax == STATUS_SUCCESS CTA0 " using ZwWriteFile", g_szDataToAppend, 4 invoke ZwWriteFile, hFile, 0, NULL, NULL, addr iosb, \ addr g_szDataToAppend, sizeof g_szDataToAppend - 1, NULL, NULLРазмер файла будет автоматически увеличен в соответствии с размером добавленных данных.
11.9 Усечение файла
Допустим, нам нужно уменьшить размер файла, отбросив ненужные данные. В данном случае, я, для простоты, уменьшаю размер файла в два раза.
Код (Text):
invoke ZwOpenFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \ FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERTОткрываем файл с доступом на запись данных.
Код (Text):
.if eax == STATUS_SUCCESS invoke ZwQueryInformationFile, hFile, addr iosb, \ addr fsi, sizeof fsi, FileStandardInformation .if eax == STATUS_SUCCESSПолучаем текущий размер файла в составе структуры FILE_STANDARD_INFORMATION.
Код (Text):
and feofi.EndOfFile.HighPart, 0 mov eax, fsi.EndOfFile.LowPart shr eax, 1 mov feofi.EndOfFile.LowPart, eax invoke ZwSetInformationFile, hFile, addr iosb, \ addr feofi, sizeof feofi, FileEndOfFileInformationУстанавливаем новый размер, равный половине текущего, пользуясь информационным классом FileEndOfFileInformation.
11.10 Удаление файла и каталога
Дело за малым - привести диск c: в первоначальное состояние. Как это ни странно, но 2000 DDK умалчивает о существовании функции ZwDeleteFile. В XP DDK говорится, что эта функция все таки существует в системе, но начиная с Windows XP. Но это не так. ZwDeleteFile есть и Windows 2000 и даже в Windows NT4. Но для удаления файла мы воспользуемся несколько более сложным способом, а вот каталог удалим с помощью ZwDeleteFile.
Код (Text):
invoke ZwCreateFile, addr hFile, DELETE + SYNCHRONIZE, addr oa, addr iosb, \ 0, 0, FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0Открываем файл для удаления. При этом всем остальным также разрешаем только удалять его.
Код (Text):
.if eax == STATUS_SUCCESS mov fdi.DeleteFile, TRUE invoke ZwSetInformationFile, hFile, addr iosb, addr fdi, sizeof fdi, FileDispositionInformationВоспользуемся информационным классом FileDispositionInformation и установим признак удаления файла. При этом в структуре FILE_OBJECT поле DeletePending изменит свое значение с FALSE на TRUE. Это будет означать, что файл отмечен для удаления. До тех пор, пока хотя бы один открытый описатель такого файла существует, он не будет удален.
Код (Text):
invoke ZwClose, hFileТеперь единственный описатель файла закрыт и файл удален.
Код (Text):
invoke ZwDeleteFile, addr oaУдаление каталога с помощью ZwDeleteFile проходит несколько проще и пояснений не требует.
11.11 Перечисление содержимого каталога
Существует два альтернативных способа определения имени создаваемого/открываемого объекта вообще и файла в частности. Доступ к именованному объекту можно получить либо используя полный путь, либо относительный. До сих пор мы использовали только абсолютные пути. Например, путь \??\c:\FileWorks\test.txt является абсолютным. В этом случае заполнение структуры OBJECT_ATTRIBUTES с помощью макроса InitializeObjectAttributes выглядит примерно так:
Код (Text):
InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("\??\c:\FileWorks\test.txt"), \ OBJ_CASE_INSENSITIVE, NULL, NULLПредпоследний параметр RootDirectory равен NULL. Параметр RootDirectory макроса InitializeObjectAttributes и одноименное поле структуры OBJECT_ATTRIBUTES, которую этот макрос заполняет, определяет описатель каталога-контейнера объекта.
Код (Text):
OBJECT_ATTRIBUTES STRUCT . . . RootDirectory HANDLE ? . . . OBJECT_ATTRIBUTES ENDSЕсли каталог-контейнер объекта уже открыт, т.е. имеется его описатель, то к объекту можно обратиться по относительному к каталогу-контейнеру пути. При этом описатель каталога-контейнера должен быть помещен в поле RootDirectory. Например, если мы уже открыли каталог \??\c:\FileWorks\ и поместили его описатель в переменную hDirectory, то можем использовать относительный путь файла test.txt таким образом:
Код (Text):
InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("test.txt"), OBJ_CASE_INSENSITIVE, hDirectory, NULLПод каталогом-контейнером подразумевается не только каталог на диске, но и каталог в пространстве имен диспетчера объектов.
Для перечисления содержимого системного каталога \%SystemRoot%\System32\Drivers\ мы как раз и будем использовать относительный путь.
Код (Text):
InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("\\SystemRoot"), \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULLЗаполняем структуру OBJECT_ATTRIBUTES, используя символьную ссылку \SystemRoot - этот путь абсолютный.
Код (Text):
invoke ZwOpenFile, addr hSystemRootDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \ addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \ FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERTОткрываем каталог \%SystemRoot%\. Флаг FILE_LIST_DIRECTORY позволит перечислять содержимое каталога.
Код (Text):
.if eax == STATUS_SUCCESS InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("system32\\drivers"), \ OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, hSystemRootDirectory, NULLЕсли каталог успешно открыт, то в переменной hSystemRootDirectory получим его описатель, который и будем использовать в качестве описателя каталога-контейнера. Заполняем структуру OBJECT_ATTRIBUTES, используя относительный путь "system32\drivers".
Код (Text):
invoke ZwOpenFile, addr hDriversDirectory, FILE_LIST_DIRECTORY + SYNCHRONIZE, addr oa, \ addr iosb, FILE_SHARE_READ + FILE_SHARE_WRITE + FILE_SHARE_DELETE, \ FILE_DIRECTORY_FILE + FILE_SYNCHRONOUS_IO_NONALERT .if eax == STATUS_SUCCESSОткрываем каталог \%SystemRoot%\System32\Drivers\ по относительному пути.
Код (Text):
mov cb, sizeof FILE_DIRECTORY_INFORMATION + 256 invoke ExAllocatePool, PagedPool, cb .if eax != NULLВыделяем небольшой буфер, в который должна поместиться структура FILE_DIRECTORY_INFORMATION и имя файла.
Код (Text):
mov pfdi, eax mov esi, eax assume esi:ptr FILE_DIRECTORY_INFORMATION invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \ esi, cb, FileDirectoryInformation, \ TRUE, $CCOUNTED_UNICODE_STRING("c*"), TRUEНачинаем перечислять файлы каталога, используя информационный класс FileDirectoryInformation. Прототип функции ZwQueryDirectoryFile, пожалуй, будет не лишним.
Код (Text):
NTSTATUS ZwQueryDirectoryFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG Length, IN FILE_INFORMATION_CLASS FileInformationClass, IN BOOLEAN ReturnSingleEntry, IN PUNICODE_STRING FileName OPTIONAL, IN BOOLEAN RestartScan );И о существовании этой функции 2000 DDK тоже умалчивает. В XP DDK говорится, что эта функция существует в системе, начиная с Windows XP. И это тоже неправда.
Параметр ReturnSingleEntry устанавливаем равным TRUE, что заставляет функцию ZwQueryDirectoryFile вернуть информацию только об одном файле, причем первом. Параметр FileName указывает на строку с условием поиска "c*", т.е. будут перечисляться только файлы, имена которых начинаются с символа "c". Это позволит сократить объем выводимой через отладочные сообщения информации. При первом вызове ZwQueryDirectoryFile мы должны установить параметр RestartScan в TRUE. Это заставит функцию ZwQueryDirectoryFile начать просмотр содержимого каталога.
Код (Text):
.while eax != STATUS_NO_MORE_FILESЗапускаем цикл, который крутится до тех пор, пока ZwQueryDirectoryFile не вернет STATUS_NO_MORE_FILES, т.е. пока все файлы в каталоге не будут перечислены.
Код (Text):
.if ( eax == STATUS_SUCCESS )Если вдруг в просматриваемом нами каталоге окажется файл, длина имени которого превысит 256 байт (что, кстати, практически невероятно, т.к. имена драйверов не превышают 8 символов), ZwQueryDirectoryFile вернет код ошибки отличный от STATUS_NO_MORE_FILES. В этом случае мы просто пропускаем такой файл.
Код (Text):
mov eax, [esi].FileNameLength mov us._Length, ax mov us.MaximumLength, ax lea eax, [esi].FileName mov us.Buffer, eax invoke RtlUnicodeStringToAnsiString, addr as, addr us, TRUE .if eax == STATUS_SUCCESS invoke RtlTimeToTimeFields, addr [esi].CreationTime, addr tf movzx eax, tf.Day movzx ecx, tf.Month movzx edx, tf.Year invoke DbgPrint, $CTA0(" %s size=%d created on %d.%02d.%04d\n"), \ as.Buffer, [esi].EndOfFile.LowPart, eax, ecx, edx invoke RtlFreeAnsiString, addr as .endifФорматируем полученную информацию, выводя имя файла, его размер и дату создания.
Код (Text):
.endif invoke ZwQueryDirectoryFile, hDriversDirectory, NULL, NULL, NULL, addr iosb, \ esi, cb, FileDirectoryInformation, \ TRUE, NULL, FALSE .endwВ цикле вызываем ZwQueryDirectoryFile, но параметры ReturnSingleEntry, FileName и RestartScan равны TRUE, NULL и FALSE соответственно. Это заставит функцию ZwQueryDirectoryFile продолжить перечисление файлов.
Код (Text):
invoke ExFreePool, pfdi .endif invoke ZwClose, hDriversDirectory .endif invoke ZwClose, hSystemRootDirectory .endifОтдаем все занятые ресурсы назад.
Исходный код драйвера в архиве. Для компиляции требуется версия KmdKit не ниже 1.5 - берите на сайте.
© Four-F
Драйверы режима ядра: Часть 11 : Базовая техника: Каталоги и файлы
Дата публикации 22 янв 2004