zoukankan      html  css  js  c++  java
  • 7、Windows驱动开发技术详解笔记(3) 基本语法回顾

     

     

    3、文件读写

    ring3 我们可以使用CreateFileReadFile WriteFile API,在ring0 同样很相似,不过函数变成了ZwCreateFileZwReadFileZwWriteFile 等内核函数。

    1ZwCreateFilering3CreateFile函数有所不同,它不能直接将需要打开或创建的文件路径传递过去,我们必须首先填写一个OBJECT_ATTRIBUTES结构。

    UNICODE_STRING str;

    OBJECT_ATTRIBUTES obj_attrib;

    RtlInitUnicodeString(&str, L"\\??\\C:\\windows\\notepad.exe");

    InitializeObjectAttributes(&obj_attrib,

    &str, // 需要操作的对象、比如文件或注册表路径等

    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,

    NULL,

    NULL);

    第三个参数OBJ_CASE_INSENSITIVE 表示不区分大小写,OBJ_KERNEL_HANDLE

    表示将要打开的句柄为内核句柄。内核句柄比起应用层句柄有很多的好处,例如它可以不受进程或线程的限制,而且在需要打开一个内核句柄时不需要考虑当前是否有权限访问该文件的问题。

    2)创建、打开文件

    创建和打开文件都可使用ZwCreateFile 函数,它的第一个参数将返回一个文件句柄,所有后续操作都可以通过这个句柄完成,在操作结束后,需要调用ZwClose 关闭句柄。

    http://msdn.microsoft.com/en-us/library/ff566424%28VS.85%29.aspx

    ZwCreateFile 函数的第三个参数就是使用我们此前填写的OBJECT_ATTRIBUTES 结构;它返回的信息通过第四个IO_STATUS_BLOCK 返回;第八、九个参数联合指明了如何打开或创建文件,其中IO_STATUS_BLOCK的定义如下所示:

    typedef struct _IO_STATUS_BLOCK {

    NTSTATUS Status;

    ULONG Information;

    }IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

    http://msdn.microsoft.com/en-us/library/ff550671%28VS.85%29.aspx

    其中Status指明了函数的执行结果,如果执行成功它的值将是STATUS_SUCCESS ,否

    则它将会是一个形如STATUS_XXX 的错误提示。

    此外,DDK 还提供了一个函数ZwOpenFile 用来简化打开文件的操作,它所需要的参数

    ZwCreateFile 更加简洁,使用更加简单。

    3)打开文件

    在内核中读写文件与用户模式下十分相似,它们分别使用ZwReadFile ZwWriteFile函数完成。

    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_FILEZwWriteFile 的参数与ZwReadFile 基本相同。

    4)其它操作

    ZwQueryInformationFileZwSetInformationFile可以分别用来获取和设置文件属性,包括文件大小、文件指针位置、文件属性(如只读、隐藏)、文件创建/修改日期等。

    ZwSetInformationFile 函数:

    NTSTATUS

    ZwSetInformationFile(

    IN HANDLE FileHandle,

    OUT PIO_STATUS_BLOCK IoStatusBlock,

    IN PVOID FileInformation,

    IN ULONG Length,

    IN FILE_INFORMATION_CLASS FileInformationClass

    );

    FileInformationClass指定修改或查询的类别,它可能的值有很多种

    http://msdn.microsoft.com/en-us/library/ff567096%28VS.85%29.aspx

    在内核模式下操作文件的函数不像用户模式下那样丰富,想复制文件就调用CopyFile、想删除文件就调用DeleteFile等,在内核模式下除了读写文件的其他所有操作都是通过这两个ZwQueryInformationZwSetInformationFile 函数完成的,而如何使这两个函数精确完成我们需要的功能,就需要通过FileInformationClass参数来指定。

    5)一个例子如下:

    声明.h

    代码
    1 BOOLEAN
    2
    3 MyCopyFile(IN PUNICODE_STRING ustrDestFile,
    4
    5 IN PUNICODE_STRING ustrSrcFile);
    6
    7 .cpp定义
    8
    9 DriverEntry函数中
    10
    11 UNICODE_STRING ustrSrcFile, ustrDestFile;
    12
    13 RtlInitUnicodeString(&ustrSrcFile, L"\\??\\C:\\windows\\notepad.exe");
    14
    15 RtlInitUnicodeString(&ustrDestFile, L"\\??\\C:\\notepad.exe");
    16
    17  if(MyCopyFile(&ustrDestFile, &ustrSrcFile))
    18
    19 {
    20
    21 KdPrint(("[Test] CopyFile Success!"));
    22
    23 }
    24
    25  else
    26
    27 {
    28
    29 KdPrint(("[Test] CopyFile Error!"));
    30
    31 }
    32
    33 return status;
    34
    35 }
    36
    37 自定义函数:
    38
    39 /************************************************************************
    40
    41 * 函数名称:MyCopyFile
    42
    43 * 功能描述:复制文件
    44
    45 * 参数列表:
    46
    47 ustrDestFile:目的文件
    48
    49 ustrSrcFile:源文件
    50
    51 * 返回 值:返回状态
    52
    53 *************************************************************************/
    54
    55 BOOLEAN
    56
    57 MyCopyFile( IN PUNICODE_STRING ustrDestFile,
    58
    59 IN PUNICODE_STRING ustrSrcFile)
    60
    61 {
    62
    63 HANDLE hSrcFile, hDestFile;
    64
    65 PVOID buffer = NULL;
    66
    67 ULONG length = 0;
    68
    69 LARGE_INTEGER offset = {0};
    70
    71 IO_STATUS_BLOCK Io_Status_Block = {0};
    72
    73 OBJECT_ATTRIBUTES obj_attrib;
    74
    75 NTSTATUS status;
    76
    77 BOOLEAN bRet = FALSE;
    78
    79 do
    80
    81 { // 打开源文件
    82
    83 InitializeObjectAttributes(&obj_attrib,
    84
    85 ustrSrcFile,
    86
    87 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
    88
    89 NULL,
    90
    91 NULL);
    92
    93 status = ZwCreateFile(&hSrcFile,
    94
    95 GENERIC_READ,
    96
    97 &obj_attrib,
    98
    99 &Io_Status_Block,
    100
    101 NULL,
    102
    103 FILE_ATTRIBUTE_NORMAL,
    104
    105 FILE_SHARE_READ,
    106
    107 FILE_OPEN,
    108
    109 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
    110
    111 NULL, 0);
    112
    113 if (!NT_SUCCESS(status))
    114
    115 {
    116
    117 bRet = FALSE;
    118
    119 goto END;
    120
    121 }
    122
    123 // 打开目标文件
    124
    125 InitializeObjectAttributes(&obj_attrib,
    126
    127 ustrDestFile,
    128
    129 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
    130
    131 NULL,
    132
    133 NULL);
    134
    135 status = ZwCreateFile(&hDestFile,
    136
    137 GENERIC_WRITE,
    138
    139 &obj_attrib,
    140
    141 &Io_Status_Block,
    142
    143 NULL,
    144
    145 FILE_ATTRIBUTE_NORMAL,
    146
    147 FILE_SHARE_READ,
    148
    149 FILE_OPEN_IF,
    150
    151 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
    152
    153 NULL, 0);
    154
    155 if (!NT_SUCCESS(status))
    156
    157 {
    158
    159 bRet = FALSE;
    160
    161 goto END;
    162
    163 }
    164
    165 // 为buffer 分配4KB 空间
    166
    167 buffer = ExAllocatePool(NonPagedPool, 1024 * 4);
    168
    169 if (buffer == NULL)
    170
    171 {
    172
    173 bRet = FALSE;
    174
    175 goto END;
    176
    177 }
    178
    179 // 复制文件
    180
    181 while (1)
    182
    183 {
    184
    185 length = 4 * 1024;
    186
    187 // 读取源文件
    188
    189 status = ZwReadFile(hSrcFile,
    190
    191 NULL,
    192
    193 NULL,
    194
    195 NULL,
    196
    197 &Io_Status_Block,
    198
    199 buffer,
    200
    201 length,
    202
    203 &offset,
    204
    205 NULL);
    206
    207 if (!NT_SUCCESS(status))
    208
    209 {
    210
    211 // 如果状态为STATUS_END_OF_FILE,说明文件已经读取到末尾
    212
    213 if (status == STATUS_END_OF_FILE)
    214
    215 {
    216
    217 bRet = TRUE;
    218
    219 goto END;
    220
    221 }
    222
    223 }
    224
    225 // 获得实际读取的长度
    226
    227 length = Io_Status_Block.Information;
    228
    229 // 写入到目标文件
    230
    231 status = ZwWriteFile(hDestFile,
    232
    233 NULL,
    234
    235 NULL,
    236
    237 NULL,
    238
    239 &Io_Status_Block,
    240
    241 buffer,
    242
    243 length,
    244
    245 &offset,
    246
    247 NULL);
    248
    249 if (!NT_SUCCESS(status))
    250
    251 {
    252
    253 bRet = FALSE;
    254
    255 goto END;
    256
    257 }
    258
    259 // 移动文件指针
    260
    261 offset.QuadPart += length;
    262
    263 }
    264
    265 } while (0);
    266
    267 END:
    268
    269 if (hSrcFile)
    270
    271 {
    272
    273 ZwClose(hSrcFile);
    274
    275 }
    276
    277 if (hDestFile)
    278
    279 {
    280
    281 ZwClose(hDestFile);
    282
    283 }
    284
    285 if (buffer = NULL)
    286
    287 {
    288
    289 ExFreePool(buffer);
    290
    291 }
    292
    293 return bRet;
    294
    295 }

    6)创建文件

    NTSTATUS ZwCreateFile(

    OUT PHANDLE FileHandle,

    IN ACCESS_MASK DesiredAccess,

    IN POBJECT_ATTRIBUTES ObjectAttribute,

    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:是一个句柄的指针。如果这个函数调用返回成成功(STATUS_SUCCESS),那就么打开的文件句柄就返回在这个地址内。

    DesiredAccess:申请的权限。打开写文件用FILE_WRITE_DATA,读文件内容用FILE_READ_DATA,删除文件或者把文件改名用DELETE,想设置文件属性,请使用FILE_WRITE_ATTRIBUTES,读文件属性则使用 FILE_READ_ATTRIBUTES。这些条件可以用|(位或)来组合。有两个宏分别组合了常用的读权限和常用的写权限,GENERIC_READGENERIC_WRITE,GENERIC_ALL代表全部权限。如果想同步的打开文件,加上SYNCHRONIZE

    typedef struct _IO_STATUS_BLOCK {

    union {

    NTSTATUS Status;

    PVOID Pointer;

    };

    ULONG_PTR Information;

    } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

    实际编程中很少用到Pointer。返回的结果在Status中。成功则为STATUS_SUCCESS。否则则是一个错误码。进一步的信息在 Information中。不同的情况下返回的Information的信息意义不同。针对ZwCreateFile调用的情况,Information 的返回值有以下几种可能:

    ·FILE_CREATED:文件被成功的新建了。

    ·FILE_OPENED: 文件被打开了。

    ·FILE_OVERWRITTEN:文件被覆盖了。

    ·FILE_SUPERSEDED: 文件被替代了。

    ·FILE_EXISTS:文件已存在。(因而打开失败了)。

    ·FILE_DOES_NOT_EXIST:文件不存在。(因而打开失败了)。

    这些返回值和打开文件的意图有关(有时希望打开已存在的文件,有时则希望建立新的文件等。

    AllocationSize参数很少使用,请设置为NULL

    FileAttributes控制新建立的文件的属性,设为FILE_ATTRIBUTE_NORMAL

    ShareAccess,实际上,这是在本代码打开这个文件的时候,允许别的代码同时打开这个文件所持有的权限。所以称为共享访问。一共有三种共享标记可以设置:FILE_SHARE_READFILE_SHARE_WRITEFILE_SHARE_DELETE。这三个标记可以用|(位或)来组合。举例如下:如果本次打开只使用了 FILE_SHARE_READ,那么这个文件在本次打开之后,关闭之前,别次打开试图以读权限打开,则被允许,可以成功打开。如果别次打开试图以写权限打开,则一定失败。返回共享冲突。

    CreateDisposition参数说明了这次打开的意图。可能的选择如下(这些选择不能组合):

    ·FILE_CREATE:新建文件。如果文件已经存在,则这个请求失败。

    ·FILE_OPEN:打开文件。如果文件不存在,则请求失败。

    ·FILE_OPEN_IF:打开或新建。如果文件存在,则打开。如果不存在,则失败。

    ·FILE_OVERWRITE:覆盖。如果文件存在,则打开并覆盖其内容。如果文件不存在,这个请求返回失败。

    ·FILE_OVERWRITE_IF:新建或覆盖。如果要打开的文件已存在,则打开它,并覆盖其内存。如果不存在,则简单的新建新文件。

    ·FILE_SUPERSEDE:新建或取代。如果要打开的文件已存在。则生成一个新文件替代之。如果不存在,则简单的生成新文件。

    CreateOption, 笔者使用FILE_NON_DIRECTORY_FILE| FILE_SYNCHRONOUS_IO_NONALERT,此时文件被同步的打开。而且打开的是文件(而不是目录。创建目录请用FILE_ DIRECTORY_FILE)。同步的打开的意义在于,以后每次操作文件的时候,比如写入文件,调用ZwWriteFile,在ZwWriteFile 返回时,文件写操作已经得到了完成。而不会有返回STATUS_PENDING(未决)的情况。在非同步文件的情况下,返回未决是常见的。此时文件请求没有完成,使用者需要等待事件来等待请求的完成。当然,好处是使用者可以先去做别的事情。

    要同步打开,前面的DesiredAccess必须含有SYNCHRONIZE

    此外还有一些其他的情况。比如不通过缓冲操作文件,希望每次读写文件都是直接往磁盘上操作的,此时CreateOptions中应该带标记 FILE_NO_INTERMEDIATE_BUFFERING。带了这个标记后,请注意操作文件每次读写都必须以磁盘扇区大小(最常见的是512字节)对齐,否则会返回错误。

  • 相关阅读:
    类的成员函数实现线程的回调函数
    Devexpress Chart series 点击时获取SeriesPoint的值
    递归树 TreeList
    ChartControl饼状图自定义调色板
    Devexpress GridControl.Export 导出
    .Net Core 实现 自定义Http的Range输出实现断点续传或者分段下载
    Js/Jquery获取网页屏幕可见区域高度
    js获取网页屏幕可视区域高度
    环境变量
    bat批处理文件怎么将路径添加到path环境变量中
  • 原文地址:https://www.cnblogs.com/mydomain/p/1855114.html
Copyright © 2011-2022 走看看