Windows驱动
+ -

Windows驱动设备栈

2021-09-17 553 0

1.栈结构

设备栈(Device Stack)结构与内存中的栈类似,但是 device stack 中的 entry 由 device object 中的 AttachedDevice 值的连接。
155547153159

并且由每个 device 的 DeviceExtension.AttachedTo 值指向下一层的 device。从而形成双向的链结构。

2. 栈顶

由 IoGetAttachedDevice() 函数来得到当前栈顶 device:

PDEVICE_OBJECT
IoGetAttachedDevice(
IN PDEVICE_OBJECT DeviceObject
)
{
    //
    // 直到 Top 元素的 AttachedDevice == NULL 
    //
    while (DeviceObject->AttachedDevice)
    DeviceObject = DeviceObject->AttachedDevice;

    return DeviceObject;
}

当传递给 IoGetAttachedDevice() 的 DeviceObject 已经是 Top 元素则返回 DeviceObject 值。

3. 栈底

使用 IoGetDeviceAttachmentBaseRef() 来得到栈底的 device object,IoGetDeviceAttachmentBaseRef() 将调用 IopGetDeviceAttachmentBase() 来遍历查找:

PDEVICE_OBJECT
IoGetDeviceAttachmentBaseRef(
IN PDEVICE_OBJECT  DeviceObject
)
{
  KIRQL OldIrql = KeRaiseIrqlToDpcLevel();

  PDEVICE_OBJECT Bottom = IopGetDeviceAttachmentBase(DeviceObject);
  ObfReferenceObject(Bottom);

  KfLowerIrql(OldIrql);

  return Bottom;
}



PDEVICE_OBJECT
IopGetDeviceAttachmentBase(
IN PDEVICE_OBJECT  DeviceObject
)
{
    //
    // 根据 DeviceObjectExtension->AttachedTo 向下查找栈底
    // 
    while (DeviceObject->DeviceObjectExtension->AttachedTo)
    {
        DeviceObject = DeviceObject->DeviceObjectExtension->AttachedTo;
    }

    return DeviceObject;
}

IoGetDeviceAttachmentBaseRef() 函数根据传递的 DeviceObject 值,找到在 Bottom 元素。如果 DeviceObject 之下没有 device object 则返回参数值。

在微软的 WDK 文档里描述:当 DeviceObject 之下没有 device object 时返回 NULL,而实际的代码是返回 DeviceObject 本身(windows 2003)。

4. 挂接 device

使用 IoAttachDeviceToDeviceStack() 函数来挂接 device,它实际上调用另一个函数来完成工作:

如下代码所示:

PDEVICE_OBJECT IoAttachDeviceToDeviceStack(
        IN PDEVICE_OBJECT SourceDevice,
        IN PDEVICE_OBJECT TargetDevice
        )
{
        return IopAttachDeviceToDeviceStackSafe(
SourceDevice, 
TargetDevice, 
NULL);                // AttachedToDeviceObject 参数为 NULL
}

它简单地调用 IopAttachDeviceToDeviceStackSafe() 函数,提供第 3 个参数为 NULL 值。

下面我将它逆为 C 代码,看起来像下面这样:

PDEVICE_OBJECT IopAttachDeviceToDeviceStackSafe(
IN PDEVICE_OBJECT SourceDevice,
IN PDEVICE_OBJECT TargetDevice
OUT PDEVICE_OBJECT *AttachedToDeviceObject OPTIONAL
)
{
    PDEVICE_OBJECT devObj = NULL;// 返回被 attach 的 object
    PDEVICE_EXTENSION SourceDeviceExtension = SourceDevice->DeviceObjectExtension;

    KIRQL OldIrql = KeRaiseIrqToDpcLevel();// 提升到 DISPATCH_LEVEL

    if (IopVerifierOn == TRUE )
    {
    IovAttachDeviceToDeviceStack(SourceDevice, TargetDevice);
    }

    //
    // 得到 stack top 的 device
    //
    devObj = IoGetAttachedDevice(TargetDevice);

    PDEVICE_EXTENSION DeviceExtension = devObj->DeviceObjectExtension;

    //
    // 下面进行 attach 操作
    //
    if ((devObj->Flags & DO_DEVICE_INITIALIZING == 0) &&
    (DeviceExtension->ExtensionFlags & (DOE_UNLOAD_PENDING | DOE_DELETE_PENDING |
    DOE_REMOVE_PENDING | DOE_REMOVE_PROCESSED) == 0))
    {

        devObj->Spare1++;
        devObj->AttachedDevice = SourceDevice;
        SourceDevice->StackSize = devObj->StackSize + 1;
        SourceDevice->AlignmentRequirement = devObj->AlignmentRequirement;
        SourceDevice->SectorSize = devObj->SectorSize;

        if (DeviceExtension->ExtensionFlags & DOE_START_PENDING)
        {
            SourceDevice->DeviceObjectExtension->ExtensionFlags |= DOE_START_PENDING;
        }

        SourceDeviceExtension->AttachedTo = devObj;
    }
    else 
    {
        devObj = NULL;
    }


    if (AttachedToDeviceObject != NULL)
    {
        *AttachedToDeviceObject = devObj;// 返回被 attach 的 device object
    }


    KfLowerIrql(OldIrql);         // 恢复原 IRQL 
    return devObj;
}

IoAttachDeviceToDeviceStack() 函数的目的是将 SourceDevice 挂接在 TargetDevice 之上。那么:SourceDevice 将变为栈 Top 元素。
155602263085

成功挂接后,函数返回原 Top 元素,如图中的 device B object。

5. 移除 device

使用 IoDetachDevice() 函数来从设备栈中的元素:

VOID 
IoDetachDevice(
IN OUT PDEVICE_OBJECT  TargetDevice
)
{
    KIRQL OldIrql = KeRaiseIrqlToDpcLevel();

    if (IopVerifierOn == TRUE)
    {
        IovDetachDevice(TargerDevice);
    }

    //
    // 删除挂接
    //
    TargetDevice->AttachedDevice->DeviceObjectExtension->AttachedTo = NULL; 
    TargerDevice->AttachedDevice = NULL;

    //
    // 检测是否需要 Unload 或 Delete 操作
    //
    if ((TargetDevice->DeviceObjectExtension->ExtensionFlags & (DOE_UNLOAD_PENDING | DOE_DELETE_PENDING 
    | DOE_REMOVE_PENDING)) && (TargerDevice->ReferenceCount == 0))
    {
        IopCompleteUnloadOrDelete(TargetDevice, FALSE, OldIrql);
    }
    else
    {
        KfLowerIrql(OldIrql);
    }
}

IoDetachDevice() 函数接受的参数是由 IoAttachDeviceToDeviceStack() 挂接成功后返回的值。
155614811389

如上图所示,将移除 device B 元素。最后调用IopCompleteUnloadOrDelete() 函数做 Unload 或 Delete 操作。

0 篇笔记 写笔记

Windows驱动设备栈
1.栈结构设备栈(Device Stack)结构与内存中的栈类似,但是 device stack 中的 entry 由 device object 中的 AttachedDevice 值的连接。并且由每个 device 的 DeviceExtension.AttachedTo 值指向下一层的 de......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!