设备扩展
对于大多数中间和最低级别的驱动程序,设备扩展是与设备对象关联的最重要的数据结构。 其内部结构由驱动程序定义,通常用于:
维护设备状态信息。
为驱动程序使用的任何内核定义对象或其他系统资源(如旋转锁)提供存储。
保存驱动程序必须在系统空间中驻留的任何数据,以执行其 I/O 操作。
由于大多数总线、函数和筛选器驱动程序 (最低级别和中间驱动程序) 任意线程上下文 (任意线程上下文) 中执行,因此设备扩展是每个驱动程序维护设备状态和驱动程序所需的所有其他设备特定数据的主要位置。 例如,实现 CustomTimerDpc 或 CustomDpc 例程的任何驱动程序通常为设备扩展中所需的内核定义的计时器和/或 DPC 对象提供存储。
每个具有 ISR 的驱动程序都必须为指向一组内核定义的中断对象的指针提供存储,并且大多数设备驱动程序将此指针存储在设备扩展中。 每个驱动程序在创建设备对象时确定设备扩展的大小,每个驱动程序定义其自己的设备扩展的内容和结构。
I/O 管理器的 IoCreateDevice 和 IoCreateDeviceSecure 例程从非分页内存池中为设备对象和扩展分配内存。
接收 IRP 的每个标准驱动程序例程还会接收指向表示所请求 I/O 操作的目标设备的设备对象的指针。 这些驱动程序例程可以通过此指针访问相应的设备扩展。 通常, DeviceObject 指针也是最低级别驱动程序 ISR 的输入参数。
下图显示了最低级别驱动程序的设备对象的设备扩展的一组具有代表性的驱动程序定义数据。 较高级别的驱动程序不会为 IoConnectInterrupt 返回并传递给 KeSynchronizeExecution 和 IoDisconnectInterrupt 的中断对象指针提供存储。 但是,如果驱动程序具有 CustomTimerDpc 例程,则较高级别的驱动程序将为下图中显示的计时器和 DPC 对象提供存储。 更高级别的驱动程序还可以为执行旋转锁和互锁工作队列提供存储。
除了为中断对象指针提供存储外,如果最低级别的设备驱动程序的 ISR 处理不同向量上的两个或更多设备的中断,或者如果具有多个 ISR,则必须为中断旋转锁提供存储。 有关注册 ISR 的详细信息,请参阅 注册 ISR。
通常,驱动程序将指向其设备对象的指针存储在其设备扩展中,如图所示。 驱动程序还可以在扩展中保留设备的资源列表的副本。
较高级别的驱动程序通常在其设备扩展中存储指向下一级别驱动程序的设备对象的指针。 在 IRP 中设置下一个较低级别的驱动程序的 I/O 堆栈位置后,较高级别的驱动程序必须将指向下一级别驱动程序的设备对象的指针传递给 IoCallDriver,如 处理 IRP 中所述。
另请注意,为较低级别的驱动程序分配 IRP 的任何较高级别的驱动程序都必须指定新 IRP 应具有的堆栈位置数。 具体而言,如果较高级别的驱动程序调用 IoMakeAssociatedIrp、 IoAllocateIrp 或 IoInitializeIrp,则必须访问下一个较低级别驱动程序的目标设备对象来读取其 StackSize 值,以便向这些支持例程提供正确的 StackSize 作为参数。
虽然较高级别的驱动程序可以通过 IoAttachDeviceToDeviceStack 返回的指针从下一个较低级别的驱动程序的设备对象读取数据,但此类驱动程序必须遵循以下实现准则:
切勿尝试将数据写入较低驱动程序的设备对象。
此准则的唯一例外是文件系统,它们设置和清除较低级别可移动媒体驱动程序设备对象的 标志 中的DO_VERIFY_VOLUME。
切勿出于以下原因尝试访问较低驱动程序的设备扩展:
没有安全的方法在两个驱动程序之间同步对单个设备扩展的访问。
实现此类后门通信方案的一对驱动程序无法单独升级,无法在不更改现有驱动程序源的情况下在它们之间插入中间驱动程序,并且无法轻松地从一个 Windows 平台重新编译和移动到下一个 Windows 平台。
为了保持它们与从一个 Windows 平台或版本到下一个 Windows 平台的较低级别驱动程序的互操作性,较高级别的驱动程序必须重用给定它们的 IRP,或者必须创建新的 IRP,并且必须使用 IoCallDriver 将请求传达给较低级别的驱动程序。