Windows驱动开发之文件操作

转载请注明来源:
enjoy5512的博客 : http://blog.csdn.net/enjoy5512
GitHub : https://github.com/whu-enjoy

1. 在驱动中使用文件

  在Windows执行体中,通过文件对象来代表文件,该文件对象是一种由对象管理器管理的执行体对象。例如:目录也是由文件对象代表的。
内核组件通过对象名来引用文件,即在文件的全路径前面加\DosDevices。(在Windows 2000及后续操作系统中,\??等同于\DosDevices)。例如,文件C:\WINDOWS\example.txt的对象名为\DosDevices\C:\WINDOWS\example.txt。你需要用对象名来打开文件以获取句柄。

使用文件步骤:
 1. 打开文件返回文件句柄。
 2. 调用合适的ZwXxxFile 函数以完成对文件的操作。
 3. 调用ZwClose函数关闭打开的文件句柄。
 
  当打开一个指向文件的文件句柄时,Windows执行体就创建了一个文件对象来代表该文件,同时返回一个代表该对象的文件句柄。因此,对于单个文件来说,会存在多个文件对象的情况。同样,由于用户模式的应用程序可能会复制文件句柄,因此,对于同一个文件对象,也会存在多个文件句柄。只有当所有指向一个文件对象的文件句柄都关闭后,Windows执行体才会删除该文件对象。

1.1 打开文件句柄

  1. 定义一个OBJECT_ATTRIBUTES结构体变量,然后调用InitializeObjectAttributes函数初始化该变量。关键是设置改变量的ObjectName字段为文件对象名。
InitializeObjectAttributes宏,初始化一个OBJECT_ATTRIBUTES结构体,它指定对象句柄的属性,供打开句柄的例程使用。

VOID InitializeObjectAttributes(
[out] POBJECT_ATTRIBUTES InitializedAttributes,
[in] PUNICODE_STRING ObjectName,
[in] ULONG Attributes,
[in] HANDLE RootDirectory,
[in, optional] PSECURITY_DESCRIPTOR SecurityDescriptor
);

参数:
InitializedAttributes [out]
指定要初始化的OBJECT_ATTRIBUTES结构体指针。

ObjectName [in]
一个指向UNICODE字符串对象的指针,它包含将要打开句柄的对象名称。
它必须是一个完整的对象名称或者是相对于RootDirectory参数指定目录的相对路径。

Attributes [in]
指定一个或多个以下列标志:
OBJ_INHERIT
这个句柄可以被当前进程的子进程继承。
OBJ_PERMANENT
此标志仅应用于对象管理器命名的对象。
默认情况下,这样的对象会在关闭所有它们打开的句柄时删除。
如果指定了此标志,对象不会在所有打开的句柄关闭时删除,
驱动可以使用ZwMakeTemporaryObject删除固定的对象。
OBJ_EXCLUSIVE
这个对象只可以有一个打开的句柄。意味着仅有一个进程可以访问此对象。
OBJ_CASE_INSENSITIVE
如果指定了此标志,在对ObjectName参数与已存在的对象名称进行匹配时,会不区分大小写。
OBJ_OPENIF
如果在创建对象的例程中指定此标志,当对象已经存在时,例程将会打开些对象。否则,创建对象的例程会返回值为STATUS_OBJECT_NAME_COLLISION的NTSTATUS代码。
OBJ_KERNEL_HANDLE
指定句柄仅能在内核模式访问。
OBJ_FORCE_ACCESS_CHECK
打开句柄的例程,应执行所有的访问检查,即使是在内核模式下也如此。

RootDirectory [in]
root目录对象句柄,用于ObjectName参数指定的相对路径时。
如果ObjectName参数是一个完整的对象名,RootDirectory是NULL。
使用ZwCreateDirectoryObject获取对象目录的句柄。

SecurityDescriptor [in, optional]
指定对象创建时应用的一个安全描述符。此参数是可选的。对象如果接受默认的安全设置,驱动程序可以指定NULL。更多信息,参见下面的备注节。

返回值:
无

备注:
InitializeObjectAttributes初始化一个OBJECT_ATTRIBUTES结构体,它设置将被打开的对象句柄的属性。调用者可以传递一个此结构的指针到实际打开句柄的例程。驱动程序运行进程上下文中,若要运行在系统进程,需要设置OBJ_KERNEL_HANDLE标志到Attributes参数。这个标志限制,使用此打开的句柄的进程仅能运行在内核模式。否则句柄可以在驱动运行的进程上下文中访问。注意,InitializeObjectAttributes始终设置OBJCECT_ATTRIBUTES的SecurityQualityOfService成员为NULL。如果驱动程序需要一个非空的值,可以直接设置SecurityQualityOfService成员。

2 调用IoCreateFile, ZwCreateFile, 或者 ZwOpenFile,传递上面定义的结构体变量,成功就会返回执行该文件的句柄。

注:驱动一般用ZwCreateFile和ZwOpenFile,IoCreateFile很少使用,当调用ZwCreateFile,ZwOpenFile或IoCreateFile时,Windows执行体创建一个代表该文件的新的文件对象,并返回一个指向该对象的句柄。文件对象一直存在,知道你关闭了所有指向它的文件句柄。
原型:
NTSYSAPI
NTSTATUS
NTAPI
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
);

参数理解:
OUT-FileHandle--------这是一个指向一个变量的指针,用来最后存放file object handleIN-DesiredAccess----这个参数指定一个访问权限,大概有以下的权限:
FILE_ANY_ACCESS 0x0000 // any type
FILE_READ_ACCESS 0x0001 // file & pipe
FILE_READ_DATA 0x0001 // file & pipe
FILE_LIST_DIRECTORY 0x0001 // directory
FILE_WRITE_ACCESS 0x0002 // file & pipe
FILE_WRITE_DATA 0x0002 // file & pipe
FILE_ADD_FILE 0x0002 // directory
FILE_APPEND_DATA 0x0004 // file
FILE_ADD_SUBDIRECTORY 0x0004 // directory
FILE_CREATE_PIPE_INSTANCE 0x0004 // named pipe
FILE_READ_EA 0x0008 // file & directory
FILE_WRITE_EA 0x0010 // file & directory
FILE_EXECUTE 0x0020 // file
FILE_TRAVERSE 0x0020 // directory
FILE_DELETE_CHILD 0x0040 // directory
FILE_READ_ATTRIBUTES 0x0080 // all types
FILE_WRITE_ATTRIBUTES 0x0100 // all types
FILE_ALL_ACCESS // All of the preceding +
STANDARD_RIGHTS_ALL
最后一个权限最大,这里面要注意的是范围问题,有的值只适合目录,有的只适合管道,有的只适合命名管道,有的同时适用,想下这个地方可以做什么文章

ObjectAttributes---指向下面这个结构的一个变量,就是来表明文件对象的属性的。
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; /* type SECURITY_DESCRIPTOR */
PVOID SecurityQualityOfService; /* type SECURITY_QUALITY_OF_SERVICE */
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
这个结构是针对很多对象的,不光是针对文件对象的,所以OBJ_PERMANENT(永久), OBJ_EXCLUSIVE(互斥),and OBJ_OPENLINK这三个实际上对于文件对象来说始终是无效的

IoStatusBlock-----这个也是个指针变量,指向一个叫做IO_STATUS_BLOCK的结构体,最后函数返回的时候,这个结构体的成员 里面要填充一些值,具体的呢就是完成状态,请求操作的一些信息,最重要的一个成员就是Information成员,他显示了函数对文件的处理方式,他的值 可能是下面的几个:
FILE_SUPERSEDED(替代)
FILE_OPENED(打开)
FILE_CREATED(创建)
FILE_OVERWRITTEN(重写)
FILE_EXISTS(存在)
FILE_DOES_NOT_EXIST(文件不存在)

再看下这个IO_STATUS_BLOCK的具体结构:
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
} DUMMYUNIONNAME;
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

AllocationSize---这是个可选的参数,他是指定初始化文件需要的内存字节数的,所以可向而知,他只有在真正的涉及到文件创建的时候才有意义,也就是说创建,重写,替换这些操作的时候。指向一个LARGE_INTEGER:
typedef union _LARGE_INTEGER {
_ANONYMOUS_STRUCT struct
{
ULONG LowPart;
LONG HighPart;
} DUMMYSTRUCTNAME;
struct
{
ULONG LowPart;
LONG HighPart;
} u;
#endif //MIDL_PASS
LONGLONG QuadPart;
} LARGE_INTEGER, *PLARGE_INTEGER;

FileAttributes---这个参数指定文件的属性,刚才是文件对象的属性。可以是以下的:
FILE_ATTRIBUTE_READONLY
FILE_ATTRIBUTE_HIDDEN
FILE_ATTRIBUTE_SYSTEM
FILE_ATTRIBUTE_DIRECTORY
FILE_ATTRIBUTE_ARCHIVE
FILE_ATTRIBUTE_NORMAL
FILE_ATTRIBUTE_TEMPORARY
FILE_ATTRIBUTE_SPARSE_FILE
FILE_ATTRIBUTE_REPARSE_POINT
FILE_ATTRIBUTE_COMPRESSED
FILE_ATTRIBUTE_OFFLINE
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED
FILE_ATTRIBUTE_ENCRYPTED

ShareAccess---指定共享的权限,以下三种的组合:
FILE_SHARE_READ
FILE_SHARE_WRITE
FILE_SHARE_DELETE

CreateDisposition---这个参数指定要对文件干嘛,可以是下面的值:
FILE_SUPERSEDE
FILE_OPEN
FILE_CREATE
FILE_OPEN_IF
FILE_OVERWRITE
FILE_OVERWRITE_IF


CreateOptions---这个参数指定创建或者打开文件的时候做的一些事情,可以是以下的组合:
FILE_DIRECTORY_FILE
FILE_WRITE_THROUGH
FILE_SEQUENTIAL_ONLY
FILE_NO_INTERMEDIATE_BUFFERING
FILE_SYNCHRONOUS_IO_ALERT
FILE_SYNCHRONOUS_IO_NONALERT
FILE_NON_DIRECTORY_FILE
FILE_CREATE_TREE_CONNECTION
FILE_COMPLETE_IF_OPLOCKED
FILE_NO_EA_KNOWLEDGE
FILE_OPEN_FOR_RECOVERY
FILE_RANDOM_ACCESS
FILE_DELETE_ON_CLOSE
FILE_OPEN_BY_FILE_ID
FILE_OPEN_FOR_BACKUP_INTENT
FILE_NO_COMPRESSION
FILE_RESERVE_OPFILTER
FILE_OPEN_REPARSE_POINT
FILE_OPEN_NO_RECALL
FILE_OPEN_FOR_FREE_SPACE_QUERY

EaBuffer---这个是个可选的参数,用来存放一些扩展的属性
EaLength---存放扩展属性的字节大小

返回值理解:
如果成功,返回STATUS_SUCCESS
如果失败,返回
STATUS_ACCESS_DENIED,
STATUS_OBJECT_NAME_NOT_FOUND, STATUS_OBJECT_NAME_COLLISION,
STATUS_OBJECT_NAME_INVALID, STATUS_SHARING_VIOLATION, STATUS_NOT_A_DIRECTORY, or
STATUS_FILE_IS_A_DIRECTORY.

1.2 文件操作

下表列出了驱动中常用的利用文件句柄操作文件的函数

操作 函数
读文件 ZwReadFile
写文件 ZwWriteFile
读文件属性 ZwQueryInformationFile
设置文件属性 ZwSetInformationFile
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);

各参数的简要介绍如下所示:
FileHandle:函数ZwCreateFile 返回的句柄。如果它是一个内核句柄,则ZwReadFile 和ZwCreateFile 并不需要在同一个进程中,因为内核句柄是各进程通用的。
Event :一个事件,用于异步完成读时;我们忽略这个参数。
ApcRoutine Apc:回调例程,用于异步完成读时;我们忽略这个参数。
IoStatusBlock:返回结果状态,与ZwCreateFile 中的同名参数相同。
Buffer:缓冲区,如果读取文件的内容成功,则内容将被读取到这里。
Length:描述缓冲区的长度,即试图读取文件的长度。
ByteOffset:要读取的文件的偏移量,也就是要读取的内容在文件中的位置。一般来说,不要将其设置为NULL,文件句柄不一定支持直接读取当前偏移。
Key:读取文件时用的一种附加信息,一般不使用。

当函数执行成功时返回STATUS_SUCCESS,实际上只要能够读取到任意字节的数据(不管它是否符合参数Length 的要求),都返回成功;但是,如果仅读取文件长度之外的部分,则返回STATUS_END_OF_FILE。ZwWriteFile 的参数与ZwReadFile 基本相同。
ZwQueryInformationFile与ZwSetInformationFile参数基本相同
ZwSetInformationFile 函数:
NTSTATUS
ZwSetInformationFile(
IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass
);

Parameters

FileHandle [in]
Handle to the file object. This handle is created by a successful call to ZwCreateFile or ZwOpenFile.

IoStatusBlock [out]
Pointer to an IO_STATUS_BLOCK structure that receives the final completion status and information about the requested operation. The Information member receives the number of bytes set on the file.

FileInformation [in]
Pointer to a buffer that contains the information to set for the file. The particular structure in this buffer is determined by the FileInformationClass parameter. Setting any member of the structure to zero tells ZwSetInformationFile to leave the current information about the file for that member unchanged.

Length [in]
The size, in bytes, of the FileInformation buffer.

FileInformationClass [in]
The type of information, supplied in the buffer pointed to by FileInformation, to set for the file. Device and intermediate drivers can specify any of the following FILE_INFORMATION_CLASS values.

2. 代码示例

//程序说明开始
//==================================================================================
// 功能 : 文件操作函数
// 参数 : 
// (入口) 
// (出口) 无
// 返回 : VOID
// 主要思路 : 
// 调用举例 : 
// 日期 : 2016年7月5日 21:07:02 - 2016年7月5日 23:47:35
//==================================================================================
//程序说明结束
VOID FileOption()
{
    HANDLE SourceFileHandle = NULL;      //源文件句柄
    HANDLE TargetFileHandle = NULL;      //目标文件句柄
    NTSTATUS Status = STATUS_SUCCESS;    //返回状态
    OBJECT_ATTRIBUTES ObjectAttributes;  //OBJECT_ATTRIBUTES结构
    UNICODE_STRING SourceFilePath = RTL_CONSTANT_STRING(L"\\??\\c:\\source.txt"); //源文件
    UNICODE_STRING TargetFilePath = RTL_CONSTANT_STRING(L"\\??\\c:\\target.txt"); //目标文件
    UNICODE_STRING String = {0};           //指向Buffer
    IO_STATUS_BLOCK IoStatusBlock;         //返回结果状态结构体
    PVOID Buffer = NULL;                   //buffer指针
    USHORT Length = 0;                     //要读写的长度
    LARGE_INTEGER Offset = {0};            //要读写的偏移

    //初始化OBJECT_ATTRIBUTES结构体
    InitializeObjectAttributes(
        &ObjectAttributes,
        &SourceFilePath,
        OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
        NULL,
        NULL);

    //以FILE_OVERWRITE_IF方式打开
    Status = ZwCreateFile(
        &SourceFileHandle,
        GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,
        &ObjectAttributes,
        &IoStatusBlock,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_READ,
        FILE_OVERWRITE_IF,
        FILE_NON_DIRECTORY_FILE|
        FILE_RANDOM_ACCESS|
        FILE_SYNCHRONOUS_IO_NONALERT,
        NULL,
        0);
    if (!NT_SUCCESS(Status))
    {
        DbgPrint("Open source file fault !! - %#x\n", Status);
        return Status;
    }

    //字符串操作
    //动态申请内存
    Buffer = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, 1024, 'Tag1');
    if (NULL == Buffer)
    {
        DbgPrint("申请Buffer失败!!\n");
        ZwClose(SourceFileHandle);
        Status = STATUS_INSUFFICIENT_RESOURCES;
        return Status;
    }
    //初始化字符串指针
    RtlInitEmptyUnicodeString(&String, Buffer, 512*sizeof(WCHAR));
    //拷贝字符串
    RtlCopyUnicodeString(&String, &SourceFilePath);
    //追加Unicode变量
    RtlAppendUnicodeStringToString(&String, &TargetFilePath);
    //追加字符串
    RtlAppendUnicodeToString(&String, L"别问我这是啥");

    //写入文件
    Length = String.Length;
    Offset.QuadPart = 0;
    Status = ZwWriteFile(
        SourceFileHandle,
        NULL,
        NULL,
        NULL,
        &IoStatusBlock,
        Buffer,
        Length,
        &Offset,
        NULL);
    if (!NT_SUCCESS(Status))
    {
        DbgPrint("写入源文件失败!!\n - %#X", Status);
        ZwClose(SourceFileHandle);
        ExFreePool(Buffer);
        return Status;
    }

    //初始化OBJECT_ATTRIBUTES结构体
    InitializeObjectAttributes(
        &ObjectAttributes,
        &TargetFilePath,
        OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE,
        NULL,
        NULL);

    //打开目标文件
    Status = ZwCreateFile(
        &TargetFileHandle,
        GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,
        &ObjectAttributes,
        &IoStatusBlock,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        NULL,
        FILE_OVERWRITE_IF,
        FILE_NON_DIRECTORY_FILE|
        FILE_RANDOM_ACCESS|
        FILE_SYNCHRONOUS_IO_NONALERT,
        NULL,
        0);
    if (!NT_SUCCESS(Status))
    {
        DbgPrint("目标文件打开失败!! - %#X", Status);
        ZwClose(SourceFileHandle);
        ExFreePool(Buffer);
        return Status;
    }

    //初始化文件指针并循环复制文件,每次复制一个字节
    Offset.QuadPart = 0;
    while(1)
    {
        //读取源文件
        Status = ZwReadFile(
            SourceFileHandle,
            NULL,
            NULL,
            NULL,
            &IoStatusBlock,
            Buffer,
            1,
            &Offset,
            NULL);
        if (!NT_SUCCESS(Status))
        {
            if (STATUS_END_OF_FILE == NULL)
            {
                Status = STATUS_SUCCESS;
            }
            break;
        }

        //写入目标文件
        Status = ZwWriteFile(
            TargetFileHandle,
            NULL,
            NULL,
            NULL,
            &IoStatusBlock,
            Buffer,
            1,
            &Offset,
            NULL);
        Offset.QuadPart += 1;
    }

    //释放指针,释放申请的内存
    ZwClose(SourceFileHandle);
    ZwClose(TargetFileHandle);
    ExFreePool(Buffer);
}

你可能感兴趣的:(文件操作,字符串操作,驱动开发,动态内存申请)