How to get from device-manager device (e.g. from i

2019-09-15 16:32发布

问题:

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

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:, ...).

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


(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})

回答1:

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

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

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)

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

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

  1. open the device - if use ZwOpenFile (of course this worked in user mode) we can use \Device\0000010f as is. if we want use win32 api 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 that DeviceType == FILE_DEVICE_DISK && sdn.PartitionNumber == 0
  3. send IOCTL_DISK_GET_DRIVE_LAYOUT_EX for get a variable-sized array of PARTITION_INFORMATION_EX structures
  4. based on DeviceNumber (which we get at step 2) and PartitionNumber (which we get at step 3) format symbolic link to partition PDO - \\?\GLOBALROOT\Device\Harddisk%d\Partition%d
  5. open partition PDO with SYNCHRONIZE access (enough because all IOCTL 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_NAME which we get at step 6. on output we receive MOUNTMGR_VOLUME_PATHS structure (also defined in mountmgr.h) alternatively we can use IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS - if all ok we got list of drive letters like C:, D:, etc..
  8. for enumerate disk drives in system we can use CM_Get_Device_ID_ListW with {4d36e967-e325-11ce-bfc1-08002be10318} filter, open every device instance by CM_Locate_DevNodeW and finally query for DEVPKEY_Device_PDOName by call CM_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);
    }
}


回答2:

Another possible solution for you, based on PowerShell and WMI alone:

$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"
}


标签: winapi wmi