7、Windows驱动开发技术详解笔记(3) 基本语法回顾

简介:     3、文件读写   在ring3 我们可以使用CreateFile、ReadFile 、WriteFile 等API,在ring0 同样很相似,不过函数变成了ZwCreateFile、ZwReadFile、ZwWriteFile 等内核函数。

 

 

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字节)对齐,否则会返回错误。

目录
相关文章
|
3月前
|
缓存 网络协议 数据安全/隐私保护
[运维笔记] - (命令).Windows server常用网络相关命令总结
[运维笔记] - (命令).Windows server常用网络相关命令总结
189 0
|
1月前
|
数据可视化 数据库 C++
Qt 5.14.2揭秘高效开发:如何用VS2022快速部署Qt 5.14.2,打造无与伦比的Windows应用
Qt 5.14.2揭秘高效开发:如何用VS2022快速部署Qt 5.14.2,打造无与伦比的Windows应用
|
3月前
|
存储 Ubuntu 开发工具
ffmpeg笔记(二)windows下和ubuntu-16.04下ffmpeg编译
ffmpeg笔记(二)windows下和ubuntu-16.04下ffmpeg编译
|
4月前
|
Linux API C++
音视频windows安装ffmpeg6.0并使用vs调试源码笔记
音视频windows安装ffmpeg6.0并使用vs调试源码笔记
111 0
|
4月前
|
监控 安全 API
5.9 Windows驱动开发:内核InlineHook挂钩技术
在上一章`《内核LDE64引擎计算汇编长度》`中,`LyShark`教大家如何通过`LDE64`引擎实现计算反汇编指令长度,本章将在此基础之上实现内联函数挂钩,内核中的`InlineHook`函数挂钩其实与应用层一致,都是使用`劫持执行流`并跳转到我们自己的函数上来做处理,唯一的不同的是内核`Hook`只针对`内核API`函数,但由于其身处在`最底层`所以一旦被挂钩其整个应用层都将会受到影响,这就直接决定了在内核层挂钩的效果是应用层无法比拟的,对于安全从业者来说学会使用内核挂钩也是很重要。
40 1
5.9 Windows驱动开发:内核InlineHook挂钩技术
|
4月前
|
监控 API C++
8.4 Windows驱动开发:文件微过滤驱动入门
MiniFilter 微过滤驱动是相对于`SFilter`传统过滤驱动而言的,传统文件过滤驱动相对来说较为复杂,且接口不清晰并不符合快速开发的需求,为了解决复杂的开发问题,微过滤驱动就此诞生,微过滤驱动在编写时更简单,多数`IRP`操作都由过滤管理器`(FilterManager或Fltmgr)`所接管,因为有了兼容层,所以在开发中不需要考虑底层`IRP`如何派发,更无需要考虑兼容性问题,用户只需要编写对应的回调函数处理请求即可,这极大的提高了文件过滤驱动的开发效率。
40 0
|
4月前
|
监控 Windows
7.4 Windows驱动开发:内核运用LoadImage屏蔽驱动
在笔者上一篇文章`《内核监视LoadImage映像回调》`中`LyShark`简单介绍了如何通过`PsSetLoadImageNotifyRoutine`函数注册回调来`监视驱动`模块的加载,注意我这里用的是`监视`而不是`监控`之所以是监视而不是监控那是因为`PsSetLoadImageNotifyRoutine`无法实现参数控制,而如果我们想要控制特定驱动的加载则需要自己做一些事情来实现,如下`LyShark`将解密如何实现屏蔽特定驱动的加载。
32 0
7.4 Windows驱动开发:内核运用LoadImage屏蔽驱动
|
14天前
|
监控 安全 API
7.3 Windows驱动开发:内核监视LoadImage映像回调
在笔者上一篇文章`《内核注册并监控对象回调》`介绍了如何运用`ObRegisterCallbacks`注册`进程与线程`回调,并通过该回调实现了`拦截`指定进行运行的效果,本章`LyShark`将带大家继续探索一个新的回调注册函数,`PsSetLoadImageNotifyRoutine`常用于注册`LoadImage`映像监视,当有模块被系统加载时则可以第一时间获取到加载模块信息,需要注意的是该回调函数内无法进行拦截,如需要拦截则需写入返回指令这部分内容将在下一章进行讲解,本章将主要实现对模块的监视功能。
30 0
7.3 Windows驱动开发:内核监视LoadImage映像回调
|
4月前
|
监控 安全 API
7.2 Windows驱动开发:内核注册并监控对象回调
在笔者上一篇文章`《内核枚举进程与线程ObCall回调》`简单介绍了如何枚举系统中已经存在的`进程与线程`回调,本章`LyShark`将通过对象回调实现对进程线程的`句柄`监控,在内核中提供了`ObRegisterCallbacks`回调,使用这个内核`回调`函数,可注册一个`对象`回调,不过目前该函数`只能`监控进程与线程句柄操作,通过监控进程或线程句柄,可实现保护指定进程线程不被终止的目的。
29 0
7.2 Windows驱动开发:内核注册并监控对象回调
|
UED Windows 搜索推荐
特别奉献WindowsCE7 技术白皮书
  欢迎下载试用新一代Windows Embedded CE(Compact 7):http://www.microsoft.com/windowsembedded/zh-cn/products/windowsce/default.mspx   Windows Embedded Compact 7引入了多项新的网络和连接技术并更新了Windows Embedded Compact CE 6.0的一些功能。