当前位置: 首页 > news >正文

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

 

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]驱动详解

相关文章:

  • Unity类继承关系 图
  • 通俗易懂的dagger2-入门篇
  • Scom 2007客户端监控
  • 【新手向】TensorFlow 安装教程:RK3399上运行谷歌人工智能
  • 域功能级别与目录林功能级别
  • 关于SVM(support vector machine)----支持向量机的一个故事
  • 静态文件的缓存
  • 广联达面试
  • JavaScript案例开发之扑克游戏
  • 信息系统开发平台OpenExpressApp:【OpenTest】 之 框架实现说明
  • 优秀者是真的优秀
  • PKU 1011
  • 阿里巴巴加强个人信息保护 获中央网信办等四部委点赞
  • mfc框架下osg的内存泄露问题(转)
  • SQL优化系列:别让强制类型转换偷走性能
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 分享一款快速APP功能测试工具
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • echarts花样作死的坑
  • Java 多线程编程之:notify 和 wait 用法
  • mockjs让前端开发独立于后端
  • mysql innodb 索引使用指南
  • Python 反序列化安全问题(二)
  • ubuntu 下nginx安装 并支持https协议
  • 阿里研究院入选中国企业智库系统影响力榜
  • -- 查询加强-- 使用如何where子句进行筛选,% _ like的使用
  • 从输入URL到页面加载发生了什么
  • 电商搜索引擎的架构设计和性能优化
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 罗辑思维在全链路压测方面的实践和工作笔记
  • 前嗅ForeSpider教程:创建模板
  • 驱动程序原理
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 入门到放弃node系列之Hello Word篇
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • SAP CRM里Lead通过工作流自动创建Opportunity的原理讲解 ...
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • #AngularJS#$sce.trustAsResourceUrl
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • (51单片机)第五章-A/D和D/A工作原理-A/D
  • (a /b)*c的值
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (转)Linux整合apache和tomcat构建Web服务器
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .md即markdown文件的基本常用编写语法
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .net 7 上传文件踩坑
  • .NET Framework 服务实现监控可观测性最佳实践
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .NET 解决重复提交问题
  • @font-face 用字体画图标
  • @JsonFormat与@DateTimeFormat注解的使用
  • [ Linux 长征路第二篇] 基本指令head,tail,date,cal,find,grep,zip,tar,bc,unname
  • [1] 平面(Plane)图形的生成算法