本文介绍了如何从设备管理器设备(例如从它的“物理设备对象名称")到它的驱动器号?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

From the device manager, I have a USB device node. I extracted its "Physical Device Object name" (e.g. \Device\0000010f).

NtOpenDirectoryObjectNtQueryDirectoryObjectNtOpenSymbolicLinkObjectNtQuerySymbolicLinkObjectQueryDosDevice 斗争数小时>,我找不到从物理设备对象名称"到实际驱动器号的方法(C:D:、...).

Fighting for hours with NtOpenDirectoryObject, NtQueryDirectoryObject, NtOpenSymbolicLinkObject, NtQuerySymbolicLinkObject and QueryDosDevice, I couldn't find a way to get from that "Physical Device Object name" to the actual drive-letter (C:, D:, ...).

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

I'm looking for any storage solution (USB/SATA/...). How do I do that?

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

(There are many similar questions, none of them answers e.g. how to get from Physical Device Object name to \Device\HarddiskVolumeXYZ or to Volume{SOME_GUID})

推荐答案

你所看到的 \Device\0000010f this is PDO (Physical Device Object) 由某个总线驱动程序创建(它有标志 DO_BUS_ENUMERATED_DEVICE)

what you view \Device\0000010f this is PDO (Physical Device Object) created by some bus driver (it have flag DO_BUS_ENUMERATED_DEVICE)

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

to it can be attached some FDO (Functional Device Object). if this is from storage stack (based on CompatibleIDs strings returned by bus device for this PDO ) typical FDO name have form \Device\Harddisk%d\DR%d and well known symbolic link to it \Device\Harddisk%d\Partition0

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

disk driver FDO enumerate partitions on volume and for every partition create PDO device object ( with well known symbolic link \Device\Harddisk%d\Partition%d where partition number always > 0, Partition0 is refer to whole disk FDO)

通常的分区与卷相同,但并非总是如此(分区和卷) 还要注意 IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS - 它返回 DISK_EXTENT 结构 - 在此处查找 DiskNumber - 包含此范围的磁盘编号. 因此卷可以放在多个磁盘.但在 99%+ IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 只返回给你一个 DISK_EXTENT

usual partition is same as volume but not always (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 的路径,您可以做什么?

so what you can do if you have path to PDO in storage stack ?

  1. 打开设备 - 如果使用 ZwOpenFile (当然这在用户模式)我们可以按原样使用 \Device\0000010f .如果我们想使用 win32api 我们需要为所有名称使用前缀 \\?\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
  1. open the device - if use ZwOpenFile (of course this worked inuser mode) we can use \Device\0000010f as is. if we want use win32api we need use prefix \\?\GLOBALROOT for all names. really we open by this bus PDO but because disk FDO is attached to PDO all our requests will be send via FDO and handled here. desired access ? SYNCHRONIZE is enough (in case CreateFile if we not set FILE_FLAG_OVERLAPPED api implicity add this flag to DESIRED_ACCESS )
  2. send IOCTL_STORAGE_GET_DEVICE_NUMBER to device. check thatDeviceType == FILE_DEVICE_DISK && sdn.PartitionNumber == 0
  3. send IOCTL_DISK_GET_DRIVE_LAYOUT_EX for get a variable-sizedarray of PARTITION_INFORMATION_EX structures
  4. based on DeviceNumber (which we get at step 2) andPartitionNumber (which we get at step 3) format symbolic link topartition PDO - \\?\GLOBALROOT\Device\Harddisk%d\Partition%d
  5. open partition PDO with SYNCHRONIZE access (enough because allIOCTL which we use have FILE_ANY_ACCESS type
  6. send IOCTL_MOUNTDEV_QUERY_DEVICE_NAME to partition
  7. now we need handle to MountManager device (\\.\MountPointManager)for send him IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH this IOCTL defined in mountmgr.h in input it require MOUNTDEV_NAMEwhich we get at step 6. on output we receive MOUNTMGR_VOLUME_PATHSstructure (also defined in mountmgr.h) alternatively we can useIOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS - if all ok we got list ofdrive letters like C:, D:, etc..
  8. for enumerate disk drives in system we can useCM_Get_Device_ID_ListW with{4d36e967-e325-11ce-bfc1-08002be10318} filter, open every deviceinstance by CM_Locate_DevNodeW and finally query forDEVPKEY_Device_PDOName by callCM_Get_DevNode_Property

好的,这里的代码示例是正确的:

ok, here the code example correct which do all this:

#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-21 02:30