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

一.先谈一下这个定义IO控制码 ,其实可以看作是一种通信协议。

 看看CTL_CODE原型:#define CTL_CODE( DeviceType, Function, Method, Access ) ( \((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \)

可以看到,这个宏四个参数,自然是一个32位分成了4部分,高16位存储设备类型,14~15位访问权限,2~13位操作功能,最后0,1两位就是确定缓冲区是如何与I/O和文件系统数据缓冲区进行数据传递方式,最常见的就是METHOD_BUFFERED。

   自定义CTL_CODE:

#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的权限。
   继续介绍这个缓冲区数据传递方式Method:

Method表示Ring3/Ring0的通信中的内存访问方式,有四种方式:
  #define METHOD_BUFFERED 0
  #define METHOD_IN_DIRECT 1
  #define METHOD_OUT_DIRECT 2
  #define METHOD_NEITHER 3

(1)如果使用METHOD_BUFFERED,表示系统将用户的输入输出都经过pIrp->AssociatedIrp.SystemBuffer来缓冲,因此这种方式的通信比较安全。

METHOD_BUFFERED方式相当于对Ring3的输入输出都进行了缓冲。
METHOD_BUFFERED方式(借图):

  (2)如果使用METHOD_IN_DIRECT或METHOD_OUT_DIRECT方式,表示系统会将输入缓冲在pIrp->AssociatedIrp.SystemBuffer中,并将输出缓冲区锁定,然后在内核模式下重新映射一段地址,这样也是比较安全的。

METHOD_IN_DIRECT和METHOD_OUT_DIRECT可称为"直接方式",是指系统依然对Ring3的输入缓冲区进行缓冲,但是对Ring3的输出缓冲区并没有缓冲,而是在内核中进行了锁定。这样Ring3输出缓冲区在驱动程序完成I/O请求之前,都是无法访问的,从一定程度上保障了安全性。如图21.1.14所示。
这两种方式,对于Ring3的输入缓冲区和METHOD_BUFFERED方式是一致的。对于Ring3的输出缓冲区,首先由系统锁定,并使用pIrp->MdlAddress来描述这段内存,驱动程序需要使用MmGetSystemAddressForMdlSafe函数将这段内存映射到内核内存地址(OutputBuffer),然后可以直接写入OutputBuffer地址,最终在驱动派遣例程返回后,由系统解除这段内存的锁定。

METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式的内存访问
  8METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式的区别,仅在于打开设备的权限上,当以只读权限打开设备时,METHOD_IN_DIRECT方式的IoControl将会成功,而METHOD_OUT_DIRECT方式将会失败。如果以读写权限打开设备,两种方式都会成功。

METHOD_IN_DIRECT和METHOD_OUT_DIRECT方式(借图):
  

(3)如果使用METHOD_NEITHER方式,“其他方式”,虽然通信的效率提高了,但是不够安全。驱动的派遣函数中输入缓冲区可以通过I/O堆栈(IO_STACK_LOCATION)的stack->Parameters.DeviceIo Control.Type3InputBuffer得到。输出缓冲区可以通过pIrp->UserBuffer得到。由于驱动中的派遣函数不能保证传递进来的用户输入和输出地址,因此最好不要直接去读写这些地址的缓冲区。应该在读写前使用ProbeForRead和ProbeForWrite函数探测地址是否可读和可写。

METHOD_ NEITHER方式是不进行缓冲的,在驱动中可以直接使用Ring3的输入输出内存地址,

驱动程序可以通过pIrpStack->Parameters.DeviceIoControl.Type3InputBuffer得到Ring3的输入缓冲区地址(其中pIrpStack是IoGetCurrentIrpStackLocation(pIrp)的返回);通过pIrp-> UserBuffer得到Ring3的输出缓冲区地址。
  由于METHOD_NEITHER方式并不安全,因此最好对Type3InputBuffer读取之前使用ProbeForRead函数进行探测,对UserBuffer写入之前使用ProbeForWrite函数进行探测,当没有发生异常时,再进行读取和写入操作。

METHOD_NEITHER方式(借图):

二 .定义驱动设备名,符号链接名
    定义好了IO控制码CTL_CODE,第二步驱动程序还要准备驱动设备名和符号链接名。

关于在Ring0层中要设置驱动设备名的同时还要设置符号链接名的原因,是因为只有符号链接名才可以被用户模式下的应用程序识别。

windows下的设备是以"\Device[设备名]”形式命名的。例如磁盘分区的c盘,d盘的设备名称就是"\Device\HarddiskVolume1”,"\Device\HarddiskVolume2”, 当然也可以不指定设备名称。     如果IoCreateDevice中没有指定设备名称,那么I/O管理器会自动分配一个数字作为设备的名称。例如"\Device\00000001"。\Device[设备名],不容易记忆,通常符号链接可以理解为设备的别名,更重要的是设备名,只能被内核模式下的其他驱动所识别,而别名可以被用户模式下的应用程序识别,例如c盘,就是名为"c:“的符号链接,其真正的设备对象是”\Device\HarddiskVolume1”,所以在写驱动时候,一般我们创建符号链接,即使驱动中没有用到,这也算是一个好的习惯吧。

驱动中符号链接名是这样写的
    L"\??\HelloDDK" —>??\HelloDDK

或者
    L"\DosDevices\HelloDDK"—>\DosDevices\HelloDDK

在应用程序中,符号链接名:
    L"\\.\HelloDDK"–>\.\HelloDDK

DosDevices的符号链接名就是??, 所以"\DosDevices\XXXX"其实就是\??\XXXX

#define DEVICE_OBJECT_NAME  L"\\Device\\BufferedIODeviceObjectName"
//设备与设备之间通信
#define DEVICE_LINK_NAME    L"\\DosDevices\\BufferedIODevcieLinkName"
//设备与Ring3之间通信

三、将符号链接名与设备对象名称关联 ,等待IO控制码

驱动程序要做的最后一步,先用IoCreateDevice函数创建设备对象,再用IoCreateSymbolicLink将符号链接名与设备对象名称关联 ,大功告成,等待IO控制码。

    //创建设备对象名称
RtlInitUnicodeString(&DeviceObjectName,DEVICE_OBJECT_NAME);
//创建设备对象
Status = IoCreateDevice(DriverObject,NULL,&DeviceObjectName,FILE_DEVICE_UNKNOWN,0, FALSE,&DeviceObject);
if (!NT_SUCCESS(Status))
{return Status;
}//创建设备连接名称
RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
//将设备连接名称与设备名称关联
Status = IoCreateSymbolicLink(&DeviceLinkName,&DeviceObjectName);if (!NT_SUCCESS(Status))
{IoDeleteDevice(DeviceObject);return Status;
}           

四、应用程序获取设备句柄,发送IO控制码。
  驱动程序铺垫打理好之后,应用程序就可以由符号链接名通过CreateFile函数获取到设备句柄DeviceHandle,再用本场的主角,DeviceIoControl通过这个DeviceHandle发送控制码了。
  先看看这两个函数:

BOOL WINAPI DeviceIoControl(_In_         HANDLE hDevice,       //CreateFile函数打开的设备句柄_In_         DWORD dwIoControlCode,//自定义的控制码_In_opt_     LPVOID lpInBuffer,    //输入缓冲区_In_         DWORD nInBufferSize,  //输入缓冲区的大小_Out_opt_    LPVOID lpOutBuffer,   //输出缓冲区_In_         DWORD nOutBufferSize, //输出缓冲区的大小_Out_opt_    LPDWORD lpBytesReturned, //实际返回的字节数,对应驱动程序中pIrp->IoStatus.Information。_Inout_opt_  LPOVERLAPPED lpOverlapped //重叠操作结构指针。同步设为NULL,DeviceIoControl将进行阻塞调用;否则,应在编程时按异步操作设计
);HANDLE CreateFile(LPCTSTR lpFileName,                         //打开的文件名DWORD dwDesiredAccess,                    //访问权限DWORD dwShareMode,                      //共享模式LPSECURITY_ATTRIBUTES lpSecurityAttributes,   //安全属性DWORD dwCreationDisposition,               //文件存在与不存在时的文件创建模式DWORD dwFlagsAndAttributes,                //文件属性设定(隐藏、只读、压缩、指定为系统文件等)HANDLE hTemplateFile                       //文件副本句柄
);

最后总结一下DeviceIoControl的通信流程:

1.驱动程序和应用程序自定义好IO控制码 (CTL_CODE宏 四个参数,32位,4部分,存储设备类型,访问权限,操作功能,缓冲区数据传递方式(四种))

2.驱动程序定义驱动设备名,符号链接名, 将符号链接名与设备对象名称关联 ,等待IO控制码(IoCreateDevice,IoCreateSymbolicLink)

3.应用程序由符号链接名通过CreateFile函数获取到设备句柄DeviceHandle,再用本场的主角,DeviceIoControl通过这个设备句柄发送控制码给派遣函数。

源代码:

  BufferedIO.h
#pragma once
#include <ntifs.h>#define CTL_SYS \CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)#define DEVICE_OBJECT_NAME  L"\\Device\\BufferedIODeviceObjectName"
//设备与设备之间通信
#define DEVICE_LINK_NAME    L"\\DosDevices\\BufferedIODevcieLinkName"
//设备与Ring3之间通信
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp);
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp);

BufferedIO.c

#include "BufferedIO.h"NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{NTSTATUS Status = STATUS_SUCCESS;PDEVICE_OBJECT  DeviceObject = NULL;UNICODE_STRING  DeviceObjectName;UNICODE_STRING  DeviceLinkName;ULONG           i;//   栈//   堆//   全局(global Static Const)DriverObject->DriverUnload = DriverUnload;//创建设备对象名称RtlInitUnicodeString(&DeviceObjectName,DEVICE_OBJECT_NAME);//创建设备对象Status = IoCreateDevice(DriverObject,NULL,&DeviceObjectName,FILE_DEVICE_UNKNOWN,0, FALSE,&DeviceObject);if (!NT_SUCCESS(Status)){return Status;}//创建设备连接名称RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);//将设备连接名称与设备名称关联Status = IoCreateSymbolicLink(&DeviceLinkName,&DeviceObjectName);if (!NT_SUCCESS(Status)){IoDeleteDevice(DeviceObject);return Status;}//设计符合我们代码的派遣历程for (i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++){DriverObject->MajorFunction[i] = PassThroughDispatch;   //函数指针}DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ControlThroughDispatch;return Status;
}
//派遣历程
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT  DeviceObject,PIRP Irp)
{Irp->IoStatus.Status = STATUS_SUCCESS;     //LastError()Irp->IoStatus.Information = 0;             //ReturnLengthIoCompleteRequest(Irp, IO_NO_INCREMENT);   //将Irp返回给Io管理器return STATUS_SUCCESS;
}
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT  DeviceObject, PIRP Irp)
{NTSTATUS Status;ULONG_PTR Informaiton = 0;PVOID InputData = NULL;ULONG InputDataLength = 0;PVOID OutputData = NULL;ULONG OutputDataLength = 0;ULONG IoControlCode = 0;PIO_STACK_LOCATION  IoStackLocation = IoGetCurrentIrpStackLocation(Irp);  //Irp堆栈  IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;InputData  = Irp->AssociatedIrp.SystemBuffer;OutputData = Irp->AssociatedIrp.SystemBuffer;InputDataLength  = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;OutputDataLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;switch (IoControlCode){case CTL_SYS:{if (InputData != NULL&&InputDataLength > 0){DbgPrint("%s\r\n", InputData);}if (OutputData != NULL&&OutputDataLength >= strlen("Ring0->Ring3") + 1){memcpy(OutputData, "Ring0->Ring3", strlen("Ring0->Ring3") + 1);Status = STATUS_SUCCESS;Informaiton = strlen("Ring0->Ring3") + 1;}else{Status = STATUS_INSUFFICIENT_RESOURCES;   //内存不够Informaiton = 0;}break;}default:break;}Irp->IoStatus.Status = Status;             //Ring3 GetLastError();Irp->IoStatus.Information = Informaiton;IoCompleteRequest(Irp, IO_NO_INCREMENT);  //将Irp返回给Io管理器return Status;                            //Ring3 DeviceIoControl()返回值
}
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{UNICODE_STRING  DeviceLinkName;PDEVICE_OBJECT  v1 = NULL;PDEVICE_OBJECT  DeleteDeviceObject = NULL;RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);IoDeleteSymbolicLink(&DeviceLinkName);DeleteDeviceObject = DriverObject->DeviceObject;while (DeleteDeviceObject != NULL){v1 = DeleteDeviceObject->NextDevice;IoDeleteDevice(DeleteDeviceObject);DeleteDeviceObject = v1;}
}   

IO.cpp

// 缓冲区IO.cpp : 定义控制台应用程序的入口点。
//#include "stdafx.h"
#include <windows.h>
#define DEVICE_LINK_NAME    L"\\\\.\\BufferedIODevcieLinkName"#define CTL_SYS \CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)
int main()
{HANDLE DeviceHandle = CreateFile(DEVICE_LINK_NAME,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if (DeviceHandle==INVALID_HANDLE_VALUE){return 0;}char BufferData = NULL;DWORD ReturnLength = 0;BOOL IsOk = DeviceIoControl(DeviceHandle, CTL_SYS,"Ring3->Ring0",strlen("Ring3->Ring0")+1,(LPVOID)BufferData,0,&ReturnLength,NULL);if (IsOk == FALSE){int LastError = GetLastError();if (LastError == ERROR_NO_SYSTEM_RESOURCES){char BufferData[MAX_PATH] = { 0 };IsOk = DeviceIoControl(DeviceHandle, CTL_SYS,"Ring3->Ring0",strlen("Ring3->Ring0") + 1,(LPVOID)BufferData,MAX_PATH,&ReturnLength,NULL);if (IsOk == TRUE){printf("%s\r\n", BufferData);}}}if (DeviceHandle != NULL){CloseHandle(DeviceHandle);DeviceHandle = NULL;}printf("Input AnyKey To Exit\r\n");getchar();return 0;
}

应用程序与驱动程序交互函数DeviceIoControl详解相关推荐

  1. ioctl 函数 参数 详解

    2019独角兽企业重金招聘Python工程师标准>>> ioctl 函数 参数 详解 2009-04-24 11:55 ioctl函数 本函数影响由fd参数引用的一个打开的文件. # ...

  2. 系统关机函数ExitWindowsEx详解

    系统关机函数ExitWindowsEx详解 "系统ShutDown"属于Windows系统的一种基本服务.功能上有"关闭系统","注销用户" ...

  3. lisp不是函授型语言_lisp函数大全详解

    lisp函数大全详解 AutoLISP提供了大量的预定义函数.若将函数名(大小写都可)作为表中的第一个元素函数变元(若有的话)作为表中后面的元素,就可以调用那个函数.本章按字母顺序列出AutoLISP ...

  4. C语言结构体中定义函数指针详解

    C语言结构体中定义函数指针详解 结构体指针函数应用场景之一--驱动程序编写 结构体的一些基本用法 形式1:先定义结构体类型,再定义变量 形式2:在定义类型的同时定义变量 形式3:直接定义变量,用无名结 ...

  5. LayoutInflater的inflate函数用法详解

    LayoutInflater的inflate函数用法详解 LayoutInflater作用是将layout的xml布局文件实例化为View类对象. 获取LayoutInflater的方法有如下三种: ...

  6. 微信小程序开发登录界面mysql_微信小程序 欢迎界面开发的实例详解

    微信小程序 欢迎界面 市面上大多数的app都会有一个欢迎界面,下面将演示如何通过微信小程序实现一个欢迎界面. 下面将会按照以下的顺序介绍: 布局的实现 逻辑的实现 样式的实现 1.布局的实现 整个布局 ...

  7. c++ memset 语言_C++中memset函数用法详解

    本文实例讲述了C++中memset函数用法.分享给大家供大家参考,具体如下: 功 能: 将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,块的大小由第三个参数指定,这个函数通常 ...

  8. window 程序报错 自动重启_好程序员web前端教程之详解JavaScript严格模式

    好程序员web前端教程之详解JavaScript严格模式,严格模式(Strict mode)是由ECMA-262规范定义的新兴JavaScript标准,发布于2009年12月第五版.旨在改善错误检查功 ...

  9. 微信小程序阻止冒泡点击_微信小程序bindtap事件与冒泡阻止详解

    bindtap就是点击事件 在.wxml文件绑定: cilck here 在一个组件的属性上添加bindtap并赋予一个值(一个函数名) 当点击该组件时, 会触发相应的函数执行 在后台.js文件中定义 ...

最新文章

  1. 类.接口.多态.向上转型.向下转型
  2. PCA原理分析和意义(二)
  3. Win7旗舰版系统hosts文件位置在哪里
  4. 机器学习笔记(十九)——最大熵原理和模型定义
  5. 小波变换和motion信号处理(二)【转载】
  6. python列表中item_del(item)与列表.删除(item)在python lis中
  7. 如何让IOS应用从容地崩溃
  8. linux 文件隐藏权限,linux文件基本权限、默认权限、隐藏权限和ACL权限
  9. Centos7下基于Pseudo-Distributed的Hadoop环境搭建
  10. eclipse debug 的断点查看和清除
  11. 在我的ibmR40上装osx86
  12. 6种方法轻松将PDF转换为Word文档,办公必备!
  13. Excel正确输入身份证号码
  14. CAD能打开PDF格式吗?这样做可以快速实现
  15. JavaScript 数组对象切片
  16. ZYNQ7000 学习(三十) 定时器终端的原理和实现
  17. LeetCode解题源代码链接集锦一
  18. Python云端开发基础
  19. 我认识的几个哈工大本科生毕业后出路
  20. 为什么这么多人喜欢做饭而不喜欢洗碗?

热门文章

  1. 《那些年啊,那些事——一个程序员的奋斗史》——97
  2. 如何清空历史的告警记录——WGCLOUD
  3. 国家统计局公布,平均工资最高的行业是......
  4. cpu和gpu(cpu和gpu温度一般在多少)
  5. 【观察】后疫情时代,数据中心效率与成本的“平衡术”
  6. TH2830新型LCR数字电桥报价/技术资料-安泰测试Agitek
  7. [jvm]频繁full gc怎么优化
  8. WebStorm 5.0 注册码
  9. 9.域名系统(DNS)
  10. 以太坊源码系列之miner解析(2)