CreateFile DeviceIoControl dwIoControlCode——应用程序与驱动程序通信

本文主要是介绍CreateFile DeviceIoControl dwIoControlCode——应用程序与驱动程序通信,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转自:CreateFile DeviceIoControl dwIoControlCode——应用程序与驱动程序通信

  在“进程内存管理器中”的一个Ring0,Ring3层通信问题,之前也见过这样的代码,这次拆分出来详细总结一下。

  先通过CreateFile函数得到设备句柄,CreateFile函数原型:

  

复制代码
HANDLE CreateFile(LPCTSTR lpFileName,                         // 文件名/设备路径 设备的名称DWORD dwDesiredAccess,                      // 访问方式DWORD dwShareMode,                          // 共享方式LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全描述符指针DWORD dwCreationDisposition,                // 创建方式DWORD dwFlagsAndAttributes,                 // 文件属性及标志HANDLE hTemplateFile                        // 模板文件的句柄
);
复制代码

打开:createFile

关闭:closehandle

与普通文件名有所不同,设备驱动的“文件名”(常称为“设备路径”)形式固定为“\\.\DeviceName”(注意写法为“\\\\.\\DeviceName”),DeviceName必须与设备驱动程序内定义的设备名称一致。

在“进程内存管理器”中:

Ring0层的kProcessMemory.h

#define DEVICE_NAME L"\\Device\\KProcessMemoryDeviceName"
#define LINK_NAME L"\\DosDevices\\KProcessMemoryLinkName"

 

Ring3层的ProcessMemoryManager.cpp

复制代码
OpenDeviceObject(L"\\\\.\\KProcessMemoryLinkName");BOOL OpenDeviceObject(LPCTSTR DeviceFullPathData)
{
m_DeviceHandle = CreateFile(DeviceFullPathData,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);if (m_DeviceHandle == INVALID_HANDLE_VALUE)
{
return FALSE;
}return TRUE;}
复制代码

可以看到在kProcessMemory.h中define了一个驱动设备名和一个符号链接名

驱动设备名是调用IoCreateDevice时使用的,IoCreateDevice函数原型:

复制代码
NTSTATUS IoCreateDevice(_In_      PDRIVER_OBJECT DriverObject,_In_      ULONG DeviceExtensionSize,_In_opt_  PUNICODE_STRING DeviceName,_In_      DEVICE_TYPE DeviceType,_In_      ULONG DeviceCharacteristics,_In_      BOOLEAN Exclusive,_Out_     PDEVICE_OBJECT *DeviceObject
);
复制代码

驱动程序中调用IoCreateDevice函数:

RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObject);

关于在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

winobj和DeviceTree可以用来查看这些信息。

关于驱动设备名和符号链接名,可以参考这篇博客:

http://www.cnblogs.com/findumars/p/5636505.html

 

接着回到CreateFile函数上来,它的第二个参数,dwDesireAceess访问方式,一般设置为0或GENERIC_READ|GENERIC_WRITE共享方式参数设置为FILE_SHARE_READ|FILE_SHARE_WRITE创建方式参数设置为OPEN_EXISTING,其它参数一般设置为0或NULL。

 

Ring3层的CreateFile函数获取了设备句柄后,将使用DeviceIoControl函数向指定的设备驱动发送一个IO控制码,驱动程序通过这个控制码来完成特定的工作。该函数原型如下:

复制代码
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将进行阻塞调用;否则,应在编程时按异步操作设计
);
复制代码

  先介绍IO控制码,驱动程序可以通过CTL_CODE宏来组合定义一个控制码,并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在驱动层,IoStackLocation->Parameters.DeviceIoControl.IoControlCode表示了这个控制码。发送不同的控制码,可以调用设备驱动程序的不同类型的功能。在头文件winioctl.h中,预定义的标准设备控制码,都以IOCTL或FSCTL开头。例如,IOCTL_DISK_GET_DRIVE_GEOMETRY是对物理驱动器取结构参数(介质类型、柱面数、每柱面磁道数、每磁道扇区数等)的控制码,FSCTL_LOCK_VOLUME是对逻辑驱动器的卷加锁的控制码。

 

lpInBuffer

由用户层发送的缓冲区数据。在“进程内存管理器“程序中,我们是通过进程ID来查询进程内存,故传入的是进程ID.在驱动层,依传输类型的不同,输入缓冲区的位置亦不同,见下表。

传输类型位置
METHOD_IN_DIRECTirp->AssociatedIrp.SystemBuffer
METHOD_OUT_DIRECTirp->AssociatedIrp.SystemBuffer
METHOD_BUFFEREDirp->AssociatedIrp.SystemBuffer
METHOD_NEITHERirpStack->Parameters.DeviceIoControl.Type3InputBuffer

nInBufferSize

由用户层发送的缓冲区大小。在驱动层,这个值是IoStackLocation->Parameters.DeviceIoControl.InputBufferLength

 

lpOutBuffer

由用户层指定,用于接收驱动层返回数据的缓冲区。在驱动层,依传输类型的不同,输出缓冲区的位置亦不同,见下表。

传输类型位置
METHOD_IN_DIRECTirp->MdlAddress
METHOD_OUT_DIRECTirp->MdlAddress
METHOD_BUFFEREDirp->AssociatedIrp.SystemBuffer
METHOD_NEITHERirp->UserBuffer

nOutBufferSize

由用户层指定,用于接收驱动层返回数据的缓冲区大小。在驱动层,这个值是IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength

 

lpBytesReturned

由用户层指定,用于接收驱动层实际返回数据大小。在驱动层,这个值是irp->IoStatus->Information

 

lpOverlapped

用于异步操作。

 

程序中的派遣函数:

复制代码

#define CTL_QUERY_PROCESS_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_NEITHER,FILE_ANY_ACCESS)

#define CTL_READ_PROCESS_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x831,METHOD_NEITHER,FILE_ANY_ACCESS)

#define CTL_WRITE_PROCESS_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x832,METHOD_NEITHER,FILE_ANY_ACCESS)

......

 

RtlInitUnicodeString(&DeviceName, DEVICE_NAME);
Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObject);

......

RtlInitUnicodeString(&LinkName, LINK_NAME);
Status = IoCreateSymbolicLink(&LinkName, &DeviceName);

......

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControlDispatch;

NTSTATUS DeviceControlDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{NTSTATUS Status = STATUS_SUCCESS;ULONG    IoControlCode = 0;ULONG    InputLength = 0;SIZE_T   OutputLength = 0;PVOID    InputData = NULL;PVOID    OutputData = NULL;PIO_STACK_LOCATION IoStackLocation = IoGetCurrentIrpStackLocation(Irp);IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;//BufferIO//InputData = OutputData = Irp->AssociatedIrp.SystemBuffer;  /*直接方式DO_DIRECT_IO / 非直接方式(缓冲方式)DO_BUFFERD_IO1) 在buffered(AssociatedIrp.SystemBuffer)方式中,I/O管理器先创建一个与用户模式数据缓冲区大小相等的系统缓冲区。而你的驱动程序将使用这个系统缓冲区工作。I/O管理器负责在系统缓冲区和用户模式缓冲区之间复制数据。2) 在direct(MdlAddress)方式中,I/O管理器锁定了包含用户模式缓冲区的物理内存页,并创建一个称为MDL(内存描述符表)的辅助数据结构来描述锁定页。因此你的驱动程序将使用MDL工作。3) 在neither(UserBuffer)方式中,I/O管理器仅简单地把用户模式的虚拟地址传递给你。而使用用户模式地址的驱动程序应十分小心。*///Neither方式提高了通信效率,但是不够安全,在读写之前应使用ProbeForRead和ProbeForWrite函数探测地址是可读和可写//详见eDiary笔记中“Driver——DeviceIoControl函数与IoControlCode”InputData = IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer;//得到Ring3的输入缓冲区地址OutputData = Irp->UserBuffer;                                            //得到Ring3的输出缓冲区地址InputLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;OutputLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;switch (IoControlCode)   //IO控制码
    {case CTL_QUERY_PROCESS_MEMORY:{if (!MmIsAddressValid(OutputData)){Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;Irp->IoStatus.Information = 0;break;}if (InputLength != sizeof(ULONG) || InputData == NULL){Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;Irp->IoStatus.Information = 0;break;}__try{ProbeForWrite(OutputData, OutputLength, 1);  //检测内存是否可写,这个函数需要在用户模式下使用,详见MSDN://If Irp->RequestorMode = KernelMode, the Irp->AssociatedIrp.SystemBuffer and Irp->UserBuffer fields do not contain user-mode addresses,//and a call to ProbeForWrite to probe a buffer pointed to by either field will raise an exception.
Status = RtlQueryVirtualMemory(*(PULONG)InputData, OutputData, OutputLength);Irp->IoStatus.Information = 0;Irp->IoStatus.Status = Status;}__except (EXCEPTION_EXECUTE_HANDLER){Irp->IoStatus.Information = 0;Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;}break;}case CTL_READ_PROCESS_MEMORY:{if (!MmIsAddressValid(OutputData) || OutputLength>MAX_LENGTH){Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;Irp->IoStatus.Information = 0;break;}if (InputLength != sizeof(READ_OPERATION) || InputData == NULL){Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;Irp->IoStatus.Information = 0;break;}__try{ProbeForWrite(OutputData, OutputLength, 1);Status = RtlReadVirtualMemory(InputData, OutputData, OutputLength);Irp->IoStatus.Information = 0;Irp->IoStatus.Status = Status;}__except (EXCEPTION_EXECUTE_HANDLER){Irp->IoStatus.Information = 0;Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;}break;}case CTL_WRITE_PROCESS_MEMORY:{if (InputLength < sizeof(WRITE_OPERATION) || InputData == NULL){Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;Irp->IoStatus.Information = 0;break;}__try{Status = RtlWriteVirtualMemory(InputData);Irp->IoStatus.Information = 0;Irp->IoStatus.Status = Status;}__except (EXCEPTION_EXECUTE_HANDLER){Irp->IoStatus.Information = 0;Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;}break;}default:{Irp->IoStatus.Information = 0;Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;break;}}IoCompleteRequest(Irp, IO_NO_INCREMENT);       //将Irp返回给IO管理器return Status;
}
复制代码

这篇关于CreateFile DeviceIoControl dwIoControlCode——应用程序与驱动程序通信的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/645001

相关文章

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

vue2 组件通信

props + emits props:用于接收父组件传递给子组件的数据。可以定义期望从父组件接收的数据结构和类型。‘子组件不可更改该数据’emits:用于定义组件可以向父组件发出的事件。这允许父组件监听子组件的事件并作出响应。(比如数据更新) props检查属性 属性名类型描述默认值typeFunction指定 prop 应该是什么类型,如 String, Number, Boolean,

linux中使用rust语言在不同进程之间通信

第一种:使用mmap映射相同文件 fn main() {let pid = std::process::id();println!(

C++编程:ZeroMQ进程间(订阅-发布)通信配置优化

文章目录 0. 概述1. 发布者同步发送(pub)与订阅者异步接收(sub)示例代码可能的副作用: 2. 适度增加缓存和队列示例代码副作用: 3. 动态的IPC通道管理示例代码副作用: 4. 接收消息的超时设置示例代码副作用: 5. 增加I/O线程数量示例代码副作用: 6. 异步消息发送(使用`dontwait`标志)示例代码副作用: 7. 其他可以考虑的优化项7.1 立即发送(ZMQ_IM

Java Web应用程序的推荐目录结构

以前没有用过maven管理过项目的依赖,最后使用上了maven,发现通过不能方式建立出来的web应用程序目录结构基本都不一样,既然每次都要到网上搜索如何建立maven管理的Web应用程序,不如自己找百度谷歌一下。 找了半天 ,感觉比较好的maven管理的web应用程序目录结构是这个: ├── pom.xml└── src├── main│ ├── java│ │ └── myg

VB和51单片机串口通信讲解(只针对VB部分)

标记:该篇文章全部搬自如下网址:http://www.crystalradio.cn/thread-321839-1-1.html,谢谢啦            里面关于中文接收的部分,大家可以好好学习下,题主也在研究中................... Commport;设置或返回串口号。 SettingS:以字符串的形式设置或返回串口通信参数。 Portopen:设置或返回串口

深入理解TCP通信

这大概是自己博客上面第三次写TCP通信demo了,总是写同样的内容也不太好啊,不过每一次都比前一次进步一点。这次主要使用了VIM编辑工具、gdb调试、wireshirk、netstat查看网络状态。 参考《C++服务器视频教程》、《Unix网络编程》 一、VIM常用命令 vim server.cpp #打开一个文件:w 写入文件:wq 保存并退出:q! 不保存退出显示行号

电子电气架构---私有总线通信和诊断规则

电子电气架构—私有总线通信和诊断规则 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节能减排。 无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事.而不是让内心的烦躁、