1. 文件的创建
对文件的创建或者打开都是通过内核函数 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 OPTIONAL,
IN ULONG FileAttributes ,
IN ULONG ShareAccess ,
IN ULONG CreateDisposition ,
IN ULONG CreateOptions ,
IN PVOID EaBuffer OPTIONAL,
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 : 指定文件的共享方式, 0 或者 FILE_SHARE_READ
CreateDisposition : 指定当文件存在或不存在时,该如何处理
CreateOptions : FILE_SYNCHRONOUS_IO_NONALERT ,指定控制打开操作和句柄使用的附加标志位。
EaBuffer : 一个指针,指向可选的扩展属性区
EaLength : 扩展属性区的长度
要创建的文件的文件名是通过第三个参数传入的。这个参数是一个 OBJECT_ATTRIBUTES 得结构。 DDK 提供了对 OBJECT_ATTRIBUTES 初始化的宏:
VOID
InitializeObjectAttributes(
OUT POBJECT_ATTRIBUTES InitializedAttributes ,
IN PUNICODE_STRING ObjectName , // 文件名
IN ULONG Attributes , // 一般为 OBJ_CASE_INSENSITIVE ,对大小写敏感
IN HANDLE RootDirectory , // 一般为 NULL
IN PSECURITY_DESCRIPTOR SecurityDescriptor // 一般为 NULL
);
示例代码:
VOID TetsCreateFile()
{
UNICODE_STRING string;
RtlInitUnicodeString(&string, L"//??//C://1.log");
OBJECT_ATTRIBUTES objattr;
InitializeObjectAttributes(&objattr,
&string, OBJ_CASE_INSENSITIVE, NULL, NULL);
HANDLE hFile;
IO_STATUS_BLOCK iostatus;
NTSTATUS status = ZwCreateFile(&hFile,
GENERIC_READ | GENERIC_WRITE, &objattr, &iostatus,
NULL, FILE_ATTRIBUTE_NORMAL, 0,
FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT,
NULL, 0);
if (!NT_SUCCESS(status))
{
KdPrint((" 文件创建失败! /n"));
}
else
{
KdPrint((" 文件创建成功! /n"));
}
// 文件操作
//......
// 关闭文件句柄
ZwClose(hFile);
}
2. 文件的创建
除了可以用 ZwCreateFile 函数打开文件, DDK 还为我们提供了一个 ZwOpenFile 函数用来打开文件,以简化文件的打开操作。
NTSTATUS
ZwOpenFile(
OUT PHANDLE FileHandle ,
IN ACCESS_MASK DesiredAccess , // 打开权限,一般为 GENERIC_ALL
IN POBJECT_ATTRIBUTES ObjectAttributes ,
OUT PIO_STATUS_BLOCK IoStatusBlock ,
IN ULONG ShareAccess ,
IN ULONG OpenOptions // 打开选项 , 一般为 FILE_SYNCHRONOUS_IO_NONALERT
);
3. 获取或修改文件属性
获取和修改文件属性,包括获取文件大小,获取或修改文件指针位置,获取或修改文件名,获取或修改文件属性(只读属性,隐藏属性),获取或修改文件创建,修改日期。
NTSTATUS
ZwSetInformationFile(
IN HANDLE FileHandle ,
OUT PIO_STATUS_BLOCK IoStatusBlock ,
IN PVOID FileInformation ,
IN ULONG Length ,
IN FILE_INFORMATION_CLASS FileInformationClass
);
NTSTATUS
ZwQueryInformationFile(
IN HANDLE FileHandle ,
OUT PIO_STATUS_BLOCK IoStatusBlock ,
OUT PVOID FileInformation ,
IN ULONG Length ,
IN FILE_INFORMATION_CLASS FileInformationClass
);
FileInformation : 依据 FileInformationClass 不同而不同。
Length : FileInformation 数据的长度
FileInformationClass : 描述修改属性的类型
(1) 当 FileInformationClass 是 FileStandardInformation 时,输入和输出数据是 FILE_STANDARD_INFORMATION 结构体,描述文件的标准信息。
typedef struct FILE_STANDARD_INFORMATION {
LARGE_INTEGER AllocationSize ; // 为文件非配的大小
LARGE_INTEGER EndOfFile ; // 距离文件结尾还有多少字节
ULONG NumberOfLinks ; // 有多少要个链接文件
BOOLEAN DeletePending ; // 是否准备删除
BOOLEAN Directory ; // 是否为目录
} FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
(2) 当 FileInformationClass 是 FileBasicInformation 时,输入和输出数据是 FILE_BASIC_INFORMATION 结构体,描述文件的基本信息。
typedef struct FILE_BASIC_INFORMATION {
LARGE_INTEGER CreationTime ;
LARGE_INTEGER LastAccessTime ;
LARGE_INTEGER LastWriteTime ;
LARGE_INTEGER ChangeTime ;
ULONG FileAttributes ; // 文件属性
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
(3) 当 FileInformationClass 是 FileNameInformation 时,输入和输出数据是 FILE_NAME_INFORMATION 结构体,描述文件名信息。
typedef struct _FILE_NAME_INFORMATION {
ULONG FileNameLength ; // 文件名长度
WCHAR FileName [1]; // 文件名
} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
(4) 当 FileInformationClass 是 FilePositionInformation 时,输入和输出数据是 FILE_POSITION_INFORMATION 结构体,描述文件指针位置信息。
typedef struct FILE_POSITION_INFORMATION {
LARGE_INTEGER CurrentByteOffset ; // 代表当期文件指针为止
} FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
示例代码:
VOID TetsFile()
{
UNICODE_STRING string;
RtlInitUnicodeString(&string, L"//??//C://1.log");
OBJECT_ATTRIBUTES objattr;
InitializeObjectAttributes(&objattr,
&string, OBJ_CASE_INSENSITIVE, NULL, NULL);
HANDLE hFile;
IO_STATUS_BLOCK iostatus;
// 打开文件
NTSTATUS status = ZwCreateFile(&hFile, GENERIC_READ,
&objattr, &iostatus, NULL, FILE_ATTRIBUTE_NORMAL, 0, F
ILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(status))
{
KdPrint((" 文件打开失败! /n"));
}
else
{
KdPrint((" 文件打开成功! /n"));
FILE_BASIC_INFORMATION fbi;
// 查询文件基本信息
status = ZwQueryInformationFile(hFile, &iostatus, &fbi, sizeof(FILE_BASIC_INFORMATION),
FileBasicInformation);
if (NT_SUCCESS(status))
{
KdPrint((" 查询文件属性成功! /n"));
}
// 设置文件只读属性
fbi.FileAttributes |= FILE_ATTRIBUTE_READONLY;
status = ZwSetInformationFile(hFile, &iostatus, &fbi, sizeof(FILE_BASIC_INFORMATION),
FileBasicInformation);
if (NT_SUCCESS(status))
{
KdPrint((" 设置文件只读属性成功! /n"));
}
// 关闭文件句柄
ZwClose(hFile);
}
}
4. 文件的写操作
NTSTATUS
ZwWriteFile(
IN HANDLE FileHandle ,
IN HANDLE Event OPTIONAL, // 一般设为 NULL
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, // 一般设为 NULL
IN PVOID ApcContext OPTIONAL, // 一般设为 NULL
OUT PIO_STATUS_BLOCK IoStatusBlock , // 记录实际写的字节数
IN PVOID Buffer , // 从这个缓冲区中开始往文件里写
IN ULONG Length , // 准备写多少字节
IN PLARGE_INTEGER ByteOffset OPTIONAL, // 从文件的多少偏移开始写
IN PULONG Key OPTIONAL // 一般设为 NULL
);
IoStatusBlock : IoStatusBlock. I nformation 记录实际写了多少字节。
示例代码:
#define BUFFER_SIZE 1024
#pragma INITCODE
VOID TetsFile()
{
UNICODE_STRING string;
RtlInitUnicodeString(&string, L"//??//C://1.log");
OBJECT_ATTRIBUTES objattr;
InitializeObjectAttributes(&objattr, &string, OBJ_CASE_INSENSITIVE,
NULL, NULL);
HANDLE hFile;
IO_STATUS_BLOCK iostatus;
// 打开文件
NTSTATUS status = ZwCreateFile(&hFile, GENERIC_WRITE,
&objattr, &iostatus,
NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_WRITE,
FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT,
NULL, 0);
// 分配 Buffer 空间
PUCHAR pBuffer = (PUCHAR)ExAllocatePool(PagedPool, BUFFER_SIZE);
RtlFillMemory(pBuffer, BUFFER_SIZE, 'A');
// 写文件
status = ZwWriteFile(hFile, NULL, NULL, NULL, &iostatus,
pBuffer, BUFFER_SIZE, NULL, NULL);
LARGE_INTEGER ByteOffset;
ByteOffset.QuadPart = 1024i64; // 设置文件指针偏移
RtlFillMemory(pBuffer, BUFFER_SIZE, 'B');
status = ZwWriteFile(hFile, NULL, NULL, NULL, &iostatus,
pBuffer, BUFFER_SIZE, &ByteOffset, NULL);
// 关闭文件句柄
ZwClose(hFile);
// 释放内存
ExFreePool(pBuffer);
}
5. 文件的读操作
NTSTATUS
ZwReadFile(
IN HANDLE FileHandle ,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock ,
OUT PVOID Buffer ,
IN ULONG Length ,
IN PLARGE_INTEGER ByteOffset OPTIONAL,
IN PULONG Key OPTIONAL
);
如果需要读取整个文件,那么得知道文件的大小,我们可以用 ZwQueryInformationFile 来实现。
示例代码:
#pragma INITCODE
VOID TetsFile()
{
UNICODE_STRING string;
RtlInitUnicodeString(&string, L"//??//C://1.log");
OBJECT_ATTRIBUTES objattr;
InitializeObjectAttributes(&objattr, &string, OBJ_CASE_INSENSITIVE, NULL, NULL);
HANDLE hFile;
IO_STATUS_BLOCK iostatus;
// 打开文件
NTSTATUS status = ZwCreateFile(&hFile, GENERIC_READ, &objattr, &iostatus,
NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
// 获得文件的大小
FILE_STANDARD_INFORMATION fsi;
status = ZwQueryInformationFile(hFile,
&iostatus, &fsi,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation);
// 分配 Buffer 空间
PUCHAR pBuffer = (PUCHAR)ExAllocatePool(PagedPool,
(LONG)fsi.EndOfFile.QuadPart);
// 读文件
status = ZwReadFile(hFile, NULL, NULL, NULL,
&iostatus, pBuffer, (LONG)fsi.EndOfFile.QuadPart, NULL, NULL);
KdPrint(("Read %d bytes/n", iostatus.Information));
// 关闭文件句柄
ZwClose(hFile);
// 释放内存
ExFreePool(pBuffer);
}