当前位置: 代码迷 >> 驱动开发 >> Window XP驱动开发(十六) 驱动程序调用驱动程序(经过设备指针)
  详细解决方案

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

热度:268   发布时间:2016-04-28 10:52:05.0
Window XP驱动开发(十六) 驱动程序调用驱动程序(通过设备指针)

转载请标明是引用于 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内核函数可以通过设备名获得文件对象指针,而不是获得设备句柄,其声明如下:

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

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

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

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

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

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

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

使其引用计数减1。


当第一次调用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

函数声明如下:

第一个参数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工程中):


测试方法:

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

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

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

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

 

1、4  用IoBuildAsynchronousFsdRequest创建IRP

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

 

1、5 用IoAllocate创建IRP

  相关解决方案