24、Windows派遣函数(2)-Windows驱动开发详解笔记,直接读写方式

简介:   1、直接读写方式 操作系统将用户模式下的缓冲区锁住,然后操作系统将这段缓冲区在内核模式地址再映射一遍。这样,用户模式的缓冲区和内核模式的缓冲区指向的是同一区域的物理地址。 操作系统将用户模式的地址锁定后,用内存描述符MDL记录这段内存。

 

1、直接读写方式

操作系统将用户模式下的缓冲区锁住,然后操作系统将这段缓冲区在内核模式地址再映射一遍。这样,用户模式的缓冲区和内核模式的缓冲区指向的是同一区域的物理地址。

操作系统将用户模式的地址锁定后,用内存描述符MDL记录这段内存。

wps_clip_image-25695

MDL 示意图

比如mdl->ByteCount就是记录的虚拟内存的大小。可以用几个宏来得到其值。

The MmGetMdlByteCount macro returns the length in bytes of the buffer described by a given MDL.

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

ReadFile的第三个参数为希望读的长度,等于stack->Parameters.Read.Length.派遣函数设置pRrp->IoStatus.Infomation告诉ReadFile实际读了多少字节,对应第四个参数。

pIrp->MdlAddress记录了MDL数据结构

示例代码 P206

代码
 
   
1 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
2 IN PIRP pIrp)
3 {
4 KdPrint(( " Enter HelloDDKRead\n " ));
5 // 扩展设备
6   PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj -> DeviceExtension;
7 NTSTATUS status = STATUS_SUCCESS;
8 // 当前I/O堆栈
9   PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
10 // 获取指定的的读字节数
11   ULONG ulReadLength = stack -> Parameters.Read.Length;
12 KdPrint(( " ulReadLength:%d\n " ,ulReadLength));
13
14 // 得到锁到的缓冲区的长度
15   ULONG mdl_length = MmGetMdlByteCount(pIrp -> MdlAddress);
16 // 得到锁到的缓冲区的首地址
17   PVOID mdl_address = MmGetMdlVirtualAddress(pIrp -> MdlAddress);
18 // 得到锁到的缓冲区的偏移量
19 ULONG mdl_offset = MmGetMdlByteOffset(pIrp -> MdlAddress);
20
21 KdPrint(( " mdl_address:0X%08X\n " ,mdl_address));
22 KdPrint(( " mdl_length:%d\n " ,mdl_length));
23 KdPrint(( " mdl_offset:%d\n " ,mdl_offset));
24
25 if (mdl_length != ulReadLength)
26 {
27 // MDL的长度应该和读长度相等,否则该操作应该设为不成功
28 pIrp -> IoStatus.Information = 0 ;
29 status = STATUS_UNSUCCESSFUL;
30 } else
31 {
32 // 用MmGetSystemAddressForMdlSafe得到MDL在内核模式下的映射
33 PVOID kernel_address = MmGetSystemAddressForMdlSafe(pIrp -> MdlAddress,NormalPagePriority);
34 KdPrint(( " kernel_address:0X%08X\n " ,kernel_address));
35 memset(kernel_address, 0XAA ,ulReadLength);
36 pIrp -> IoStatus.Information = ulReadLength; // bytes xfered
37 }
38
39 pIrp -> IoStatus.Status = status;
40
41 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
42 KdPrint(( " Leave HelloDDKRead\n " ));
43
44 return status;
45 }
46
2IO设备控制操作

The DeviceIoControl function sends a control code directly to a specified device driver, causing the corresponding device to perform the corresponding operation.

来进行读写,及应用程序与驱动间的通信。需要一个I/O控制码,DeviceIoControl 把这个控制码和请求一起传递给驱动程序,在派遣函数中分别对不同I/O控制码进行处理。

DeviceIoControl->lpBytesReturned 对应着IRP中的pIrp->IoStatus.Infomation.

wps_clip_image-23998

DeviceIoControl的内部,用户提供的输入缓冲区内容被复制到IRP中的pIrp->Associate.SystemBuffer内存地址,复制的字节数由DeviceIoControl指定的输入字节。

派遣函数读取pIrp->Associate.SystemBuffer内存地址,从而获取应用程序提供的输入数据。派遣函数还可以写入pIrp->Associate.SystemBuffer的内存地址,这被当作设备输出数据。系统会把这个地址的数据再次复制到DeviceIoControl提供的缓冲区。复制的字节由pIrp->IoStatus.Infomation指定。DeviceIoControl通过第7个参数得到这个字节数。

示例代码 P211

代码
 
   
1 #pragma PAGEDCODE
2 NTSTATUS HelloDDKDeviceIOControl(IN PDEVICE_OBJECT pDevObj,
3 IN PIRP pIrp)
4 {
5 NTSTATUS status = STATUS_SUCCESS;
6 KdPrint(( " Enter HelloDDKDeviceIOControl\n " ));
7
8 // 得到当前堆栈
9 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
10 // 得到输入缓冲区大小
11 ULONG cbin = stack -> Parameters.DeviceIoControl.InputBufferLength;
12 // 得到输出缓冲区大小
13 ULONG cbout = stack -> Parameters.DeviceIoControl.OutputBufferLength;
14 // 得到IOCTL码
15 ULONG code = stack -> Parameters.DeviceIoControl.IoControlCode;
16
17 ULONG info = 0 ;
18
19 switch (code)
20 { // process request
21 case IOCTL_TEST1:
22 {
23 KdPrint(( " IOCTL_TEST1\n " ));
24 // 缓冲区方式IOCTL
25 // 显示输入缓冲区数据
26 UCHAR * InputBuffer = (UCHAR * )pIrp -> AssociatedIrp.SystemBuffer;
27 for (ULONG i = 0 ;i < cbin;i ++ )
28 {
29 KdPrint(( " %X\n " ,InputBuffer[i]));
30 }
31
32 // 操作输出缓冲区
33 UCHAR * OutputBuffer = (UCHAR * )pIrp -> AssociatedIrp.SystemBuffer;
34 memset(OutputBuffer, 0xAA ,cbout);
35 // 设置实际操作输出缓冲区长度
36 info = cbout;
37 break ;
38 }
39 case IOCTL_TEST2:
40 {
41 KdPrint(( " IOCTL_TEST2\n " ));
42 // 缓冲区方式IOCTL
43 // 显示输入缓冲区数据
44
45 // 缓冲区方式IOCTL
46 // 显示输入缓冲区数据
47 UCHAR * InputBuffer = (UCHAR * )pIrp -> AssociatedIrp.SystemBuffer;
48 for (ULONG i = 0 ;i < cbin;i ++ )
49 {
50 KdPrint(( " %X\n " ,InputBuffer[i]));
51 }
52
53 // pIrp->MdlAddress为DeviceIoControl输出缓冲区地址相同
54 KdPrint(( " User Address:0X%08X\n " ,MmGetMdlVirtualAddress(pIrp -> MdlAddress)));
55
56 UCHAR * OutputBuffer = (UCHAR * )MmGetSystemAddressForMdlSafe(pIrp -> MdlAddress,NormalPagePriority);
57 // InputBuffer被映射到内核模式下的内存地址,必定在0X80000000-0XFFFFFFFF之间
58 memset(OutputBuffer, 0xAA ,cbout);
59 // 设置实际操作输出缓冲区长度
60 info = cbout;
61 break ;
62 }
63 case IOCTL_TEST3:
64 {
65 KdPrint(( " IOCTL_TEST3\n " ));
66 // 缓冲区方式IOCTL
67
68 // 缓冲区方式IOCTL
69 // 显示输入缓冲区数据
70 UCHAR * UserInputBuffer = (UCHAR * )stack -> Parameters.DeviceIoControl.Type3InputBuffer;
71 KdPrint(( " UserInputBuffer:0X%0X\n " ,UserInputBuffer));
72
73 // 得到用户模式地址
74 PVOID UserOutputBuffer = pIrp -> UserBuffer;
75
76 KdPrint(( " UserOutputBuffer:0X%0X\n " ,UserOutputBuffer));
77
78 __try
79 {
80 KdPrint(( " Enter __try block\n " ));
81
82 // 判断指针是否可读
83 ProbeForRead(UserInputBuffer,cbin, 4 );
84 // 显示输入缓冲区内容
85 for (ULONG i = 0 ;i < cbin;i ++ )
86 {
87 KdPrint(( " %X\n " ,UserInputBuffer[i]));
88 }
89
90 // 判断指针是否可写
91 ProbeForWrite(UserOutputBuffer,cbout, 4 );
92
93 // 操作输出缓冲区
94 memset(UserOutputBuffer, 0xAA ,cbout);
95
96 // 由于在上面引发异常,所以以后语句不会被执行!
97 info = cbout;
98
99 KdPrint(( " Leave __try block\n " ));
100 }
101 __except(EXCEPTION_EXECUTE_HANDLER)
102 {
103 KdPrint(( " Catch the exception\n " ));
104 KdPrint(( " The program will keep going\n " ));
105 status = STATUS_UNSUCCESSFUL;
106 }
107
108 info = cbout;
109 break ;
110 }
111
112 default :
113 status = STATUS_INVALID_VARIANT;
114 }
115
116 // 完成IRP
117 pIrp -> IoStatus.Status = status;
118 pIrp -> IoStatus.Information = info; // bytes xfered
119 IoCompleteRequest( pIrp, IO_NO_INCREMENT );
120
121 KdPrint(( " Leave HelloDDKDeviceIOControl\n " ));
122
123 return status;
124 }
参考

[1]驱动详解

目录
相关文章
|
3月前
|
缓存 网络协议 数据安全/隐私保护
[运维笔记] - (命令).Windows server常用网络相关命令总结
[运维笔记] - (命令).Windows server常用网络相关命令总结
177 0
|
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调试源码笔记
107 0
|
4月前
|
监控 安全 API
5.9 Windows驱动开发:内核InlineHook挂钩技术
在上一章`《内核LDE64引擎计算汇编长度》`中,`LyShark`教大家如何通过`LDE64`引擎实现计算反汇编指令长度,本章将在此基础之上实现内联函数挂钩,内核中的`InlineHook`函数挂钩其实与应用层一致,都是使用`劫持执行流`并跳转到我们自己的函数上来做处理,唯一的不同的是内核`Hook`只针对`内核API`函数,但由于其身处在`最底层`所以一旦被挂钩其整个应用层都将会受到影响,这就直接决定了在内核层挂钩的效果是应用层无法比拟的,对于安全从业者来说学会使用内核挂钩也是很重要。
38 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`将解密如何实现屏蔽特定驱动的加载。
30 0
7.4 Windows驱动开发:内核运用LoadImage屏蔽驱动
|
5天前
|
监控 安全 API
7.3 Windows驱动开发:内核监视LoadImage映像回调
在笔者上一篇文章`《内核注册并监控对象回调》`介绍了如何运用`ObRegisterCallbacks`注册`进程与线程`回调,并通过该回调实现了`拦截`指定进行运行的效果,本章`LyShark`将带大家继续探索一个新的回调注册函数,`PsSetLoadImageNotifyRoutine`常用于注册`LoadImage`映像监视,当有模块被系统加载时则可以第一时间获取到加载模块信息,需要注意的是该回调函数内无法进行拦截,如需要拦截则需写入返回指令这部分内容将在下一章进行讲解,本章将主要实现对模块的监视功能。
19 0
7.3 Windows驱动开发:内核监视LoadImage映像回调
|
4月前
|
监控 安全 API
7.2 Windows驱动开发:内核注册并监控对象回调
在笔者上一篇文章`《内核枚举进程与线程ObCall回调》`简单介绍了如何枚举系统中已经存在的`进程与线程`回调,本章`LyShark`将通过对象回调实现对进程线程的`句柄`监控,在内核中提供了`ObRegisterCallbacks`回调,使用这个内核`回调`函数,可注册一个`对象`回调,不过目前该函数`只能`监控进程与线程句柄操作,通过监控进程或线程句柄,可实现保护指定进程线程不被终止的目的。
29 0
7.2 Windows驱动开发:内核注册并监控对象回调
|
4月前
|
监控 安全 API
7.6 Windows驱动开发:内核监控FileObject文件回调
本篇文章与上一篇文章`《内核注册并监控对象回调》`所使用的方式是一样的都是使用`ObRegisterCallbacks`注册回调事件,只不过上一篇博文中`LyShark`将回调结构体`OB_OPERATION_REGISTRATION`中的`ObjectType`填充为了`PsProcessType`和`PsThreadType`格式从而实现监控进程与线程,本章我们需要将该结构填充为`IoFileObjectType`以此来实现对文件的监控,文件过滤驱动不仅仅可以用来监控文件的打开,还可以用它实现对文件的保护,一旦驱动加载则文件是不可被删除和改动的。
26 1
7.6 Windows驱动开发:内核监控FileObject文件回调
|
4月前
|
监控 安全 API
6.9 Windows驱动开发:内核枚举进线程ObCall回调
在笔者上一篇文章`《内核枚举Registry注册表回调》`中我们通过特征码定位实现了对注册表回调的枚举,本篇文章`LyShark`将教大家如何枚举系统中的`ProcessObCall`进程回调以及`ThreadObCall`线程回调,之所以放在一起来讲解是因为这两中回调在枚举是都需要使用通用结构体`_OB_CALLBACK`以及`_OBJECT_TYPE`所以放在一起来讲解最好不过。
35 1
6.9 Windows驱动开发:内核枚举进线程ObCall回调

热门文章

最新文章