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; \
}
PnpMgr分析





