Window XP驱动开发(十六) 驱动程序调用驱动程序(通过设备指针)

2023-11-11

转载请标明是引用于 http://blog.csdn.net/chenyujing1234 

欢迎大家提出意见,一起讨论!

代码及EzDriverInstaller下载地址 : http://www.rayfile.com/zh-cn/files/9840cf8f-c41f-11e1-b25b-0015c55db73d/

(编译环境:VS2008+DDK库(参考:Window XP驱动开发(十六) XP下新建驱动程序工程并编译的第二种方法))

 

我有一篇文章是介绍以文件句柄形式调用其它驱动程序的方法:

Window XP驱动开发(十五) 驱动程序调用驱动程序(以文件句柄形式)

现在介绍以设备指针调用其它驱动程序的方法。

 

 

1、通过设备指针调用其他驱动程序

前面介绍了如何使用ZwCreateFile内核函数打开设备,还介绍了如何用ZwReadFile内核函数读取设备。

这些操作和应用程序中的CreateFile和ReadFile 函数的使用很类似。其实,CreateFile和ReadFile这两个API函数分别调用了ZwCreateFile和ZwReadFile内核函数

ZwReadFile内核函数内部会创建IRP_MJ_READ类型的IRP, 然后通过这个IRP传送到相应驱动的派遣函数中。

本节介绍的驱动程序调用其他驱动程序的方法,不是借用ZwCreateFile和ZwReadFile等内核函数,而是“手动”构造各个IRP,

然后将IRP传递到相应的驱动程序的派遣函数里。

1、1  用IoGetDeviceObjectPointer获得设备指针

每个内核中的句柄都会和一个内核对象的指针联系起来。例如,进程对象的句柄和进程对象的指针关联,

线程对象的句柄和线程对象的指针关联,内核事件句柄和内核对象关联。

ZwCreateFile内核函数可以通过设备名打开设备句柄,这个设备句柄和一个文件对象的指针关联。

IoGetDeviceObjectPointer内核函数可以通过设备名获得文件对象指针,而不是获得设备句柄,其声明如下:

IoGetDeviceObjectPointer(
    __in  PUNICODE_STRING ObjectName,
    __in  ACCESS_MASK DesiredAccess,
    __out PFILE_OBJECT *FileObject,
    __out PDEVICE_OBJECT *DeviceObject
    );

第一个参数ObjectName:设备名,用UNICODE字符串表示;

第二个参数DesiredAccess :以什么样的权限得到设备句柄;

第三个参数FileObject:同时会返回一个和设备相关的文件对象指针;

第四个参数DeviceObejct:返回的设备对象指针

Windows内核会为每一个对象指针保存一个“引用计数”,当对象被创建时引用计数为1。如果想引用这个对象,计数会加1。

如果删除对象时,Windows先将引用计数减1,如果引用计数不是0,系统不会删除对象。

当调用IoGetDeviceObjectPointer内核函数后,设备对象的引用计数就会加1,当用完这个设备对象后,应用调用ObDereferenceObject内核函数,

使其引用计数减1。

#define ObDereferenceObject(a)                                     \
        ObfDereferenceObject(a)


当第一次调用IoGetDeviceObjectPointer内核函数时,会根据设备名打开设备,这时文件对象指针计数为1。此后如果再次调用

IoGetDeviceObjectPointer打开设备,就不是真正地打开设备了,而是只将引用计数加1。打开设备时,系统会创建一个

IRP_MJ_CREATE类型的IRP,并将这个IRP传递到驱动程序的派遣函数中。

每次调用ObDereferenceObject内核函数都会将“引用计数”减1,如果减至0就会关闭设备。关闭设备时,系统会创建一个IRP_MJ_CLOSE类型的IRP,

将将其传递到相应驱动的派遣函数中。

从上述内容可以看出IoGetDeviceObjectPointer和ObDereferenceObject内核函数完全正确可以代替ZwCreateFile和ZwCloseFile内核函数。另外,这种方法还能获

得设备对象指针关联的文件对象指针。

1、2 创建IRP传递给驱动的派遣函数

本节介绍如何手动创建IRP,并将 其传递给相应的程序程序。这样的好处是比ZwReadFile内核灵活。ZwReadFile内核函数是针对设备句柄操作的,而传递IRP是通过设备对象的指针操作。

(1)可以通过IoBuildSynchronousFsdRequest和IoBuildAsynchronousFsdRequest两个内核函数创建IRP,它们分别用来创建同步类型的IRP和异步类型的IRP。

这两个内核函数可以创建IRP_MJ_PNP、IRP_MJ_READ、IRP_MJ_WRITE、MJ_FLUSH_BUFFERS和IIRP_MJ_SHUTDOWN类型的IRP。

可以通过IoBuildDeviceIoControlRequest内核函数创建IRP_MJ_INTERNAL_DEVICE_CONTROL和IRP_MJ_DEVICE_CONTROL两个类型的IRP,

这两个内核函数只能创建同步类型的IRP。

另外,还可以使用IoAllocateIrp内核函数,它可以创建任意类型的IRP。IoBuildSynchronousFsdRequest、IoBuildAsynchronousFsdRequest、IoBuildDeviceIoControlRequest这三个内核函数都是属于靠近上层的内核函数。

而IoAllocateIrp是比较底层的内核函数,以下三个内核都是通过IoAllocateIrp实现的。

(2)创建完IRP后,还要构造IRP的I/O堆栈,每层I/O堆栈对应一个设备对象。由于示例程序DriverA是单层驱动程序,所以只需要构造IRP的第一层I/O堆栈。

(3)最后是通过IoCallDriver内核函数调用相应的驱动。IoCallDriver 内核函数会根据IRP的类型,找到相应的派遣函数。

总结一下,手动创建IRP有以下几个步骤:

(1)先得到设备的指针。一种方法是用IoGetDeviceObjectPointer内核函数得到设备对象的指针;

                                             另一种方法是通过ZwCreateFile内核函数先得到设备句柄,然后调用ObReferenceObjectByPointer内核函数通过设备句柄得到设备对象指针。

(2)手动创建IRP,有4个内核函数可以选择,它们是IoBuildSynchronousFsdRequest、IoBuildAsynchronousFsdRequest、IoBuildDeviceIoControlRequest和

         IoAllocateIrq,其中IoAllocateIrp内核函数是最灵活的,使用也最复杂。

(3)构造IRP的I/O堆栈。

(4)调用IoCallDriver内核函数,其内部会调用设备对象的派遣函数。

 

1、3  用IoBuildSynchronousFsdRequest创建IRP

函数声明如下:

PIRP
IoBuildSynchronousFsdRequest(
    __in  ULONG MajorFunction,
    __in  PDEVICE_OBJECT DeviceObject,
    __inout_opt PVOID Buffer,
    __in_opt ULONG Length,
    __in_opt PLARGE_INTEGER StartingOffset,
    __in  PKEVENT Event,
    __out PIO_STATUS_BLOCK IoStatusBlock
    );

第一个参数MajorFunction:这个参数是创建的IRP的主类型,IoBuldSynchronousFsdRequest函数只支持IRP_MJ_PNP、IRP_MJ_READ、IRP_MJ_WRITE、MJ_FLUSH_BUFFERS和IIRP_MJ_SHUTDOWN。

第二个参数DeviceObject:这个参数是设备对象指针,IRP将会传递给这个设备对象。

第三个参数Buffer:对于IRP_MJ_READ和IRP_MJ_WRITE,Buffer指的是输入和输出缓冲区

第四个参数Length:这个参数是缓冲区的大小

第五个参数StartingOffset:这个参数是偏移量;

第六个参数Event:这个参数是同步事件,这个创建同步类型的IRP的关键,后面会有介绍。

使用IoBuildSynchronousFsdRequest内核函数创建同步类型的IRP,关键在于第六个参数Event 。

在调用IoBuildSynchronousFsdRequest之前,需要准备一个事件,这个事件会和IRP请求关联,当IRP请求被结束时该事件被触发。

IoBuildSynchronousFsdRequest和IoBuildAsynchronousFsdRequest内核函数之间的区别就是是否提供事件。

下面的代码演示了如何使用IoBuildSynchronousFsdRequest内核函数创建同步类型IRP(在代码中的DriverB工程中):

NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
								 IN PIRP pIrp) 
{
	KdPrint(("DriverB:Enter B HelloDDKRead\n"));
	NTSTATUS ntStatus = STATUS_SUCCESS;

	UNICODE_STRING DeviceName;
	RtlInitUnicodeString( &DeviceName, L"\\Device\\MyDDKDeviceA" );

	PDEVICE_OBJECT DeviceObject = NULL;
	PFILE_OBJECT FileObject = NULL;
	//得到设备对象句柄,计数器加1
	//如果是第一次调用IoGetDeviceObjectPointer,会打开设备,相当于调用ZwCreateFile
	ntStatus = IoGetDeviceObjectPointer(&DeviceName,FILE_ALL_ACCESS,&FileObject,&DeviceObject);

	KdPrint(("DriverB:FileObject:%x\n",FileObject));
	KdPrint(("DriverB:DeviceObject:%x\n",DeviceObject));

	// 判断是否成功打开设备
	if (!NT_SUCCESS(ntStatus))
	{
		KdPrint(("DriverB:IoGetDeviceObjectPointer() 0x%x\n", ntStatus ));

		ntStatus = STATUS_UNSUCCESSFUL;
		// 设置IRP的完成状态
		pIrp->IoStatus.Status = ntStatus;
		// 设置IRP操作的字节数
		pIrp->IoStatus.Information = 0;	// bytes xfered
		// 将IRP请求结束
		IoCompleteRequest( pIrp, IO_NO_INCREMENT );
		KdPrint(("DriverB:Leave B HelloDDKRead\n"));

		return ntStatus;
	}

	KEVENT event;
	// 初始化一个同步对象
	KeInitializeEvent(&event,NotificationEvent,FALSE);
	IO_STATUS_BLOCK status_block;
	// 将32位整数转为64位的整数
	LARGE_INTEGER offsert = RtlConvertLongToLargeInteger(0);

	//创建同步IRP
	PIRP pNewIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
												DeviceObject,
												NULL,0,
												&offsert,&event,&status_block);
 	KdPrint(("DriverB:pNewIrp:%x\n",pNewIrp));

	// 得到下一层的I/O堆栈
	PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(pNewIrp);
	// 设置I/O堆栈的文件对象指针
	stack->FileObject = FileObject;

	//调用DriverA,会一直调用到DriverA的派遣函数
	NTSTATUS status = IoCallDriver(DeviceObject,pNewIrp);
	// 判断操作是否被挂起
    if (status == STATUS_PENDING) 
	{

		//如果DriverA的派遣函数没有完成IRP,则等待IRP完成
       status = KeWaitForSingleObject(
                            &event,
                            Executive,
                            KernelMode,
                            FALSE, // Not alertable
                            NULL);
        status = status_block.Status;
    }

	//将引用计数减1,如果此时计数器减为0,
	//则将关闭设备,相当于调用ZwClose
 	ObDereferenceObject( FileObject );


	ntStatus = STATUS_SUCCESS;
	// 设置IRP的完成状态
	pIrp->IoStatus.Status = ntStatus;
	// 设置IRP的操作字节数
	pIrp->IoStatus.Information = 0;	// bytes xfered
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );
	KdPrint(("DriverB:Leave B HelloDDKRead\n"));
	return ntStatus;
}


测试方法:

(1) 标准驱动DriverA的设计与文章(Window XP驱动开发(十五) 驱动程序调用驱动程序(以文件句柄形式))一样,请参考代码。

(2) DriverB 的设计请参考代码。

(3) 安装HelloDDKA.sys、HelloDDKB.sys(安装方法与Window XP驱动开发(十五) 驱动程序调用驱动程序(以文件句柄形式)一样)

 通过DebugView看到的打印信息如下:

 

1、4  用IoBuildAsynchronousFsdRequest创建IRP

这个内核函数比IoBuildSynchronousFsdRequest内核函数少一个事件参数。

 

1、5 用IoAllocate创建IRP

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Window XP驱动开发(十六) 驱动程序调用驱动程序(通过设备指针) 的相关文章

随机推荐

  • 十分钟利用windows7漏洞破解开机密码

    所有win7系统都使用 首先连按五下Shift键弹出粘滞键提醒 然后我们点击否后关机 启动系统时将其强制关机 虚拟机利用电源关闭虚拟机 自用主机就在开机时长按关机键强制关闭系统 随后启动系统 我们选择启动启动修复 推荐 选择取消即不还原 等
  • Python数据可视化——折线图

    Python数据可视化 折线图 随着数据分析和数据科学的飞速发展 数据可视化成为了越来越重要的一环 而Python作为一门强大的编程语言 其在数据可视化领域也有着不俗的表现 本文将为大家介绍如何使用Python的Matplotlib库创建一
  • 【Transformers】第 6 章:用于标记分类的微调语言模型

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • Vue.config.js常用配置详解

    摘要 本文将介绍Vue config js中常用的配置选项 包括publicPath outputDir devServer chainWebpack等 并提供相应的代码示例 帮助读者更好地理解和配置Vue项目 1 publicPath p
  • 新汽车电子技术图谱

    商业模式 改变传统对于OEM来讲的 卖车即结束 的模式 会员模式 共享模式 租赁模式 运营模式等各种新型的数字出行体验模式 OTA云 远程刷新 远程诊断 远程车控 远程数据上传 第三方App 应用商店 边缘计算 多级云计算 大数据处理 AI
  • Android4.4深入浅出之SurfaceFlinger与Client通信框架(一)

    SurfaceFlinger框架是基于Binder进程间通信机制搭建的 SF作为一个服务进程 用户程序想要跟它通信必然要经过Binder机制 首先说一下 用户要跟SF通信 那么SF必须出现在ServiceManager中 因为SF也是一个服
  • ROS STAGE教程1

    默认路径opt ros kinetic share 下有stage 和 stage ros 到该路径下可运行 rosrun stage ros stageros rospack find stage ros world willow err
  • STM32+HC-05蓝牙模块学习与使用

    HC 05蓝牙串口通信 HC05模块是一款高性能主从一体蓝牙串口模块 是一种集成蓝牙功能的PCBA板 用于短距离无线通信 十分方便 从某宝商家那里可以看到 蓝牙可以使用多种方法使用 这里我使用的是蓝牙主机连接 所以我们这里需要准备的器件 两
  • 【python学习】函数式编程和高阶函数 map filter reduce lambda表达式 sorted 闭包 装饰器

    函数式编程就是一种抽象程度很高的编程范式 纯粹的函数式编程语言编写的函数没有变量 因此 任意一个函数 只要输入是确定的 输出就是确定的 这种纯函数我们称之为没有副作用 而允许使用变量的程序设计语言 由于函数内部的变量状态不确定 同样的输入
  • cudaMemcpy() 犯错误

    cudaMemcpy void dst const void src size t count enum cudaMemcpyKind kind 错误 count 是 bytes 个数 不是数据类型个数 让我debug好久的一个错误啊 转载
  • YUV图像数据分析

    做视频采集与处理 自然少不了要学会分析YUV数据 因为从采集的角度来说 一般的视频采集芯片输出的码流一般都是YUV数据流的形式 而从视频处理 例如H 264 MPEG视频编解码 的角度来说 也是在原始YUV码流进行编码和解析 所以 了解如何
  • rust异步编程2

    概述 异步编程参考书籍 async book 此学习根据Rust语言圣经 中tokio专栏 tokio 是一个将 rust提供的async await 特性编写的异步代码运行起来的异步运行时 tokio async std smol等异步运
  • uniapp css

    ifdef APP PLUS height calc var status bar height 80upx endif 计算 状态栏 其他高度
  • 信息学奥赛一本通:2073:【例2.16 】三角形面积

    题目描述 传说古代的叙拉古国王海伦二世发现的公式 利用三角形的三条边长来求取三角形面积 已知 ABC中的三边长分别为a b c 求 ABC的面积 提示 海伦公式 s p p a p b p c 其中p a b c 2 输入 三角形的三条边长
  • 不用sqrt()函数,求平方根的三种方法

    最近看到了这个比较有意思的题目 探究了一下 文章目录 1 二分法 2 牛顿法 3 来自于Quake III源码的解法 4 完整代码 参考 当然有最暴力的方法 直接遍历 0 0 x 区间内所有的数据 也可以是 x 2 看值是否相等 但该方法太
  • 记录--纯CSS实现一个简单又不失优雅的步骤条

    这里给大家分享我在网上总结出来的一些知识 希望对大家有所帮助 步骤条是一种用于引导用户按照特定流程完成任务的导航条 在各种分步表单交互场景中广泛应用 先来看一下几个主流前端 UI 框架中步骤条组件的样子 ElementPlus AntDes
  • 输入月份号,输出该月的英文月名。用指针数组处理

    输入月份号 输出该月的英文月名 用指针数组处理 include
  • 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中...

    题目描述 给定一个整数数组nums和一个整数目标值target 请你在该数组中找出和为目标值target的那两个整数 并返回它们的数组下标 你可以假设每种输入只会对应一个答案 但是 数组中同一个元素在答案里不能重复出现 你可以按任意顺序返回
  • 一口Linux公众号粉丝过万总结

    0 楔子 终于万粉了 总算熬过了冷启动阶段 一万这个小目标看着很简单 但是实际做的时候 发现远没有自己想的那么容易 亿万粉丝 其实并不是很多 一度犹豫要不要写这个万份总结 和嵌入式领域内的一些大佬相比 这个粉丝量实在微不足道 也远没有到达我
  • Window XP驱动开发(十六) 驱动程序调用驱动程序(通过设备指针)

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家提出意见 一起讨论 代码及EzDriverInstaller下载地址 http www rayfile com zh cn files 9840