PnpMgr分析
+ -

Windows设备实例路径生成规则

2023-08-31 264 0

PNP管理器通过PipMakeGloballyUniqueId来生成设备的实例路径。
默认情况下,设备的实例路径由系统按一定的规则生成,但是也有例外,即可以通过总线驱动来指定设备的实例路径。
设备实例路径生成规则是选择由按系统规则还是由驱动指定,取决于总线驱动返回的PDO设备的DeviceCapability中的UniqueID字段。微软的文档也有说明:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/install/instance-ids

如果UniqueID为TRUE,则由总线驱动总提供的设备 ID 和 实例 ID 构成的设备实例 ID 将唯一标识系统中的设备。
如果为 FALSE,则设备的总线提供的 实例 ID 仅对设备的总线是唯一的。如:

USB\VID_0295&PID_00C9&MI_00\9&86ffdff&13&0000

其中USB\VID_0295&PID_00C9&MI_00是硬件ID
9&86ffdff&13&0000中的9是设备总线树深度,86ffdff是父设备实例路径的Hash值,在父设备的ParentIdPrefix
最后的0000是通过GetInstanceId返回的设备实例ID.

 NextParentId.<level>.<hash>:REG_DWORD: <NextInstance>

相关的代码见:

   globallyUnique = FALSE;
    if (NT_SUCCESS(status)) {

        if (capabilities.NoDisplayInUI) {

            DeviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI;
        }
        if (capabilities.UniqueID) {

            globallyUnique = TRUE;
        }
    }

源码生成见:

NTSTATUS
PipMakeGloballyUniqueId(
    IN PDEVICE_OBJECT   DeviceObject,
    IN PWCHAR           UniqueId,
    OUT PWCHAR         *GloballyUniqueId
    )
{
    NTSTATUS status;
    ULONG length;
    PWSTR id, Prefix = NULL;
    HANDLE enumKey;
    HANDLE instanceKey;
    UCHAR keyBuffer[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) + sizeof(ULONG)];
    PKEY_VALUE_PARTIAL_INFORMATION keyValue, stringValueBuffer = NULL;
    UNICODE_STRING valueName;
    ULONG uniqueIdValue, Hash, hashInstance;
    PDEVICE_NODE parentNode;

    PAGED_CODE();

    id = NULL;

    //
    // We need to build an instance id to uniquely identify this
    // device.  We will accomplish this by producing a prefix that will be
    // prepended to the non-unique device id supplied.
    //

    //
    // To 'unique-ify' the child's instance ID, we will retrieve
    // the unique "UniqueParentID" number that has been assigned
    // to the parent and use it to construct a prefix.  This is
    // the legacy mechanism supported here so that existing device
    // settings are not lost on upgrade.
    //

    PiLockPnpRegistry(FALSE);

    parentNode = ((PDEVICE_NODE)DeviceObject->DeviceObjectExtension->DeviceNode)->Parent;

    status = IopOpenRegistryKeyEx( &enumKey,
                                   NULL,
                                   &CmRegistryMachineSystemCurrentControlSetEnumName,
                                   KEY_READ | KEY_WRITE
                                   );

    if (!NT_SUCCESS(status)) {
        IopDbgPrint((   IOP_ENUMERATION_ERROR_LEVEL,
                        "PipMakeGloballyUniqueId:\tUnable to open HKLM\\SYSTEM\\CCS\\ENUM (status %08lx)\n",
                        status));
        goto clean0;
    }

    //
    // Open the instance key for this devnode
    //
    status = IopOpenRegistryKeyEx( &instanceKey,
                                   enumKey,
                                   &parentNode->InstancePath,
                                   KEY_READ | KEY_WRITE
                                   );

    if (!NT_SUCCESS(status)) {

        IopDbgPrint((   IOP_ENUMERATION_ERROR_LEVEL,
                        "PipMakeGloballyUniqueId:\tUnable to open registry key for %wZ (status %08lx)\n",
                        &parentNode->InstancePath,
                        status));
        goto clean1;
    }

    //
    // Attempt to retrieve the "UniqueParentID" value from the device
    // instance key.
    //
    keyValue = (PKEY_VALUE_PARTIAL_INFORMATION)keyBuffer;
    PiWstrToUnicodeString(&valueName, REGSTR_VALUE_UNIQUE_PARENT_ID);

    status = ZwQueryValueKey(instanceKey,
                             &valueName,
                             KeyValuePartialInformation,
                             keyValue,
                             sizeof(keyBuffer),
                             &length
                             );

    if (NT_SUCCESS(status)) {
        ASSERT(keyValue->Type == REG_DWORD);
        ASSERT(keyValue->DataLength == sizeof(ULONG));
        if ((keyValue->Type != REG_DWORD) ||
            (keyValue->DataLength != sizeof(ULONG))) {
            status = STATUS_INVALID_PARAMETER;
            goto clean2;
        }

        uniqueIdValue = *(PULONG)(keyValue->Data);

        //
        // OK, we have a unique parent ID number to prefix to the
        // instance ID.
        Prefix = (PWSTR)ExAllocatePool(PagedPool, 9 * sizeof(WCHAR));
        if (!Prefix) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            goto clean2;
        }
        swprintf(Prefix, L"%x", uniqueIdValue);
    } else {

        //
        // This is the current mechanism for finding existing
        // device instance prefixes and calculating new ones if
        // required.
        //

        //
        // Attempt to retrieve the "ParentIdPrefix" value from the device
        // instance key.
        //

        PiWstrToUnicodeString(&valueName, REGSTR_VALUE_PARENT_ID_PREFIX);
        length = (MAX_PARENT_PREFIX + 1) * sizeof(WCHAR) +
            FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
        stringValueBuffer = ExAllocatePool(PagedPool,
                                           length);
        if (stringValueBuffer) {
            status = ZwQueryValueKey(instanceKey,
                                     &valueName,
                                     KeyValuePartialInformation,
                                     stringValueBuffer,
                                     length,
                                     &length);
        }
        else {
            status = STATUS_INSUFFICIENT_RESOURCES;
            goto clean2;
        }

        if (NT_SUCCESS(status)) {

            ASSERT(stringValueBuffer->Type == REG_SZ);
            if (stringValueBuffer->Type != REG_SZ) {
                status = STATUS_INVALID_PARAMETER;
                goto clean2;
            }

            //
            // Parent has already been assigned a "ParentIdPrefix".
            //

            Prefix = (PWSTR) ExAllocatePool(PagedPool,
                                            stringValueBuffer->DataLength);
            if (!Prefix)
            {
                status = STATUS_INSUFFICIENT_RESOURCES;
                goto clean2;
            }
            wcscpy(Prefix, (PWSTR) stringValueBuffer->Data);
        }
        else
        {
            //
            // Parent has not been assigned a "ParentIdPrefix".
            // Compute the prefix:
            //    * Compute Hash
            //    * Look for value of the form:
            //        NextParentId.<level>.<hash>:REG_DWORD: <NextInstance>
            //      under CCS\Enum.  If not present, create it.
            //    * Assign the new "ParentIdPrefix" which will be of
            //      of the form:
            //        <level>&<hash>&<instance>
            //

            // Allocate a buffer once for the NextParentId... value
            // and for the prefix.
            length = (ULONG)(max(wcslen(REGSTR_VALUE_NEXT_PARENT_ID) + 2 + 8 + 8,
                         MAX_PARENT_PREFIX) + 1);

            // Device instances are case in-sensitive.  Upcase before
            // performing hash to ensure that the hash is case-insensitve.
            status = RtlUpcaseUnicodeString(&valueName,
                                            &parentNode->InstancePath,
                                            TRUE);
            if (!NT_SUCCESS(status))
            {
                goto clean2;
            }
            HASH_UNICODE_STRING(&valueName, &Hash);
            RtlFreeUnicodeString(&valueName);

            Prefix = (PWSTR) ExAllocatePool(PagedPool,
                                            length * sizeof(WCHAR));
            if (!Prefix) {
                status = STATUS_INSUFFICIENT_RESOURCES;
                goto clean2;
            }

            // Check for existence of "NextParentId...." value and update.
            swprintf(Prefix, L"%s.%x.%x", REGSTR_VALUE_NEXT_PARENT_ID,
                     Hash, parentNode->Level);
            RtlInitUnicodeString(&valueName, Prefix);
            keyValue = (PKEY_VALUE_PARTIAL_INFORMATION)keyBuffer;
            status = ZwQueryValueKey(enumKey,
                                     &valueName,
                                     KeyValuePartialInformation,
                                     keyValue,
                                     sizeof(keyBuffer),
                                     &length
                                     );
            if (NT_SUCCESS(status) && (keyValue->Type == REG_DWORD) &&
                (keyValue->DataLength == sizeof(ULONG))) {
                hashInstance = *(PULONG)(keyValue->Data);
            }
            else {
                hashInstance = 0;
            }

            hashInstance++;

            status = ZwSetValueKey(enumKey,
                                   &valueName,
                                   TITLE_INDEX_VALUE,
                                   REG_DWORD,
                                   &hashInstance,
                                   sizeof(hashInstance)
                                   );

            if (!NT_SUCCESS(status)) {
                goto clean2;
            }

            hashInstance--;

            // Create actual ParentIdPrefix string
            PiWstrToUnicodeString(&valueName, REGSTR_VALUE_PARENT_ID_PREFIX);
            length = swprintf(Prefix, L"%x&%x&%x", parentNode->Level,
                     Hash, hashInstance) + 1;
            status = ZwSetValueKey(instanceKey,
                                   &valueName,
                                   TITLE_INDEX_VALUE,
                                   REG_SZ,
                                   Prefix,
                                   length * sizeof(WCHAR)
                                   );
            if (!NT_SUCCESS(status))
            {
                goto clean2;
            }
        }
    }

    // Construct the instance id from the non-unique id (if any)
    // provided by the child and the prefix we've constructed.
    length = (ULONG)(wcslen(Prefix) + (UniqueId ? wcslen(UniqueId) : 0) + 2);
    id = (PWSTR)ExAllocatePool(PagedPool, length * sizeof(WCHAR));
    if (!id) {
        status = STATUS_INSUFFICIENT_RESOURCES;
    } else if (UniqueId) {
        swprintf(id, L"%s&%s", Prefix, UniqueId);
    } else {
        wcscpy(id, Prefix);
    }

clean2:
    ZwClose(instanceKey);

clean1:
    ZwClose(enumKey);

clean0:
    PiUnlockPnpRegistry();

    if (stringValueBuffer) {
        ExFreePool(stringValueBuffer);
    }

    if (Prefix) {
        ExFreePool(Prefix);
    }

    *GloballyUniqueId = id;
    return status;
}

HASH的方法如下:

//
// Hash routine from CNTFS (see cntfs\prefxsup.c)
// (used here in the construction of unique ids)
//

#define HASH_UNICODE_STRING( _pustr, _phash ) {                             \
    PWCHAR _p = (_pustr)->Buffer;                                           \
    PWCHAR _ep = _p + ((_pustr)->Length/sizeof(WCHAR));                     \
    ULONG _chHolder =0;                                                     \
                                                                            \
    while( _p < _ep ) {                                                     \
        _chHolder = 37 * _chHolder + (unsigned int) (*_p++);                \
    }                                                                       \
                                                                            \
    *(_phash) = abs(314159269 * _chHolder) % 1000000007;                    \
}

0 篇笔记 写笔记

DevCon ListClass命令
DevCon ListClass用于列出指定设备设置类中的所有设备。 在本地和远程计算机上有效。命令格式devcon [/m:\computer] listclass class [class...]参数如果要在指定的远程机器上运行,使用/m参数,并且目标计算机的路径使用反斜杠””来标识。......
Devcon ListClass命令代码实现
ListClass命令用于列出当前系统中该设备类下的的有设备实例并显示设备的友名。ListClass命令由两部分组成:第一部分是根据设备的类名获取其类GUID,然后再通过SetupDiGetClassDevsEx打开该GUID分类的设备集,获取其 HDEVINFO devs句柄。当然这里也为了显......
DevCon DriverNodes命令
DevCon DriverNodes命令列出与设备兼容的所有驱动程序包及其版本和驱动排名。DevCon DriverNodes 操作仅在本地计算机上运行。DevCon DriverNodes 操作对于排查驱动安装问题特别有用。 例如,可以使用它来确定设备Windows INF 文件或自定义的第三方 ......
Windows设备实例路径生成规则
PNP管理器通过PipMakeGloballyUniqueId来生成设备的实例路径。默认情况下,设备的实例路径由系统按一定的规则生成,但是也有例外,即可以通过总线驱动来指定设备的实例路径。设备实例路径生成规则是选择由按系统规则还是由驱动指定,取决于总线驱动返回的PDO设备的DeviceCapabil......
获取设备的父系节点实例路径CM_Get_Parent
在Windows设备管理器中,每个设备节点都有一个属性Parent父系,表示该设备的父节点的实例路径;我们可以使用如下代码来枚举显示,其原理就是通过CM_Get_Parent获取到父设备的DEVINST,然后再通过CM_Get_Device_ID获取到它的实例路径#include
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!