驱动开发之 用DeviceIoControl实现应用程序与驱动程序通信

摘要:
例如:#defineIOCTL_TESTCTL_CODE4.下面介绍不同的数据操作模式.METHOD_BUFFERED:缓冲区模式用户提供的输入缓冲区的内容被复制到IRP中的pIrp-˃AssociatedIrp.SystemBuffer内存地址,复制的长度是DeviceIoControl指定的输入字节数。驱动程序输出数据时,还可以向pIrp-˃AssociatedIrp.SystemBuffer中写入,操作系统会将此地址的数据复制到DeviceIoControl的输出缓冲区。直接内存模式中,操作系统会将DeviceIoControl指定的输出缓冲区锁定,然后在内核模式地址下重新映射一段地址。派遣函数中IRP中的pIrp-˃MdlAddress记录DeviceIoControl指定的输出缓冲区。

Ring3测试程序:http://blog.csdn.net/zj510/article/details/8216321

1.readfile和writefile可以实现应用程序与驱动程序通信,另外一个Win32 API 是DeviceIoControl。

应用程序自定义一中IO控制码,然后调用DeviceIoControl函数,IO管理器会产生一个MajorFunction 为IRP_MJ_DEVICE_CONTROL,MinorFunction 为自己定义的控制码的IRP,系统就调用相应的处理IRP_MJ_DEVICE_CONTROL的派遣函数,你在派遣函数中判断MinorFunction,是自定义的控制码你就进行相应的处理。

2.首先介绍一下DeviceIoControl函数

1 BOOL WINAPI DeviceIoControl(
2   _In_         HANDLE hDevice,      //已经打开的设备句柄
3   _In_         DWORD dwIoControlCode,//自定义的控制码,稍后介绍怎么定义
4   _In_opt_     LPVOID lpInBuffer,    //输入缓冲区
5   _In_         DWORD nInBufferSize,  //输入缓冲区的大小
6   _Out_opt_    LPVOID lpOutBuffer,   //输出缓冲区
7   _In_         DWORD nOutBufferSize, //输出缓冲区的大小
8   _Out_opt_    LPDWORD lpBytesReturned, //实际返回的字节数,对应驱动程序中pIrp->IoStatus.Information。
9   _Inout_opt_  LPOVERLAPPED lpOverlapped //重叠操作结构指针。同步设为NULL,DeviceIoControl将进行阻塞调用;否则,应在编程时按异步操作设计
10 );

例如:

1 UCHAR InputBuffer[10];
2 UCHAR OutputBuffer[10];
3 
4 //将输入缓冲区全部置成0XBB
5 memset(InputBuffer,0xBB,10);
6 DWORD dwOutput;
7 //输入缓冲区作为输入,输出缓冲区作为输出
8 BOOL bRet = DeviceIoControl(hDevice, IOCTL_TEST, InputBuffer, 10, OutputBuffer, 10, &dwOutput, (LPOVERLAPPED)NULL);

3.定义IO控制码

#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)

IOCTL_Device_Function:生成的IRP的MinorFunction

DeviceType:设备对象的类型。设备类型可参考:http://blog.csdn.net/liyun123gx/article/details/38058965

Function :自定义的IO控制码。自己定义时取0x800到0xFFF,因为0x0到0x7FF是微软保留的。

Method :数据的操作模式。

METHOD_BUFFERED:缓冲区模式

METHOD_IN_DIRECT:直接写模式

METHOD_OUT_DIRECT:直接读模式

METHOD_NEITHER :Neither模式

Access:访问权限,可取值有:

FILE_ANY_ACCESS:表明用户拥有所有的权限

FILE_READ_DATA:表明权限为只读

FILE_WRITE_DATA:表明权限为可写

也可以 FILE_WRITE_DATA | FILE_READ_DATA:表明权限为可读可写,但还没达到FILE_ANY_ACCESS的权限。

例如:#define IOCTL_TEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

4.下面介绍不同的数据操作模式

(1).METHOD_BUFFERED:缓冲区模式

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

驱动程序输出数据时,还可以向pIrp->AssociatedIrp.SystemBuffer中写入,操作系统会将此地址的数据复制到DeviceIoControl的输出缓冲区。

复制的字节数通过设置pIrp->IoStatus.Information来指定。

派遣函数中通过下面代码得到输入缓冲区输出缓冲的大小以及IOCTL

1 //得到当前堆栈
2 PIO_STACK_LOCATION stack =IoGetCurrentIrpStackLocation(pIrp);
3 //得到输入缓冲区大小
4 ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
5 //得到输出缓冲区大小
6 ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
7 //得到IOCTL码
8 ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;

通过操作pIrp->AssociatedIrp.SystemBuffer来进行数据的输入输出

1 UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
2 for (ULONG i=0;i<cbin;i++)
3 {
4     KdPrint(("%X
",InputBuffer[i]));
5 }
6 
7 //操作输出缓冲区,输出缓冲区和输入缓冲区是一个缓冲区
8 UCHAR* OutputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
9 memset(OutputBuffer,0xAA,cbout);
10 //设置实际操作输出缓冲区长度
11  pIrp->IoStatus.Information = cbout;        

(2) METHOD_IN_DIRECT与METHOD_OUT_DIRECT 直接内存模式

与缓冲模式相同,用户提供的输入缓冲区的内容被复制到IRP中的pIrp->AssociatedIrp.SystemBuffer内存地址,复制的长度是DeviceIoControl指定的输入字节数。

直接内存模式中,操作系统会将DeviceIoControl指定的输出缓冲区锁定,然后在内核模式地址下重新映射一段地址。

派遣函数中IRP中的pIrp->MdlAddress记录DeviceIoControl指定的输出缓冲区。派遣函数应该使用MmGetSystemAddressForMdlSafe将这段内存映射到内核模式下的内存地址。

得到输入输出缓冲区的大小以及IOCTL的方式与缓冲区模式相同。

另外需要注意CTL_CODE设置的权限问题,若以只读方式打开设备,METHOD_IN_DIRECT的IOCTL操作会失败。

派遣函数中处理直接内存模式:

1 //显示输入缓冲区数据
2 UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
3 for (ULONG i=0;i<cbin;i++)
4 {
5     KdPrint(("%X
",InputBuffer[i]));
6 }
7 //pIrp->MdlAddress为DeviceIoControl输出缓冲区地址相同
8 KdPrint(("User Address:0X%08X
",MmGetMdlVirtualAddress(pIrp->MdlAddress)));
9 UCHAR* OutputBuffer = (UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
10 //InputBuffer被映射到内核模式下的内存地址,必定在0X80000000-0XFFFFFFFF之间
11 memset(OutputBuffer,0xAA,cbout);

(3)METHOD_NEITHER :Neither模式

因为此模式直接访问用户模式地址,这是很危险的,所以此模式很少被用到。

使用用户模式地址必须保证调用DeviceIoControl 的线程与派遣函数运行在同一个线程上下文中。

派遣函数得到输入缓冲区的方式与前两种不同,此模式是通过IO堆栈的stack->Parameters.DeviceIoControl.Type3InputBuffer;得到输入缓冲区。

驱动通过pIrp->UserBuffer得到输出缓冲区。

得到输入输出缓冲区的长度与IOCTL的方式与前两种相同。

由于驱动程序的派遣函数不能保证传递进来的用户地址是合法地址,所以要对传入的用户模式地址进行可读写判断。这就需要ProbeForRead函数和ProbeForWrite函数与_try _execpt 结合使用。

下面是驱动派遣函数中Neither模式

1 //显示输入缓冲区数据
2 UCHAR* UserInputBuffer = (UCHAR*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
3 KdPrint(("UserInputBuffer:0X%0X
",UserInputBuffer));
4 
5 //得到用户模式地址
6 PVOID UserOutputBuffer = pIrp->UserBuffer;
7 KdPrint(("UserOutputBuffer:0X%0X
",UserOutputBuffer));
8 
9 __try
10 {
11     KdPrint(("Enter __try block
"));
12 
13     //判断指针是否可读
14     ProbeForRead(UserInputBuffer,cbin,4);
15     //显示输入缓冲区内容
16     for (ULONG i=0;i<cbin;i++)
17 {
18         KdPrint(("%X
",UserInputBuffer[i]));
19 }
20 
21     //判断指针是否可写
22     ProbeForWrite(UserOutputBuffer,cbout,4);
23 
24     //操作输出缓冲区
25     memset(UserOutputBuffer,0xAA,cbout);
26 
27     //如果在上面引发异常,所以以后语句不会被执行!
28     pIrp->IoStatus.Information =cbout;
29 
30     KdPrint(("Leave __try block
"));
31 }
32 __except(EXCEPTION_EXECUTE_HANDLER)
33 {
34     KdPrint(("Catch the exception
"));
35     KdPrint(("The program will keep going
"));
36     status =STATUS_UNSUCCESSFUL;
37 }
38 
39 pIrp->IoStatus.Information = cbout;

免责声明:文章转载自《驱动开发之 用DeviceIoControl实现应用程序与驱动程序通信》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇layui 表格添加删除行Swift 内存管理详解下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Qt 实现简单的TCP通信

这段时间用到了QT的TCP通信,做了初步的学习与尝试,编写了一个客户端和服务器基于窗口通信的小例程。 使用QT的网络套接字需要.pro文件中加入一句: QT += network 一、客户端 1、客户端的代码比服务器稍简单,总的来说,使用QT中的QTcpSocket类与服务器进行通信只需要以下5步: (1)创建QTcpSocket套接字对象 socket...

利用MATLAB仿真节点个数和节点通信半径与网络连通率的关系

一、目的        ①在不同节点个数的情况下,用Matlab拟合出连通率与通信半径的关系曲线。        ②在不同节点通信半径的情况下,用Matlab拟合出连通率与节点个数的关系曲线。 二、方法描述        在1x1的单位矩形中随机部署传感器节点,而且假设每个节点的通信半径一样。在每一组节点个数和节点通信半径下进行1000次试验,进而分别模拟...

深入浅出--iOS的TCP/IP协议族剖析&amp;amp;&amp;amp;Socket

深入浅出--iOS的TCP/IP协议族剖析&&Socket 简介 该篇文章主要回顾--TCP/IP协议族中的TCP/UDP、HTTP;还有Socket。(--该文很干,酝酿了许久!你能耐心看完吗?) 我在这个文章中,列举了常见的TCP/IP族中的协议,今天主角是--传输层协议。 传输层(Transport Layer)是OSI(七...

RTU、DTU、工业网关三者有何区别

随着计算机与传感器技术的发展,能实现检测的数据在电脑上显示,再后来,以太网出现了,延伸了物理传输距离,伴随着 5G/4G/3G 网络、Wi-Fi、蓝牙、zigbee、lora等无线网络传输技术的出现,这么多技术?怎么办,网关能解决,网关能够适配更多协议标准,网关是一个翻译器,对收到的信息要重新打包,实现数据的转换。 举个更简单比喻来说明网关,你想从一个房间...

《图解HTTP》

图解HTTP web及网络基础 http1.0 http1.1 TCP/IP协议族 IP协议 作用:把各种数据包传送给对方 IP地址 指明节点被分配到的地址 MAC地址 网卡所属的固定地址 ARP协议依赖MAC地址进行通信 TCP协议 提供字节流服务:将大块数据分割为报文段进行传输 三次握手 SYN SYN/ACK ACK DNS服...

BACnet标准初探

一、准备知识BACnet是用于智能建筑的通讯协定,是国际标准化组织(ISO)、美国国家标准协会(ANSI)及美国冷冻空调协会(ASHRAE)定义的通讯协定。BACnet针对智能建筑及控制系统的应用所设计的通讯,可用在暖通空调系统(HVAC,包括暖气、通风、空气调节)也可以用在照明控制、门禁系统、火警侦测系统及其相关的设备。优点在于能降低维护系统所需成本并且...