⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 解读nt与xp的分层驱动模型.txt

📁 驱动开发过程中要注意的一些要点以及一些基本资料
💻 TXT
📖 第 1 页 / 共 2 页
字号:
 解读Windows 2000/XP分层驱动模型
           
   可扩展性是Windows NT/2000/XP设计的目标之一,其分层驱动模型是可扩展性的最好体现。实现分层依赖于IO管理器的两个重要的设计:1、Windows中的任何一个驱动程序都被设计成Client/Server模式。对于客户端驱动,通过IoGetDeviceObjectPointer之类的获取服务端驱动导出的Device对象,通过IO管理器的IoCallDriver请求服务端的服务。IoCallDriver实际上根据客户端的调用参数(通过IRP)调用服务端的派遣入口(回调函数)接受客户端的请求。2、IO管理器实现一个分层的数据结构,在DEVICE_OBJECT对象中保存某种关系,自动将请求IRP发给设备栈中的最高的一个设备,由其决定如何处理,或是自身处理,或是向下传递,达到分层的目的。鉴于这种能力,分层驱动模型可以实现很多应用,如文件监控,加密,防病毒等等,由于PNP的引入,这种应用将更加广泛。实际上这种分层模型在Windows NT/2000/XP中无处不在,不信的话,请执行如下命令看看:

   findstr /M IoAttachDevice %SYSTEMROOT%\system32\drivers\*.sys

   所有列出的Driver几乎都可以看作分层驱动的例子。对于分层驱动的介绍几乎充斥着所有介绍Windows驱动的任何一本书中。本文不想过多于重复这些内容,旨在从底层数据结构的实现上说明这种分层的实现。我们首先从DEVICE_OBJECT开始说明。下面是这个结构的部分定义:

   typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _DEVICE_OBJECT {
          .
          .
          .
       struct _DRIVER_OBJECT *DriverObject;
       struct _DEVICE_OBJECT *NextDevice;
       struct _DEVICE_OBJECT *AttachedDevice;
          .
          .
          .
       PVOID DeviceExtension;
          .
          .
          .
       CCHAR StackSize;
          .
          .
          .
       struct _DEVOBJ_EXTENSION  *DeviceObjectExtension;
          .
          .
          .
   } DEVICE_OBJECT;

   成员DriverObject是DeviceObject对应的DRIVER_OBJECT,通过这个对象,IO管理器可以知道如何为这个设备提供服务(通过调用DriverObject提供的Dispatch入口)。通常一个DRIVER_OBJECT可以为一至多个DEVICE_OBJECT提供服务,各个DEVICE服务不同的同一类型的物理或逻辑设备。她们也有可能扮演不同的角色,如对于PNP引入的PDO与FDO,下面我会详细介绍。正因为这样NextDevice成员即用于链接这些DEVICE_OBJECT。所以可以得到这样的结论,像前一句所描述的,对于同一个Driver导出的PDO与FDO则通过NextDevice成员逻辑上建立关系。而对于AttachedDevice成员对于Legacy的Driver(Windows NT 4.0之前,在之后的版本中也可以正常使用),主要通过这一字段来实现本文开头提到的IO管理器提供的第二项功能。如下示意图所示,AttacheDevice1附加至Device1之上, 这种情况下AttachDevice1与Device1通常不是由同一个Driver服务的,即他们没有通过NextDevice成员链接在一起的,这样我们可以通过书写另一个Driver,通过附加一个Device至一个已存在的设备上改变或监视这个设备的行为。当然这时候Device1的AttachedDevice成员即指向AttachDevice1。而AttachedDevice1并没有被任何设备附接过,所以其AttachedDevice成员指向NULL。通过调用IoCallDriver请求Device1服务时,IO管理器内部会调用IoGetAttachedDevice之类的,获得附接在Device1之上的最高层设备。这里是AttachedDevice1,而对于Device2,即是AttachedDevice3,如果没有附接任何设备,当然就是Device1了。这样我们附接的设备就有机会执行相应的操作了。另外对于下图Device1与Device2的情况,很明显,Device1位于Device2之上,而这时就是我们开头介绍的第一种情况,Device1通过IoCallDriver请求Device2,逻辑上建立一种关系(我们不能通过任何数据结构标识这种关系,通常这是驱动开发人员设计成的逻辑关系,对于Windows 2000/XP上如何知道这种关系,当然最好的出入就是DDK文档了)。


   |-------------|____AttachDevice1
   |   Device1   |
   |-------------|


                                      ------AttachDevice3
   |-------------|____AttachDevice2---|
   |   Device2   |
   |-------------|


   DeviceExtension成员通常是驱动开发人员自已定义的结构,其存储设备相关的内容,例如用于区别PDO与FDO等等。在调用IoCreateDevice时指定其大小,IO管理器分配sizeof(DEVICE_OBJECT)+DeviceExtensionSize的非分页内存用于设备对象,这样这两个结构在物理上是连续的,所以我们在一些文件系统驱动程序中经常看过VOLUME_DEVICE_OBJECT这样的定义(紧随标准的DEVICE_OBJECT后即是专用的用于VOLUME的定义,避免在Dispatch入口每次都要引用这个成员)。

   StackSize指当前设备栈的设备个数(AttachedDevice个数加上设备本身),用于IO管理器分配IRP时指定STACK_LOCATION个数。

   上面的所有叙述即构成了Windows NT 4.0之前的Windows分层驱动模型。这也是遗留的(Legacy)的分层驱动程序的主要工作思路。AttachedDevice指出了Attached了的设备,Microsoft在Windows 2000中的DeviceObjectExtension结构成员中引入了一个AttachedTo指出被当前设备Attached的低层设备,这在Windows NT是没有实现的。DeviceObjectExtension是一个很重要的结构,下面要介绍的支持pnp的WDM驱动的AddDevice入口也在这儿。她是由系统定义的结构(区别于DeviceExtension)。对于DRIVER_OBJECT也有个类似的称为DriverExtenion的结构,后者有一个ClientDriverExtension结构,由IoAllocateDriverObjectExtension分配,IoGetDriverObjectExtension获得。classpnp.sys即是通过这一方法,实现对disk.sys、tape.sys与cdrom.sys的管理;NDIS.SYS也通过这一方法实现各个Miniport/IM/Protocol Driver的管理。

    Windows 2000及其以后的NT系列引入了WDM,支持PNP、Power管理及WMI,为了让操作系统本身就对此支持,ntoskrnl.exe中导出了几个置有DRVO_BUILTIN_DRIVER(ntddk.h中定义)标志的Driver,分别为pnpmanager、WMIxWDM及ACPI_HAL,后者是支持ACPI的HAL(这是在我Windows XP Professional台式机上的情况,不知道不支持ACPI的机子啥模样,我的机子上ntoskrnl.exe还导出\FileSystem\Raw驱动用于文件系统的支持,我想这几个设备名可能会随机器配置及Windows版本不同而不同,实际上我在我Windows 2000 Server SP0笔记本上ntoskrnl.exe生成如下四个设备:Pnpmanager,PCI_HAL,WDM与RAW,我底下的叙述都是基本我台式机上的,至于出现的一些不同我可能会另外指出,也有可能没有),前两个driver都是为了支持WDM而引入的。WMIxWDM导出WMIDataDevice用作WMI的支持,这与本文讨论分层驱动没有关系,以下我重点介绍Pnpmanager。

    在介绍之前,我们来看一下devmgmt.msc“依连接排序设备”视图:

    TSU00(机器名,由pnpmanager实现的虚拟Root总线枚举)
        |
        |+ Advanced Configuration and Power Interface(ACPI) PC(ntoskrnl中的ACPI_HAL驱动实现)
             |
             |+Micrsoft ACPI-Compliant System(由acpi.sys枚举)
                   |
                   |+PCI bus(由pci.sys实现)
                        |
                        |+(连接的设备)

   这是我机子上的情况,pnpmanager是一个总线驱动程序(在ntoskrnl.exe内部实现,如果你有checked build的ntoskrnl.exe,你可以很容易的发现其由base\ntos\io\pnpmgr\pnpdd.c实现,看过Sysinternals导出的Windows XP Source Tree了吗?),她实现一个称为Root的虚拟总线。所有Legacy设备,都是连接至这个虚拟的总线上的,不信的话,你在devmgmt.msc的上面列出的草图上继续选“显示隐藏的设备看看”。从这种意义上看她要为每一个连接到她上面的设备建立一个PDO。对于这些PDO通常是以00000001开始的十六进制命名,如在我机子我实验某一时刻设备名一直至00000050,共80个PDO(在我的Windows 2000 Server的机子上并不是这样命名的,虽然也是基于十六进制的,但却是从挺大的一个数值开始的)。我非常喜欢随Windows XP DDK一些发行的OSR的DeviceTree,但为了更好的理解,还是以windbg作个实验吧:

   找出上面草图ACPI_HAL附接的由pnpmanager实现的PDO,通常这是pnpmanager实现的第一个PDO,即命名为00000001(如果你的Pnpmanager生成的设备不是这样命名的,请使用!drvobj pnpmanager找出生成的对应的PDO,我的Windows 2000 Server SP0的笔记本上,pnpmanager的第一个PDO用于服务ESS声卡,而并不是我原以为的PCI_HAL,你可能另需要使用!devstack或是下面要介绍的!devnoe命令,方法不详述):

   kd> !object \Device\00000001

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -