在驱动程序开发中,经常会对文件进行操作。与Win32API不同,DDK提供另一套对文件的操作函数。
对文件的创建或者打开都是通过内核函数 ZwCreateFile 实现的。和Windows API类似,这个内核函数返回一个文件句柄,文件的所有操作都是依靠这个句柄进行操作的。在文件操作完毕后,需要关闭这个句柄。
NTSTATUS ZwCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer,
IN ULONG EaLength
);
// FileHandle: 返回打开文件的句柄
// DesiredAccess: 对打开文件操作的描述,读、写或是其他。一般指定为GENERIC_READ 或 GENERIC_WRITE。
// ObjectAttributes:是OBJECT_ATTRIBUTES结构的地址,该结构包含要代开的文件名。
// IoStatusBlock: 指向一个IO_STATUS_BLOCK结构,该结构接收ZwCreateFile操作的结果状态。
// AllocationSize: 是一个指针,指向一个64位整数,该数指定文件初始分配时的大小。该参数仅关系到创建或重写文件操作,如果忽略它,那么文件长度将从0开始,并随着写入增长。
// FileAttributes: 0或FILE_ATTRIBUTE_NORMAL,指定新创建文件的属性。
// ShareAccess: FILE_SHARED_READ或0,指定文件的共享方式。如果仅为读数据而打开文件,则可以与其他线程同时读取该文件。如果为写数据而打开文件,可能不希望其他线程访问该文件。
// CreateDisposition: FILE_OPEN 或 FILE_OVERWRITE_IF, 表明当指定文件存在或不存在时应如何处理。
// CreateOptions: FILE_SYNCHRONOUS_IO_NONALERT, 制定控制打开操作和句柄使用的附加标志位。
// EaBuffer: 一个指针,指向可选的扩展属性区。
// EaLength: 扩展属性区的长度。
这个函数需要填写CreateDisposition参数。如果想打开文件,CreateDisposition参数设置成FILE_OPEN。如果想创建文件,CreateDisposition参数设置成FILE_OVERWRITE_IF。此时,无论文件是否存在,都会创建新文件。
文件名的指定是通过设定第三个参数ObjectAttributes。这个参数是一个 OBJECT_ATTRIBUTES 结构。DDK提供对OBJECT_ATTRIBUTES结构初始化的宏 InitializeObjectAttributes.
VOID InitializeObjectAttributes(
OUT POBJECT_ATTRIBUTES InitializedAttributes,
IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN HANDLE RootDirectory,
IN PSECURITY_DESCRIPTOR RecurityDescriptor
);
// InitializedAttributes:返回的 POBJECT_ATTRIBUTES 结构;
// ObjectName: 对象名称, 用UNICODE_STRING描述,这里设置的是文件名。
// Attributes: 一般设为OBJ_CASE_INSENSITIVE, 对大小写敏感。
// RootDirectory:一般设为NULL。
// RecurityDescriptor: 一般设为NULL。
另外文件名必须是符号链接或者是设备名。符号链接的概念在前面已经说过。如, 盘符“C:”就是一个符号链接。这里应该用“\??\c:”代替,“c:\1.log”要写成"\??\c:\1.log"。
其中, "\??\c:"是符号链接,内核会将它转换成设备名“\Device\HarddiskVolume1”。下面的代码演示了如何在驱动程序中创建文件和打开文件。
(1)创建文件
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iostatus;
HANDLE hfile;
UNICODE_STRING logFileUnicodeStr1;
// 初始化UNICODE_STRING字符串
RtlInitUnicodeString(&logFileUnicodeStr1, L"\\?\\C:\\1.log");
// 或者写成 L"\\Device\\HarddiskVolume1\\1.log"
// 初始化objectAttributes
InitializeObjectAttributes(&objectAttributes,
&logFileUnicodeStr1,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
// 创建文件
NTSTATUS ntStatus = ZwCreateFile(&hfile,
GENERIC_WRITE,
&objectAttributes,
&iostatus,
NULL,
FILE_ATTRIBUTE_NOMAL,
FILE_SHARE_READ,
FILE_OPEN_IF,
FILE_SYNCHARONOUS_IO_NONALERT,
NULL,
0);
if(NT_STATUS(ntStatus))
{
KdPrint(("Create file successfully!\n"));
)
else
{
KdPrint(("Create file unsuccessfully!\n"));
}
// 文件操作
// ……
// 关闭文件句柄
ZwClose(hfile);
(2)打开文件
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK iostatus;
HANDLE hfile;
UNICODE_STRING logFileUnicodeStr1;
// 初始化UNICODE_STRING字符串
RtlInitUnicodeString(&logFileUnicodeStr1, L"\\?\\C:\\1.log");
// 或者写成 L"\\Device\\HarddiskVolume1\\1.log"
// 初始化objectAttributes
InitializeObjectAttributes(&objectAttributes,
&logFileUnicodeStr1,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
// 创建文件
NTSTATUS ntStatus = ZwCreateFile(&hfile,
GENERIC_READ,
&objectAttributes,
&iostatus,
NULL,
FILE_ATTRIBUTE_NOMAL,
FILE_SHARE_READ,
FILE_OPEN, // 打开文件,如果不存在,则返回错误
FILE_SYNCHARONOUS_IO_NONALERT,
NULL,
0);
if(NT_STATUS(ntStatus))
{
KdPrint(("Create file successfully!\n"));
)
else
{
KdPrint(("Create file unsuccessfully!\n"));
}
// 文件操作
// ……
// 关闭文件句柄
ZwClose(hfile);
我们可以观察打开文件和创建文件的不同操作方法。需要注意的是,文件被打开后,一定要记得关闭句柄,关闭句柄使用ZwClose内核函数。