You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows过滤驱动中如何判断DeviceObject是否为便携设备?

正确判断便携可移动设备的方法

你之前直接用上层DeviceObject的Characteristics字段判断行不通——过滤驱动通常附加在功能设备对象(FDO)或中间过滤设备上,这些上层设备的属性不会直接反映物理硬件的真实特性,必须先获取到最底层的物理设备对象(PDO),再查询PDO的硬件属性才能准确判断。

以下是两种可靠的实现方式:

方法1:通过IoQueryDeviceProperty查询可移动属性

这是最直接的方式,通过系统API直接获取设备的可移动能力:

NTSTATUS IsDeviceRemovable(PDEVICE_OBJECT DeviceObject, BOOLEAN* IsRemovable)
{
    if (!IsRemovable)
        return STATUS_INVALID_PARAMETER;

    *IsRemovable = FALSE;
    // 获取底层PDO
    PDEVICE_OBJECT pdo = IoGetAttachedDeviceReference(DeviceObject);
    if (!pdo)
        return STATUS_UNSUCCESSFUL;

    DEVICE_CAPABILITIES caps = {0};
    caps.Size = sizeof(DEVICE_CAPABILITIES);
    NTSTATUS status = IoQueryDeviceProperty(
        pdo,
        DevicePropertyCapabilities,
        sizeof(caps),
        &caps,
        NULL
    );

    if (NT_SUCCESS(status))
    {
        *IsRemovable = caps.Removable;
    }

    ObDereferenceObject(pdo);
    return status;
}

调用这个函数后,若IsRemovable返回TRUE,则说明当前设备是可移动便携设备(包括U盘、SD卡、移动硬盘等)。

方法2:通过存储设备属性查询更细节信息

如果需要更精准的判断(比如只拦截USB类便携设备),可以通过IOCTL_STORAGE_QUERY_PROPERTY查询存储设备的总线类型和可移动属性:

NTSTATUS IsUsbPortableDevice(PDEVICE_OBJECT DeviceObject, BOOLEAN* IsUsbPortable)
{
    if (!IsUsbPortable)
        return STATUS_INVALID_PARAMETER;

    *IsUsbPortable = FALSE;
    PDEVICE_OBJECT pdo = IoGetAttachedDeviceReference(DeviceObject);
    if (!pdo)
        return STATUS_UNSUCCESSFUL;

    STORAGE_PROPERTY_QUERY query = {0};
    query.PropertyId = StorageDeviceProperty;
    query.QueryType = PropertyStandardQuery;

    // 先查询所需缓冲区大小
    STORAGE_DEVICE_DESCRIPTOR desc = {0};
    desc.Size = sizeof(desc);
    ULONG returnedSize = 0;
    NTSTATUS status = IoBuildDeviceIoControlRequest(
        IOCTL_STORAGE_QUERY_PROPERTY,
        pdo,
        &query, sizeof(query),
        &desc, sizeof(desc),
        FALSE,
        NULL,
        NULL
    );

    if (status == STATUS_BUFFER_TOO_SMALL)
    {
        // 分配足够的缓冲区
        PVOID buffer = ExAllocatePool2(POOL_FLAG_PAGED, desc.Size, 'DevQ');
        if (buffer)
        {
            RtlCopyMemory(buffer, &desc, sizeof(desc));
            status = IoBuildDeviceIoControlRequest(
                IOCTL_STORAGE_QUERY_PROPERTY,
                pdo,
                &query, sizeof(query),
                buffer, desc.Size,
                FALSE,
                NULL,
                NULL
            );

            if (NT_SUCCESS(status))
            {
                PSTORAGE_DEVICE_DESCRIPTOR pDesc = (PSTORAGE_DEVICE_DESCRIPTOR)buffer;
                // 判断是否为USB总线且可移动
                if (pDesc->BusType == BusTypeUsb && pDesc->RemovableMedia)
                {
                    *IsUsbPortable = TRUE;
                }
            }
            ExFreePool(buffer);
        }
    }

    ObDereferenceObject(pdo);
    return status;
}

关键注意事项

  • 必须获取底层PDO:上层设备对象的属性是继承或修改后的,不代表物理硬件;IoGetAttachedDeviceReference会返回设备栈最底层的PDO,用完记得调用ObDereferenceObject释放引用。
  • 同步处理IRP:在过滤驱动中查询设备属性时,建议用同步方式发送IRP(避免异步操作带来的上下文问题),可以用IoCreateIrp+IoCallDriver+KeWaitForSingleObject的组合实现同步调用(上述示例简化了IRP发送逻辑,实际实现需完善同步处理)。

内容的提问来源于stack exchange,提问作者Sinusoidal Depleneration

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 19:37:53