1. 栈结构
设备栈(Device Stack)结构与内存中的栈类似,但是 device stack 中的 entry 由 device object 中的 AttachedDevice 值的连接。
如下图所示:
并且由每个 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 元素。
成功挂接后,函数返回原 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() 挂接成功后返回的值。
如上图所示,将移除 device B 元素。最后调用IopCompleteUnloadOrDelete() 函数做 Unload 或 Delete 操作。
作者:邓志