Windows驱动编程基础教程 联系客服

发布时间 : 星期五 文章Windows驱动编程基础教程更新完毕开始阅读01cc64564b73f242326c5f2a

楚狂人Windows驱动编程基础教程

// 返回值

NTSTATUS status;

// 首先初始化含有文件路径的OBJECT_ATTRIBUTES OBJECT_ATTRIBUTES object_attributes;

UNICODE_STRING ufile_name = RTL_CONST_STRING(L”\\\\??\\\\C:\\\\a.dat”); InitializeObjectAttributes( &object_attributes, &ufile_name,

OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL);

// 以OPEN_IF方式打开文件。 status = ZwCreateFile( &file_handle,

GENERIC_READ | GENERIC_WRITE, &object_attributes, &io_status, NULL,

FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN_IF,

FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS |

FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);

值得注意的是路径的写法。并不是像应用层一样直接写“C:\\\\a.dat”。而是写成了“\\\\??\\\\C:\\\\a.dat”。这是因为ZwCreateFile使用的是对象路径。“C:”是一个符号链接对象。符号链接对象一般都在“\\\\??\\\\”路径下。 这种文件句柄的关闭非常简单。调用ZwClose即可。内核句柄的关闭不需要和打开在同一进程中。示例如下:

ZwClose(file_handle);

3.3 文件的读写操作

打开文件之后,最重要的操作是对文件的读写。读与写的方法是对称的。只是参数输入与输出的方向不同。读取文件内容一般用ZwReadFile,写文件一般使用ZwWriteFile。

NTSTATUS ZwReadFile(

IN HANDLE FileHandle,

IN HANDLE Event OPTIONAL,

楚狂人Windows驱动编程基础教程

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);

FileHandle:是前面ZwCreateFile成功后所得到的FileHandle。如果是内核句柄,ZwReadFile和ZwCreateFile并不需要在同一个进程中。句柄是各进程通用的。

Event :一个事件。用于异步完成读时。下面的举例始终用同步读,所以忽略这个参数。请始终填写NULL。

ApcRoutine Apc:回调例程。用于异步完成读时。下面的举例始终用同步读,所以忽略这个参数。请始终填写NULL。

IoStatusBlock:返回结果状态。同ZwCreateFile中的同名参数。

Buffer:缓冲区。如果读文件的内容成功,则内容被被读到这个缓冲里。 Length:描述缓冲区的长度。这个长度也就是试图读取文件的长度。

ByteOffset:要读取的文件的偏移量。也就是要读取的内容在文件中的位置。一般的说,不要设置为NULL。文件句柄不一定支持直接读取当前偏移。

Key:读取文件时用的一种附加信息,一般不使用。设置NULL。

返回值:成功的返回值是STATUS_SUCCESS。只要读取到任意多个字节(不管是否符合输入的Length的要求),返回值都是STATUS_SUCCESS。即使试图读取的长度范围超出了文件本来的大小。但是,如果仅读取文件长度之外的部分,则返回STATUS_END_OF_FILE。

ZwWriteFile的参数与ZwReadFile完全相同。当然,除了读写文件外,有的读者可能会问是否提供一个ZwCopyFile用来拷贝一个文件。这个要求未能被满足。如果有这个需求,这个函数必须自己来编写。下面是一个例子,用来拷贝一个文件。利用到了ZwCreateFile,ZwReadFile和ZwWrite这三个函数。不过作为本节的例子,只举出ZwReadFile和ZwWriteFile的部分:

NTSTATUS MyCopyFile(

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 {

// 这里请用前一小节说到的例子打开target_path和source_path所对应的 // 句柄target和source,并为buffer分配一个页面也就是4k的内存。 ? ?

楚狂人Windows驱动编程基础教程

// 然后用一个循环来读取文件。每次从源文件中读取4k内容,然后往 // 目标文件中写入4k,直到拷贝结束为止。 while(1) {

length = 4*1024; // 每次读取4k。 // 读取旧文件。注意status。 status = ZwReadFile (

source,NULL,NULL,NULL,

&my_io_status,buffer, length,&offset, NULL);

if(!NT_SUCCESS(status)) {

// 如果状态为STATUS_END_OF_FILE,则说明文件 // 的拷贝已经成功的结束了。

if(status == STATUS_END_OF_FILE) status = STATUS_SUCCESS; break; }

// 获得实际读取到的长度。

length = IoStatus.Information;

// 现在读取了内容。读出的长度为length.那么我写入 // 的长度也应该是length。写入必须成功。如果失败, // 则返回错误。

status = ZwWriteFile(

target,NULL,NULL,NULL, &my_io_status,

buffer,length,&offset, NULL);

if(!NT_SUCCESS(status)) break;

// offset移动,然后继续。直到出现STATUS_END_OF_FILE // 的时候才结束。

offset.QuadPart += length; }

} while(0);

// 在退出之前,释放资源,关闭所有的句柄。 if(target != NULL) ZwClose(target); if(source != NULL) ZwClose(source); if(buffer != NULL)

ExFreePool(buffer);

楚狂人Windows驱动编程基础教程

return STATUS_SUCCESS; }

除了读写之外,文件还有很多的操作。比如删除、重新命名、枚举。这些操作将在后面实例中用到时,再详细讲解。