Источник: http://wasm.ru/print.php?article=drvw2k11

Драйверы режима ядра: Часть 11 : Базовая техника: Каталоги и файлы



Обеспечение возможности работы с файлами является важнейшей задачей любой операционной системы. Посмотрим, какие возможности предоставляют нам операционные системы семейства NT.


11.1 Таблица описателей ядра

Прежде чем мы перейдем непосредственно к теме статьи, обсудим один важный момент, которому прежде не было уделено должного внимания. Для получения описателя объекта требуется заполнить структуру OBJECT_ATTRIBUTES - это мы делали уже много раз примерно таким образом:


 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 таким образом:


 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. Все они роботают почти независимо друг от друга.


 ;@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 не входят некоторые нужные нам константы и структуры.


 include \masm32\include\w2k\ntifs.inc

В Installable File System (IFS) Kit, предназначенный для разработки драйверов файловых систем, входит заголовочный файл ntifs.h, аналогом которого и является включаемый файл ntifs.inc. Этот файл входит в состав KmdKit начиная с версии 1.5.


11.3 Создание каталога и файла


     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.


     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) параметры.


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. Последние два параметра не используются драйверами.


     .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 завершается так:


 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 мы узнаем, что же произошло: был ли создан новый каталог или такой каталог уже существовал и поэтому был открыт.


         invoke ZwClose, hDirectory
     .endif

Лишний раз повторю, что в ядре необходимо явно закрывать все открытые описатели.


     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.


 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 примерно таким образом:


 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
 .endif

ObReferenceObjectByHandle вернет в переменной pFileObject указатель на структуру FILE_OBJECT, соответствующую описателю файла. При этом счетчик ссылок будет увеличен на единицу. Вызовом ObfDereferenceObject необходимо вернуть ему старое значение.


11.5 Запись в файл

К этому моменту у нас уже есть каталог и файл нулевого размера. Пора в него что-нибудь записать - это будет строка символов.


     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 позволит не только писать данные в файл, но и изменять его свойства, а также добавлять данные.


 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 - больше никто не сможет открыть наш файл ни для записи, ни для удаления, а сможет только получить доступ на чтение из файла.


     .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 говорит само за себя.


 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 Изменение свойств файла

Предположим, нам потребовалось предотвратить удаление файла.


     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 требуются для получения и изменения свойств файла соответственно.


     .if eax == STATUS_SUCCESS
         invoke ZwQueryInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation
         .if eax == STATUS_SUCCESS

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


             or fbi.FileAttributes, FILE_ATTRIBUTE_READONLY
             invoke ZwSetInformationFile, hFile, addr iosb, addr fbi, sizeof fbi, FileBasicInformation

Добавляем к атрибутам файла необходимый нам флаг. Когда понадобиться произвести изменения в файле мы сбросим этот атрибут в процедуре UnmarkAsReadOnly так:


             and fbi.FileAttributes, not FILE_ATTRIBUTE_READONLY


11.7 Чтение из файла

Теперь, для разнообразия, откроем файл для чтения с помощью функции ZwOpenFile, специально предназначенной для этих целей. Назначение параметров совпадает с аналогичными параметрами функции ZwCreateFile.


 NTSTATUS
   ZwOpenFile(
     OUT PHANDLE             FileHandle,
     IN  ACCESS_MASK         DesiredAccess,
     IN  POBJECT_ATTRIBUTES  ObjectAttributes,
     OUT PIO_STATUS_BLOCK    IoStatusBlock,
     IN  ULONG               ShareAccess,
     IN  ULONG               OpenOptions
     );


     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

FILE_READ_DATA - в данный момент нам будет достаточно только доступа для чтения данных из файла. Комбинация флагов FILE_SHARE_READ, FILE_SHARE_WRITE и FILE_SHARE_DELETE позволит всем остальным получать полный доступ к файлу.


     .if eax == STATUS_SUCCESS
         invoke ZwQueryInformationFile, hFile, addr iosb, addr fsi, sizeof fsi, FileStandardInformation
         .if eax == STATUS_SUCCESS

Мы собираемся прочитать все содержимое файла, но для этого требуется буфер, размер которого зависит от размера файла. Размер файла можно узнать вызвав функцию ZwQueryInformationFile с информационным классом FileStandardInformation, передав ей указатель на структуру FILE_BASIC_INFORMATION.


             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
                 .endif

                 invoke ExFreePool, p
             .endif

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


11.8 Добавление данных в файл

Есть несколько путей добавления данных в файл. Можно открыть его с флагом FILE_WRITE_DATA, установить текущую позицию указателя файла на его конец, передав смещение в параметре ByteOffset функции ZwWriteFile, и писать данные. Текущая позиция указателя файла, кстати, храниться в FILE_OBJECT.CurrentByteOffset. А можно открыть файл в флагом FILE_APPEND_DATA и текущая позиция указателя файла будет автоматически установлена на его конец. Так мы и поступим.


     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 Усечение файла

Допустим, нам нужно уменьшить размер файла, отбросив ненужные данные. В данном случае, я, для простоты, уменьшаю размер файла в два раза.


     invoke ZwOpenFile, addr hFile, FILE_WRITE_DATA + SYNCHRONIZE, addr oa, addr iosb, \
                         FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT

Открываем файл с доступом на запись данных.


     .if eax == STATUS_SUCCESS
         invoke ZwQueryInformationFile, hFile, addr iosb, \
                         addr fsi, sizeof fsi, FileStandardInformation
         .if eax == STATUS_SUCCESS

Получаем текущий размер файла в составе структуры FILE_STANDARD_INFORMATION.


             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.


     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

         mov fdi.DeleteFile, TRUE
         invoke ZwSetInformationFile, hFile, addr iosb, addr fdi, sizeof fdi, FileDispositionInformation

Воспользуемся информационным классом FileDispositionInformation и установим признак удаления файла. При этом в структуре FILE_OBJECT поле DeletePending изменит свое значение с FALSE на TRUE. Это будет означать, что файл отмечен для удаления. До тех пор, пока хотя бы один открытый описатель такого файла существует, он не будет удален.


         invoke ZwClose, hFile

Теперь единственный описатель файла закрыт и файл удален.


     invoke ZwDeleteFile, addr oa

Удаление каталога с помощью ZwDeleteFile проходит несколько проще и пояснений не требует.


11.11 Перечисление содержимого каталога

Существует два альтернативных способа определения имени создаваемого/открываемого объекта вообще и файла в частности. Доступ к именованному объекту можно получить либо используя полный путь, либо относительный. До сих пор мы использовали только абсолютные пути. Например, путь \??\c:\FileWorks\test.txt является абсолютным. В этом случае заполнение структуры OBJECT_ATTRIBUTES с помощью макроса InitializeObjectAttributes выглядит примерно так:


 InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("\??\c:\FileWorks\test.txt"), \
                            OBJ_CASE_INSENSITIVE, NULL, NULL

Предпоследний параметр RootDirectory равен NULL. Параметр RootDirectory макроса InitializeObjectAttributes и одноименное поле структуры OBJECT_ATTRIBUTES, которую этот макрос заполняет, определяет описатель каталога-контейнера объекта.


 OBJECT_ATTRIBUTES STRUCT
 . . .
     RootDirectory               HANDLE          ?
 . . .
 OBJECT_ATTRIBUTES ENDS

Если каталог-контейнер объекта уже открыт, т.е. имеется его описатель, то к объекту можно обратиться по относительному к каталогу-контейнеру пути. При этом описатель каталога-контейнера должен быть помещен в поле RootDirectory. Например, если мы уже открыли каталог \??\c:\FileWorks\ и поместили его описатель в переменную hDirectory, то можем использовать относительный путь файла test.txt таким образом:


 InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("test.txt"), OBJ_CASE_INSENSITIVE, hDirectory, NULL

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

Для перечисления содержимого системного каталога \%SystemRoot%\System32\Drivers\ мы как раз и будем использовать относительный путь.


     InitializeObjectAttributes addr oa, $CCOUNTED_UNICODE_STRING("\\SystemRoot"), \
                                 OBJ_CASE_INSENSITIVE + OBJ_KERNEL_HANDLE, NULL, NULL

Заполняем структуру OBJECT_ATTRIBUTES, используя символьную ссылку \SystemRoot - этот путь абсолютный.


     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 позволит перечислять содержимое каталога.


     .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".


         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\ по относительному пути.


             mov cb, sizeof FILE_DIRECTORY_INFORMATION + 256

             invoke ExAllocatePool, PagedPool, cb
             .if eax != NULL

Выделяем небольшой буфер, в который должна поместиться структура FILE_DIRECTORY_INFORMATION и имя файла.


                 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, пожалуй, будет не лишним.


 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 начать просмотр содержимого каталога.


                 .while eax != STATUS_NO_MORE_FILES

Запускаем цикл, который крутится до тех пор, пока ZwQueryDirectoryFile не вернет STATUS_NO_MORE_FILES, т.е. пока все файлы в каталоге не будут перечислены.


                     .if ( eax == STATUS_SUCCESS )

Если вдруг в просматриваемом нами каталоге окажется файл, длина имени которого превысит 256 байт (что, кстати, практически невероятно, т.к. имена драйверов не превышают 8 символов), ZwQueryDirectoryFile вернет код ошибки отличный от STATUS_NO_MORE_FILES. В этом случае мы просто пропускаем такой файл.


                         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

В цикле вызываем ZwQueryDirectoryFile, но параметры ReturnSingleEntry, FileName и RestartScan равны TRUE, NULL и FALSE соответственно. Это заставит функцию ZwQueryDirectoryFile продолжить перечисление файлов.


                 invoke ExFreePool, pfdi
             .endif
             invoke ZwClose, hDriversDirectory
         .endif
         invoke ZwClose, hSystemRootDirectory
     .endif

Отдаем все занятые ресурсы назад.

Исходный код драйвера в архиве. Для компиляции требуется версия KmdKit не ниже 1.5 - берите на сайте.

  [C] Four-F

© 2002-2007 wasm.ru - all rights reserved and reversed