【问题标题】:How to get from device-manager device (e.g. from its "Physical Device Object name") to its drive-letter?如何从设备管理器设备(例如,从其“物理设备对象名称”)获取其驱动器号?
【发布时间】:2017-07-14 14:15:56
【问题描述】:

在设备管理器中,我有一个 USB 设备节点。我提取了它的“物理设备对象名称”(例如\Device\0000010f)。

NtOpenDirectoryObjectNtQueryDirectoryObjectNtOpenSymbolicLinkObjectNtQuerySymbolicLinkObjectQueryDosDevice 战斗了几个小时,我找不到从“物理设备对象名称”到实际驱动器号的方法(C:, D:, ...)。

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


(有很多类似的问题,没有一个回答,例如如何从物理设备对象名称获取到\Device\HarddiskVolumeXYZVolume{SOME_GUID}

【问题讨论】:

  • 这里没有 1:1 映射,您可以在一个物理设备上安装多个驱动器号。最好一一浏览驱动器号并找出它们的物理设备。
  • @JonathanPotter,当然,但是有 1:N 映射,在这种情况下,我需要在该设备上安装所有分区(反之亦然 - 来自分区的设备,没关系)。好的 - “最好一个一个地检查驱动器号并找出它们的物理设备” - 怎么样?
  • @Tar:也没有 1:N 映射。卷不需要分配驱动器号。驱动器号是 MS DOS 新时代遗留下来的。

标签: winapi wmi


【解决方案1】:

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

可以附加一些FDO功能设备对象)。如果这是来自存储堆栈(基于总线设备为此 PDO 返回的 CompatibleIDs 字符串)典型的 FDO 名称的格式为 \Device\Harddisk%d\DR%d 并且很好到它的已知符号链接\Device\Harddisk%d\Partition0

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

通常的分区与卷相同,但并不总是 (Partitions and Volumes) 还要注意 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS - 它返回 DISK_EXTENT 结构的数组 - 在此处查找 DiskNumber -The number of the disk that contains this extent. 以便卷可以放置在多个磁盘上。但在 99%+ IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 只返回给你一个 DISK_EXTENT

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

  1. 打开设备-如果使用ZwOpenFile(当然这在 用户模式)我们可以按原样使用\Device\0000010f。如果我们想使用 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 步得到)将符号链接格式化为 分区 PDO - \\?\GLOBALROOT\Device\Harddisk%d\Partition%d
  5. SYNCHRONIZE 访问打开分区PDO(足够了,因为所有 我们使用的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);
    }
}

【讨论】:

  • 好的哇。一旦我得到这个工作,如果可以的话,我会接受你的回答 10 次......但只是,呃,小东西 - 你给 alloca(guz)guz 是什么?
  • @Tar static volatile UCHAR guz; 这真的是 0。我使用 alloca(0) 获取顶部堆栈指针,但编译器非常“智能”并且可以放弃这个调用。为了防止这种情况,我使用alloca(guz) - 这与alloca(0) 具有相同的效果,但是因为我将guz 定义为volatile,所以这是禁用“优化”并且所有工作都作为例外。但是这是我的(在堆栈中分配内存的非常罕见的风格)你可以使用自己的方式。这与问题(磁盘/卷/等)无关
  • 仍在为DeviceIoControl(hPartition, IOCTL_MOUNTDEV_QUERY_STABLE_GUID, 0, 0, &amp;guid, sizeof(guid), &amp;dwBytesRet, NULL) 苦苦挣扎:我收到The request is not supported(错误号50 或0x32)。我确定并使用WinObj 安装了该设备(例如\\?\GLOBALROOT\Device\Harddisk2\Partition0)。
  • @Tar - 这对于 USB 磁盘来说是绝对正常的。一定是。我添加这个调用只是为了演示(通常只有硬盘驱动器支持它)
  • @Tar - 在函数 QueryPartitionW32 中的前 2 个调用仅用于演示。结果未使用。可以跳过它并从 PVOID stack = alloca(guz); 开始 - 真的需要从 IOCTL_MOUNTDEV_QUERY_DEVICE_NAME 开始
【解决方案2】:

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

$PDO = "\Device\00000052"
$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"
}

【讨论】:

  • 在另一台机器上非常有用,当我只有 powershell 时 - 谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-02
  • 2014-03-27
相关资源
最近更新 更多