这种通信方式,就是驱动程序和应用程序自定义一种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. 用python绘制柱状图标题-使用Python绘制图表大全总结
  2. 运营类产品:用户行为的影响因子是什么?
  3. Oracle创建和管理表
  4. MySQL TEXT数据类型的最大长度
  5. How to make a Logical Volume ON AIX5.3
  6. 线程----Monitor(互斥锁)类设置超时值
  7. 1.3编程基础之算术表达式与顺序执行 12 计算球的体积
  8. ggplot2 | 注释函数
  9. 《统计学习方法》——k近邻法
  10. dspemif怎么读_DSP技术在EMIF接口中的BOOT方法简析
  11. 离线缓存与客户端存储总结
  12. python for ArcGIS 绘制成都市板块地图
  13. 移植MotionDriver到RTT
  14. 西门子S7200plc通信不上实际问题和解决方法
  15. 笔记本外接显示器闪屏
  16. matlab排版形式是什么样子,版式设计技巧!论图文排版的基本形式
  17. 华为荣耀V20存瑕疵,证明三星在技术上还是要强一些
  18. 亚马逊echo中国使用_我需要Amazon Echo才能使用Alexa吗?
  19. 树莓派换源 bullseye
  20. FileRun多功能网盘的搭建

热门文章

  1. 【数字音频】WAV和PCM的关系和区别
  2. 带哨兵节点的链_限流降级神器-哨兵(sentinel)的资源调用链原理分析
  3. 常见的评价指标及其计算方法
  4. 数学建模:14 时间序列
  5. php考研大学,2019考研:49所院校公布研究生招生简章及专业目录
  6. 云智教家校互动用户服务协议
  7. 2022年熔化焊接与热切割考试练习题及模拟考试
  8. 关于C++中有符号整型和无符号整型的转换方法
  9. Linux系统中安装和管理程序 太详细了
  10. ionic2微信支付,分享,登录