如何从设备管理器设备(例如从其 "Physical Device Object name")获取其驱动器号?

How to get from device-manager device (e.g. from its "Physical Device Object name") to its drive-letter?

从设备管理器中,我有一个 USB 设备节点。我提取了它的"Physical Device Object name"(例如\Device[=11=]00010f)。

NtOpenDirectoryObjectNtQueryDirectoryObjectNtOpenSymbolicLinkObjectNtQuerySymbolicLinkObjectQueryDosDevice 战斗数小时后,我找不到解决方法"Physical Device Object name" 到实际的驱动器号(C:D:、...)。

我正在寻找任何存储解决方案 (USB/SATA/...)。我该怎么做?


(有很多类似的问题,其中 none 回答了例如如何从物理设备对象名称到 \Device\HarddiskVolumeXYZVolume{SOME_GUID}

你看到的\Device[=11=]00010f这是由一些总线驱动程序创建的PDO物理设备对象)(它有标志DO_BUS_ENUMERATED_DEVICE)

它可以附加一些 FDO功能设备对象)。如果这是来自存储堆栈(基于 CompatibleIDs 总线设备为此 PDO 返回的字符串)典型的 FDO 名字有形式 \Device\Harddisk%d\DR%d 和众所周知的符号 link \Device\Harddisk%d\Partition0

磁盘驱动程序 FDO 枚举卷上的分区并为每个分区创建 PDO 设备对象(具有众所周知的符号 link \Device\Harddisk%d\Partition%d 其中分区号总是 > 0,Partition0 是指整个磁盘 FDO)

通常的分区与卷相同,但并非总是如此 (Partitions and Volumes) also note IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS - it return array of DISK_EXTENT structures - look here for DiskNumber -The number of the disk that contains this extent. so volume can placed on several disks. but in 99%+ IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS return to you only one DISK_EXTENT

那么,如果您在存储堆栈中有 PDO 的路径,您可以做什么?

  1. 打开设备 - 如果使用 ZwOpenFile(当然这在 用户模式)我们可以按原样使用 \Device[=11=]00010f 。如果我们想使用 win32 api 我们需要为所有名称使用前缀 \?\GLOBALROOT。我们实际上是通过这条总线 PDO 打开的,但是因为磁盘 FDO 附加到 PDO 我们所有的请求都将是通过 FDO 发送并在此处处理。所需的访问权限? SYNCHRONIZE 就足够了(如果 CreateFile 如果我们不设置 FILE_FLAG_OVERLAPPED api 隐式将此标志添加到 DESIRED_ACCESS
  2. 发送IOCTL_STORAGE_GET_DEVICE_NUMBER到设备。检查那个 DeviceType == FILE_DEVICE_DISK && sdn.PartitionNumber == 0
  3. 发送IOCTL_DISK_GET_DRIVE_LAYOUT_EX获取可变大小 PARTITION_INFORMATION_EX 结构数组
  4. 基于 DeviceNumber(我们在第 2 步得到)和 PartitionNumber(我们在第 3 步得到)格式化符号 link 到 分区 PDO - \?\GLOBALROOT\Device\Harddisk%d\Partition%d
  5. 打开分区 PDO 并具有 SYNCHRONIZE 访问权限(足够了,因为所有 我们使用的IOCTLFILE_ANY_ACCESS类型
  6. 发送IOCTL_MOUNTDEV_QUERY_DEVICE_NAME到分区
  7. 现在我们需要 MountManager 设备的句柄 (\.\MountPointManager) 发送给他 IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH 这个 IOCTL 在输入中定义 mountmgr.h 它需要 MOUNTDEV_NAME 我们在第 6 步得到。在输出中我们收到 MOUNTMGR_VOLUME_PATHS 结构(也在 mountmgr.h 中定义)或者我们可以使用 IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS - 如果一切正常,我们得到列表 C:D: 等驱动器号。
  8. 我们可以使用枚举系统中的磁盘驱动器 CM_Get_Device_ID_ListW{4d36e967-e325-11ce-bfc1-08002be10318}过滤,打开每一个设备 通过 CM_Locate_DevNodeW 实例并最终查询 DEVPKEY_Device_PDOName 通过电话 CM_Get_DevNode_Property

好的,这里是正确的代码示例,可以完成所有这些操作:

#include <mountmgr.h>

// guz == 0 always, volatile for prevent CL "optimization" - it can drop alloca(0) call
static volatile UCHAR guz;

ULONG QueryPartitionW32(HANDLE hPartition, HANDLE hMountManager)
{
    MOUNTDEV_STABLE_GUID guid;
    ULONG dwBytesRet;

    if (DeviceIoControl(hPartition, IOCTL_MOUNTDEV_QUERY_STABLE_GUID, 0, 0, &guid, sizeof(guid), &dwBytesRet, NULL))
    {
        DbgPrint("StableGuid = \\?\Volume{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
            guid.StableGuid.Data1, guid.StableGuid.Data2, guid.StableGuid.Data3,
            guid.StableGuid.Data4[0],
            guid.StableGuid.Data4[1],
            guid.StableGuid.Data4[2],
            guid.StableGuid.Data4[3],
            guid.StableGuid.Data4[4],
            guid.StableGuid.Data4[5],
            guid.StableGuid.Data4[6],
            guid.StableGuid.Data4[7]
        );
    }

    // assume NumberOfDiskExtents == 1
    VOLUME_DISK_EXTENTS vde;
    if (DeviceIoControl(hPartition, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, 0, 0, &vde, sizeof(vde), &dwBytesRet, 0))
    {
        if (vde.NumberOfDiskExtents)
        {
            DbgPrint("ofs=%I64u, len=%I64u\n", vde.Extents->StartingOffset.QuadPart, vde.Extents->ExtentLength.QuadPart);
        }
    }

    PVOID stack = alloca(guz);

    union {
        PVOID buf;
        PMOUNTDEV_NAME pmdn;
    };

    ULONG err;
    ULONG cb = 0, rcb = sizeof(MOUNTDEV_NAME) + 0x10, InputBufferLength;

    do 
    {
        if (cb < rcb)
        {
            cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
        }

        if (DeviceIoControl(hPartition, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, 0, 0, buf, cb, &dwBytesRet, NULL))
        {
            DbgPrint("%.*S\n", pmdn->NameLength >> 1, pmdn->Name);

            union {
                PVOID pv;
                PMOUNTMGR_VOLUME_PATHS pmvp;
            };

            cb = 0, rcb = sizeof(MOUNTMGR_VOLUME_PATHS) + 0x10, InputBufferLength = sizeof(MOUNTDEV_NAME) + pmdn->NameLength;

            do 
            {
                if (cb < rcb)
                {
                    cb = RtlPointerToOffset(pv = alloca(rcb - cb), pmdn);
                }

                if (DeviceIoControl(hMountManager, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
                    pmdn, InputBufferLength, pv, cb, &dwBytesRet, NULL))
                {
                    PWSTR sz = pmvp->MultiSz;

                    while(*sz)
                    {
                        DbgPrint("%S\n", sz);
                        sz += 1 + wcslen(sz);
                    }
                    return NOERROR;
                }

                rcb = sizeof(MOUNTMGR_VOLUME_PATHS) + pmvp->MultiSzLength;

            } while ((err = GetLastError()) == ERROR_MORE_DATA);

            break;
        }

        rcb = sizeof(MOUNTDEV_NAME) + pmdn->NameLength;

    } while ((err = GetLastError()) == ERROR_MORE_DATA);

    return err;
}

ULONG EnumDiskPartitionsW32(HANDLE hDisk, HANDLE hMountManager)
{
    STORAGE_DEVICE_NUMBER sdn;

    ULONG dwBytesRet;

    if (!DeviceIoControl(hDisk, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesRet, NULL))
    {
        return GetLastError();
    }

    if (sdn.DeviceType != FILE_DEVICE_DISK || sdn.PartitionNumber != 0)
    {
        return ERROR_GEN_FAILURE;
    }

    WCHAR sz[128], *c = sz + swprintf(sz, L"\\?\GLOBALROOT\Device\Harddisk%d\Partition", sdn.DeviceNumber);

    PVOID stack = alloca(guz);

    union {
        PVOID buf;
        PDRIVE_LAYOUT_INFORMATION_EX pdli;
    };

    ULONG cb = 0, rcb, PartitionCount = 4;

    for (;;)
    {
        if (cb < (rcb = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[PartitionCount])))
        {
            cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
        }

        if (DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, buf, cb, &dwBytesRet, NULL))
        {
            if (PartitionCount = pdli->PartitionCount)
            {
                PPARTITION_INFORMATION_EX PartitionEntry = pdli->PartitionEntry;

                do 
                {
                    if (!PartitionEntry->PartitionNumber)
                    {
                        continue;
                    }

                    _itow(PartitionEntry->PartitionNumber, c, 10);

                    DbgPrint("%S\n", sz);

                    HANDLE hPartition = CreateFile(sz, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

                    if (hPartition != INVALID_HANDLE_VALUE)
                    {
                        QueryPartitionW32(hPartition, hMountManager);
                        CloseHandle(hPartition);
                    }

                } while (PartitionEntry++, --PartitionCount);
            }

            return NOERROR;
        }

        switch (ULONG err = GetLastError())
        {
        case ERROR_MORE_DATA:
            PartitionCount = pdli->PartitionCount;
            continue;
        case ERROR_BAD_LENGTH:
        case ERROR_INSUFFICIENT_BUFFER:
            PartitionCount <<= 1;
            continue;
        default:
            return err;
        }
    }
}

void DiskEnumW32(HANDLE hMountManager)
{
    static const WCHAR DEVCLASS_DISK[] = L"{4d36e967-e325-11ce-bfc1-08002be10318}";

    enum { flags = CM_GETIDLIST_FILTER_CLASS|CM_GETIDLIST_FILTER_PRESENT };

    ULONG len;
    if (!CM_Get_Device_ID_List_SizeW(&len, DEVCLASS_DISK, flags))
    {
        PWSTR buf = (PWSTR)alloca(len * sizeof(WCHAR));

        if (!CM_Get_Device_ID_ListW(DEVCLASS_DISK, buf, len, flags))
        {
            PVOID stack = buf;
            static const WCHAR prefix[] = L"\\?\GLOBALROOT";

            ULONG cb = 0, rcb = sizeof(prefix) + 0x20;

            while (*buf)
            {
                DbgPrint("%S\n", buf);

                DEVINST dnDevInst;
                if (!CM_Locate_DevNodeW(&dnDevInst, buf, CM_LOCATE_DEVNODE_NORMAL))
                {
                    DEVPROPTYPE PropertyType;
                    int err;

                    union {
                        PVOID pv;
                        PWSTR sz;
                        PBYTE pb;
                    };

                    do 
                    {
                        if (cb < rcb)
                        {
                            rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
                        }

                        rcb -= sizeof(prefix) - sizeof(WCHAR);

                        if (!(err = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_Device_PDOName, &PropertyType, 
                            pb + sizeof(prefix) - sizeof(WCHAR), &rcb, 0)))
                        {
                            if (PropertyType == DEVPROP_TYPE_STRING)
                            {
                                memcpy(pv, prefix, sizeof(prefix) - sizeof(WCHAR));
                                DbgPrint("%S\n", sz);

                                HANDLE hDisk = CreateFile(sz, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

                                if (hDisk != INVALID_HANDLE_VALUE)
                                {
                                    EnumDiskPartitionsW32(hDisk, hMountManager);
                                    CloseHandle(hDisk);
                                }
                            }
                            else
                            {
                                err = ERROR_GEN_FAILURE;
                            }

                            break;
                        }

                        rcb += sizeof(prefix) - sizeof(WCHAR);

                    } while (err == CR_BUFFER_SMALL);

                }
                buf += 1 + wcslen(buf);
            }
        }
    }
}

void DiskEnumW32()
{
    HANDLE hMountManager = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);

    if (hMountManager != INVALID_HANDLE_VALUE)
    {
        DiskEnumW32(hMountManager);
        CloseHandle(hMountManager);
    }
}

另一种可能的解决方案,仅基于 PowerShell 和 WMI:

$PDO = "\Device[=10=]000052"
$DiskDriverData = Get-WmiObject Win32_PNPSignedDriver | Where {$_.PDO -eq $PDO}
$DeviceID = """" + $DiskDriverData.DeviceID.Replace("\","\") + """"
$ComputerInfo = Get-WmiObject Win32_Computersystem
$name = $ComputerInfo.Name
$FullString = "\$name\root\cimv2:Win32_PnPEntity.DeviceID=$DeviceID"

$PNPDevice = Get-WmiObject Win32_PNPDevice | Where {$_.SystemElement -eq $FullString}

$DiskDriveToPartition = Get-WmiObject -Class Win32_DiskDriveToDiskPartition | where {$_.Antecedent -eq $PNPDevice.SameElement}

foreach ($i in $DiskDriveToPartition) {
$Partition = Get-WmiObject -Class Win32_LogicalDiskToPartition | Where {$_.Antecedent -eq $i.Dependent}
$PartitionLetter = $Partition.Dependent.split('"')[1]
Write-Host -ForegroundColor Green "Detected Partition for the given PDO: $PartitionLetter"
}