11.1 核心句柄表
11.2 FileWorks驱动程序源代码
11.3 创建目录与文件
11.4 文件对象
11.5 写入文件
11.6 修改文件属性
11.7 读取文件
11.8 向文件追加数据
11.9 截短文件
11.10 删除文件与目录
11.11 列举目录内容
源程序: KmdKit/examples/basic/FileWorks
提供对文件的读写功能是操作系统的一项重要任务。我们来看一下NT家族的操作系统都为我们提供了那些功能。
11.1 核心句柄表
在开始讨论本文的主题之前,我们先来讨论一个重要的问题,我们之前并未对其给予应有的注意。为了取得对象的句柄需要填充OBJECT_ATTRIBUTES结构体——我们已经做过很多遍了,其样子如下:
InitializeObjectAttributes addr oa, addr g_usName, OBJ_CASE_INSENSITIVE, NULL, NULL
初始化了OBJECT_ATTRIBUTES后,我们调用函数创建或打开这个对象并获得其句柄(handle)。但这个句柄进入的是得到上下文的那个进程的句柄表。因为对于进程句柄表是其特有的,所以使用这个句柄就只能在进程自己的上下文中。例如,若是试图在其它进程的上下文中打开这个句柄的话,好的情况下会操作失败,而运气不好的话,要是在这个进程句柄表中有取值相同的句柄——要知道句柄只是一个32位的数(准确地讲是一个位的结构体)——就可能关闭其它对象。甚至如果获得的句柄是驱动句柄,但是是在用户进程中,句柄就会进入这个进程的句柄表并有可能有意或无意地在用户模式下使用对象。不希望的事情却总是发生,这样的情况常常出现。正是因此,内核组件和驱动程序有其特殊性,它们不喜欢使用句柄,而是使用reference to object,这样比较好,只需简单地使用指向内存中对象结构体的指针。为了统计对对象的引用,在对象的首部保存着一个引用计数(reference count)。如果需要像本例和上例中的那样访问对象,就要设计一个循环,在不同的上下文中对其进行访问,让系统将句柄放入核心句柄表中(kernel handle table)。
从Windows 2000开始,在系统中有了专门的核心句柄表。在这个表中的句柄只能内核模式下的任意进程上下文中访问,与进程特有的句柄不同。甚至于,比如说如果在System进程的上下文、在DriverEntry函数中获得句柄,则就不能在用户进程上下文中使用对象。System进程实现了自己私有的句柄表,其与核心句柄表不同。
对于在核心句柄表中的句柄,需要在调用InitializeObjectAttributes宏时显式地设置OBJ_KERNEL_HANDLE标志,形式如下:
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
; 还记得吧, 传递给DbgPrint函数的用于格式化Unicode的代码(%C, %S, %lc, %ls, %wc, %ws, %wZ)只能在
; 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
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
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文件。在1.5版的KmdKit中我加入了这个文件。
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标志。
创建目录和文件用的都是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函数有相当多的参数,所以我在下面给出了它的原型,而且不得不使用了C语言的语义,以显示出输入(IN)、输出(OUT)和可选的参数。
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,本例中不需要为目录指定什么特别的属性。可选参数AllocationSize为0定义了所创建文件的大小为0。对于目录来说这是很自然的。参数ShareAccess定义为什么样的值,就要以什么样的权限访问目录。在本例中,我们不允许其它程序访问目录,故将此参数设为0。参数CreateDisposition定义了该目录已经存在,或者相反,文件不存在时系统的行为。我们使用标志FILE_OPEN_IF,这个标志表示如果目录已经存在,就将其打开。我们向参数CreateOptions传递的是标志FILE_DIRECTORY_FILE和FILE_SYNCHRONOUS_IO_NONALERT的组合。第一个表示要建立的是目录而非文件,无需特别解释。FILE_SYNCHRONOUS_IO_NONALERT定义了()对文件所有的操作都要是同步的,例如,调用ZwReadFile后在没有实际读取完文件数据时函数不会返回。在I/O管理器中为文件维护了一个当前文件位置上下文(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结构体中会有额外的信息。
还记得我们是如何处理I/O请求的(见前面的章节)。例如,在驱动程序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结构体都对应着同一个磁盘文件。第一个FILE_OBJECT结构体将设置ReadAccess域,第二个则设置WriteAccess域。例如,试图写入第一个句柄指向的文件时,系统会发现域WriteAc