文章目录
文件、注册表、线程
4.1 文件操作
4.1.1 使用 OBJECT_ATTRIBUTES
一般打开文件应该传入这个文件的路径。但是内核并不直接接受一个字符串,所以使用者必须首先填写一个 OBJECT_ATTRIBUTES 结构。文档中并没有公开,但是该结构总是被 InitializeObjectAttributes 初始化。
VOID InitializeObjectAttributes(
OUT POBJECT_ATTRIBUTES InitializedAttributes,
IN PUNICODE_STRING ObjectName,
IN HANDLE RootDirectory,
IN PSECURITY_DESCRIPTOR SecurityDescriptor);
其中 InitializedAttributes 是要初始化的 OBJECT_ATTRIBUTES 结构的指针。objectName 是对象名字字符串,也就是文件名。
在 Windows 内核中,无论是打开文件、著恶策表键还是打卡设备,都会先调用InitializedAttributes 初始化一个 OBJECT_ATTRIBUTES 。
Attributes 只需填写 OBJ_CASE_INSENSITIVE OBJ_KERNEL_HANDLE 即可。OBJ_CASE_INSENSITIVE 意味着字符串不区分大小写。OBJ_KERNEL_HANDLE 表明打开的是一个内核句柄。内核举报比应用层句柄使用更方便,可以不受线程和进程的限制,在任何线程中都可以读写。也不需要顾及当前进程是否有权限访问该文件的问题。如果不使用内核句柄,则有时不得不填写后面的 SecurityDescriptor 参数。
RootDirectory 用于相对开的情况,目前省略。传入NULL即可。
SecurityDescriptor 用于设置安全描述符,设置内核句柄时不需要填写。
4.1.2 打开和关闭文件
下面的函数用于打开一个文件。
NTSTATUS ZwCreateFile(
OUT PHANDLE FileHandle,
IN ACCESS_MASK DesireAccess,
IN POBJECT_ATTRIBUTES Object_Attribute,
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: 一个句柄的指针。如果函数调用成功,那么打开的文件句柄就保存在这个地址里。
DesireAccess :申请的权限。 有两个宏分别组合了常用的读权限和写权限,分别为 GENERIC_READ 和 GENERIC_WRITE。还有一个宏代表全部权限,是 GENERIC_ALL。如果想同步打开文件,要加上 SYNCHRONIZE。不同的权限描述符之间可以用 | (位或) 来组合使用。
Object_Attribute : 对象描述。为 OBJCET_ATTRIBUTES 的结构地址。里面包含了要打开的文件名称,在上一节有介绍。
IoStatusBlock : 该结构在内核开发中经常使用,它往往用于表示一个操作的结果。该结构如下:
typedef struce _IO_STATUS_BLOCK{
union{
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
}IO_STATUS_BLOCK,*PIO_STATUS_BLOCK;
在实际的编程中很少会用到 Pointer,一般返回结果在 Status 中,成功则为 STATUS_SUCCESS ; 否则就返回一个错误码,进一步的信息保存在 Information 中。
AllocationSize : 这是一个指向64位整数的指针。该数定义文件初始分配的大小。该参数仅关系到创建或者重写文件操作,可以忽略。如果忽略它那么文件长度从0开始,并随着写入而增长。
FileAttributes : 这个参数控制新建立的文件属性,一般的设置为 0 或者 FILE_ATTRIBUTE_NORMAL 即可。
ShareAccess : 本代码打开这个文件时,允许别的代码同时打开这个文件的所有的权限,所以称为共享访问。一共有三种共享访问的标志可以设置: FILE_SHARE_READ、FILE_SHARE_WRITE、FILE_SHARE_DELETE 。这三种标志可以用 | 来组合使用。
CreateDisposition : 这个参数说明了本次打开文件的意图。可能的选择如下(不能互相组合):
- FILE_CREATE : 新建文件。如果文件已经存在,则请求失败。
- FILE_OPEN: 打开文件。如果文件不存在,则请求失败。
- FILE_OPEN_IF: 如果文件存在则打开,否则重新创建一个文件。
- FILE_OVERWRITE: 如果文件存在则打开并覆盖,否则重新创建一个。
- FILE_SUPERSEDE: 如果打开的文件已经存在,生成一个新的文件其他它。如果不存在,则简单的重新创建文件。
CreateOptions : 作者经常使用 FILE_NON_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT。 此时文件被同步的打开。同步打开文件的意义在于,以后每次操作文件时,比如写入文件 调用 ZwWriteFile ,在该函数返回时,文件写操作已经完成了,而不会有返回 STATUS_PENDING (未决) 的情况。在非同步文件的情况下,返回未决是常见的。
如果希望每次读写文件都是直接往磁盘上操作。此时 CreateOption 中应该带标志 FILE_NO_INTERMEDIATE_BUFFERING 。但是要注意,对磁盘的操作必须保证文件每次读写都必须以磁盘扇区大小对齐。(常见的是512字节),否则会返回错误。
直接给出该函数调用的示例:
//要返回的文件句柄
HADNLE file_handle = NULL;
//返回值
NTSTATUS status;
//首先初始化含有文件路径的 OBJECT_ATTRIBUTES
OBJECT_ATTRIBUTES object_attributes;
UNICODE_STRING ufile_name = RTL_CONSTANT_STRING(L"\\??\/C:\\a.dat");
InitializeObjectAttributes(
&object_attributes,
&ufile_name,
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
NULL,
NULL);
//以 FILE_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_RANDOM_ACCESS |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
注意路径的写法,并不像是应用层一样直接写 “C:\a.dat”,而是写成 “\??\\C:\\a.dat”。这是因为 ZwCreateFile 使用的是对象路径,而 “C:” 是一个符号链接对象,符号链接对象一般都在 “\??\\” 下。
关闭文件则比较简单,直接使用文件句柄和 ZwClose 函数。
ZwClose(file_handle);
4.1.3 文件读/写操作
打开文件后,最重要的操作是对文件的读写。首先介绍文件的读。
NTSTATUS ZwReadFile(
IN HANDLE FileHandle,
I