打开文件后,最重要是的操作在是对文件的读/写。读写的方法是对称的知识参数输入与输出方向不同。读取文件内容一般用ZwReadFile,写文件一般用ZwWriteFile。
先看看 ZwReadFile 结构吧:
NTSTATUS ZwReadFile(
_In_ HANDLE FileHandle,
_In_opt_ HANDLE Event,
_In_opt_ PIO_APC_ROUTINE ApcRoutine,
_In_opt_ PVOID ApcContext,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_Out_ PVOID Buffer,
_In_ ULONG Length,
_In_opt_ PLARGE_INTEGER ByteOffset,
_In_opt_ PULONG Key
);
参数解释:
FileHandle:是ZwCreateFile成功后所得到的FileHandle。若是内核句柄,ZwReadFile和ZwCreateFile并不需要在同一个进程中。句柄是个进程通用的。
Event : 一个事件,用于异步完成读时。设置为NULL 即可。
ApcRoutine :回调例程,用于异步完成读时,设置为 NULL即可。
IoStatusBlock :返回结构状态,同ZwCreateFile中的同名参数。
Buffer :缓冲区。如果读文件的内容成功,则分内容被读到这个缓冲区里。
Length :描述缓冲区的长度。这个长度也就是试图读取文件的长度。
ByteOffset : 要读取的文件的偏移量,也就是要读取的内容在文件中的位置,一般不要设置为NULL,文件句柄不一定支持直接读取当前偏移量。
Key : 读取文件时使用的一种附加信息,一般不用,设置为NULL即可。
ZwReadFile 的返回值: 调用成功,则返回STATUS_SUCCESSS。只要读取到的任意多个字节,返回值都是STATUS_SUCCESS。及时试图读取的长度超出了文件本来的大小。但是如果仅是读取文件长度之外的部分,则返回的是:STATUS_END_OF_FILE。
ZwWriteFile 参数与ZwReadFile完全相同。
说了这么多,看看代码,简单的实现了文件的拷贝。
NTSTATUS CopyFile(PUNICODE_STRING target_path, PUNICODE_STRING source_path)
{
//---源文件的句柄
HANDLE target = NULL, source = NULL;
//----拷贝文件的缓冲区
PVOID buffer = NULL;
LARGE_INTEGER offset = { 0 };
IO_STATUS_BLOCK io_status = { 0 };
do
{
//---这里就开始 获取 源文件 和目标路径句柄,并为 buffer分配一个页面。然后就是循环读取文件,再写入文件。
while (1)
{
///--每次读取4KB
length = 4 * 1024;
//---
status = ZwReadFile(
source,
NULL,
NULL,
NULL,
&my_io_status,
buffer,
length,
&offset,
NULL);
if (!NT_SUCCESS(status))
{
//---若状态为STATUS_END_OF_FILE,则文件的拷贝结束。
if (STATUS_END_OF_FILE == status)
{
status = STATUS_SUCCESS;
}
break;
}
//---得到文件被读取到的长度
length = IoStatus.Information;
//---下面开始写文件
status = ZwWriteFile(
target,
NULL,
NULL,
NULL,
&my_io_status,
buffer,
length,
&offset,
NULL);
if (!NT_SUCCESS(status))
{
break;
}
///----继续,直到出现 STATUS_END_OF_FILE的时候结束
offset.QuadPart += length;
}
} while (0);
//---释放资源,关闭句柄
if (NULL !=target)
{
ZwClose(target);
}
if (NULL != source)
{
ZwClose(source);
}
if (NULL != buffer)
{
ExFreePool(buffer);
}
return STATUS_SUCCESS;
}