文件读写程序分为四个函数,即DriverUnload()、DriverEntry()、CreateFileTest()和OpenFileTest()。这四个函数的功能非常清晰,DriverUnload()是卸载例程,DriverEntry()是驱动程序的入口,CreateFileTest()用于新建文件,OpenFileTest()用于打开已建立的文件并读写的函数。
1.创建、打开和关闭 文件
内核驱动通过内核函数创建或打开文件ZwCreateFile()操作Win32 API类似地,将通过参数接收返回的文件句柄。其返回值是操作是否成功的状态码。ZwCreateFile()函数的定义如下:
参数介绍如下。
FileHandle:用于接收创建文件后的文件句柄。
DesiredAccess:打开文件操作描述、读写,一般指定为 GENERIC_READ 或 GENERIC_WRITE;该参数和 CreateFile()函数中的参数相同。
ObjectAttributes:指向 OBJECT_ATTRIBUTES 结构体指针包含要创建或打开的文件名。
IoStatusBlock:指向 IO_STATUS_BLOCK 结构体的指针用于接收操作结果。
AllocationSize:该参数指向文件初始化分配时的一个 64 位整数。
FileAttributes:通常为 FILE_ATTRIBUTE_NORMAL,该参数和 CreateFile()函数中的参数相同。
ShareAccess:指定文件的共享方式可指定为 FILE_SHARE_READ、FILE_SHARE_WRITE 或 FILE_SHARE_DELETE,该参数和 CreateFile()函数中的参数相同。
CreateDisposition:描述这个调用 ZwCreateFile()函数的意图可以指定为 FILE_CREATE、FILE_OPEN、FILE_OPEN_IF 等。
CreateOptions:通常指定为 FILE_SYNCHRONOUS_IO_NONALERT,例如,在写入文件时,调用 ZwWriteFile()函数, ZwWriteFile()调用返回时,已完成文件写作操作。
EaBuffer:该参数表示指针,指向可选的扩展属性区,一般为 NULL。
EaLength:该参数表示扩展属性区的长度,一般为 0。
ZwCreateFile()函数的第 3 参数是指向 OBJECT_ATTRIBUTES 结构体定义如下:
该结构体通常不需要用户逐个进行初始化,而是使用InitializeObjectAttributes()函数初始化,函数定义如下:
从InitializeObjectAttributes()函数的定义可以看出其参数和OBJECT_ATTRIBUTES结构体的成员变量相同。InitializeObjectAttributes()函数参数说明如下。
InitializeAttributes:指向 OBJECT_ATTRIBUTES 结构体指针。
ObjectName:对象名称,用 UNICODE_STRING 描述,对 ZwCreateFile()函数而言,该处指定为文件名。
Attributes:一般设置为 OBJ_CASE_INSENSITIVE,意思是名字字符串不区分大小写。
RootDirectory:一般设置为 NULL。
SecurityDescriptor:设置安全描述符,一般设置为 NULL。
ObjectName 必须使用 UNICODE_STRING描述 类型,UNICODE_STRING 是一种内核宽字符串包装的数据结构,其定义如下:
结构成员说明如下。
Length:单位为字节的字符串参数,如果是 N 个字符,那么 Length 的值为 N 2 倍个字符。
MaximumLength:字节是整个字符缓冲区的更大长度。
Buffer:缓冲区指针。
对于 UNICODE_STRING 型字符串通过 KdPrint()也可以调试输出,输出方式如下:
UNICODE_STRING类型的字符串在使用前需要进行初始化,初始化的 *** 有两种:一种是使用内核函数RtlInitUnicodeString()初始化,另一种 *** 是申请内存空间进行初始化。通常使用之一种 *** 。RtlInitUnicodeString()函数的定义如下:
参数说明如下。
DestinationString:要初始化的 UNICODE_STRING 字符串指针。
SourceString:字符串的内容。
在为InitializeObjectAttributes()函数传输第二个参数时,需要指定的文件名称为符号链接。在应用层下,描述文件的完整路径是“c:\a.txt”;在内核下,描述的方式是“\??\c:\a.txt”。内核模式下的符号链接“\??\”(或者是“\DosDevices\”)开头;在用户模式下使用符号链接“\\.\”开头。
上面介绍的ZwCreateFile()函数不仅可以创建文件,还可以打开文件。但由于参数太多,内核函数专门提供了一个用于打开文件的函数ZwOpenFile()其定义如下:
ZwOpenFile()函数相当于只用于打开文件的简化版本ZwCreateFile()函数,各参数的使用 *** 和ZwCreateFile()函数相同,这里不重复介绍。
使用内核函数关闭文件句柄ZwClose()其定义如下:
函数只包含一个参数,即打开文件的句柄。除了关闭文件句柄外,函数还可以关闭其他类型资源的句柄,如注册表句柄。
2. 文件相关操作
文件相关操作主要介绍四个内核函数,即ZwReadFile()、ZwWriteFile()、ZwQueryInformationFile()和ZwSetInformationFile()。例代码实现了文件的读写操作,判断打开的文件是否为目录,获取文件的长度和设置文件的指针。
首先来看ZwQueryInformationFile()和ZwSetInformationFile()两个函数的定义。ZwQueryIn formationFile()函数的定义如下:
参数说明如下。
FileHandle:打开的文件句柄。
IoStatusBlock:返回设置状态。
FileInformation:依据 FileInformationClass 不同而不同。
Length:FileInformation 数据长度。
FileInformationClass:描述需要获得的属性类型。
ZwSetInformationFile()函数的定义如下:
ZwSetInformationFile()函数参数与 ZwQueryInformationFile()函数的参数几乎相同,但两个函数的第 3 参数略有不同,区别在于 ZwQueryInformationFile()来说是一个输出参数,对于 ZwSetInformationFile()是输入参数。这里一定要注意。
对于ZwQueryInformationFile()和ZwSetInformationFile()对于这两个函数,第五个参数决定了要读取或设置的属性类型,第三个参数根据第五个参数接受或传递相应的值。
两个函数的第五个参数有三种常用值FileStandardInformation、FileBasic Information和FilePositionInformation。每种类型对应不同的结构体,这些结构体是ZwQueryInformationFile()和ZwSetInformationFile()使用函数的第三个参数。
FileStandardInformation对应的结构体定义如下:
FileBasicInformation对应的结构体定义如下:
FilePositionInformation对应的结构体定义如下:
了解第三个参数和第五个参数后,就可以了解第四个参数的值,这是第三个参数的大小。
在上述结构中广泛使用LARGE_INTEGER实际上,数据类型是一个联合体。LARGE_INTEGER定义如下:
该结构主要用于表示64位的整数类型,通常用于QuadPart成员。
ZwReadFile()函数的定义如下:
参数说明如下。
FileHandle:打开文件的句柄。
Event:异步读取时,一般设置为 NULL。
ApcRoutine:回调例程,用于异步完成读取时,一般设置为 NULL。
ApcContext:一般设置为 NULL。
IoStatusBlock:指向 IO_STATUS_BLOCK 指针,记录读取操作状态,IoStatusBlock.Information 用于记录读取的字节数。
Buffer:保存读取文件内容的缓冲区。
Length:准备读取文件内容的字节数。
ByteOffset:指定读取内容的偏移地址。
Key:读取文件时的附加信息一般设置为 NULL。
ZwWriteFile()函数的定义如下:
该函数的参数类似ZwReadFile()函数,Buffer它是一个缓冲区,用于写入文件内容。
3. 内存管理函数
文件读写代码中使用了三个与内存相关的内核函数,即ExAllocatePool()、RtlFillMem ory()和ExFreePool()。
ExAllocatePool()函数用于申请内存空间,定义如下:
参数说明如下。
PoolType:这个参数是枚举值,通常有两个值,即 NonPagedPool 和 PagedPool;前者表示非分页内存,后者表示分页内存;永远不会交换到文件中的虚拟内存称为非分页内存,可以交换到文件中的虚拟内存称为分页内存。
NumberOfBytes:表示需要分配的内存大小。
该函数的返回值内存地址。
RtlFillMemory()填充内存的函数定义如下:
参数说明如下。
Desination:填写内存地址的起始位置。
Length:填充长度。
Fill:字节需要填充。
ExFreePool()函数用于回收 ExAllocatePool()申请的内存空间,其定义如下:
函数只有一个参数,指向ExAllocatePool()指针函数分配内存空间。