Windows 11内核模式驱动无法获取全部硬盘信息求助
Windows内核模式获取磁盘信息:符号链接处理错误分析与修复
我是内核编程新手,在Windows 11内核模式下尝试获取硬盘名称、序列号等信息时遇到问题。目前用IoGetDeviceInterfaces获取磁盘符号链接的代码能正常运行,但在循环内调用自定义的SymbolicLinkTarget函数解析符号链接时,出现名称为空、调用ZwOpenSymbolicLinkObject导致BSOD的问题。
当前可用的符号链接获取代码
static VOID DeviceInterfaceTest_Func() { NTSTATUS Status; PWSTR SymbolicLinkList; PWSTR SymbolicLinkListPtr; GUID Guid = { 0x53F5630D, 0xB6BF, 0x11D0, { 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B } }; //Defined in mountmgr.h Status = IoGetDeviceInterfaces( & Guid, NULL, 0, & SymbolicLinkList); if (!NT_SUCCESS(Status)) { return; } KdPrint(("IoGetDeviceInterfaces results:\n")); for (SymbolicLinkListPtr = SymbolicLinkList; SymbolicLinkListPtr[0] != 0 && SymbolicLinkListPtr[1] != 0; SymbolicLinkListPtr += wcslen(SymbolicLinkListPtr) + 1) { KdPrint(("Symbolic Link: %S\n", SymbolicLinkListPtr)); PUNICODE_STRING PTarget {}; UNICODE_STRING Input; NTSTATUS s = 0; Input.Length = sizeof((PWSTR) & SymbolicLinkListPtr); Input.MaximumLength = 200 * sizeof(WCHAR); Input.Buffer = (PWSTR) ExAllocatePool2(PagedPool, Input.MaximumLength, 0); s = SymbolicLinkTarget( & Input, PTarget); if (s == STATUS_SUCCESS) { //KdPrint(("%S\n", PTarget->Buffer)); KdPrint(("Finished!\n")); } } ExFreePool(SymbolicLinkList); }
出现问题的SymbolicLinkTarget函数代码
NTSTATUS SymbolicLinkTarget(_In_ PUNICODE_STRING SymbolicLinkStr, _Out_ PUNICODE_STRING PTarget) { OBJECT_ATTRIBUTES ObjectAtiribute {}; NTSTATUS Status = 0; HANDLE Handle = nullptr; InitializeObjectAttributes( & ObjectAtiribute, SymbolicLinkStr, OBJ_CASE_INSENSITIVE, 0, 0); KdPrint(("Object length:%u \n", ObjectAtiribute.Length)); KdPrint(("Object name:%s \n", ObjectAtiribute.ObjectName -> Buffer)); Status = ZwOpenSymbolicLinkObject(&Handle, GENERIC_READ, &ObjectAtiribute); if (Status != STATUS_SUCCESS) { KdPrint(("ZwOpenSymbolicLinkObject failed (0x%08X)\n", Status)); return Status; } UNREFERENCED_PARAMETER(PTarget); ULONG Tag1 = 'Tag1'; PTarget->MaximumLength = 200 * sizeof(WCHAR); PTarget->Length = 0; PTarget->Buffer = (PWCH)ExAllocatePool2(PagedPool, PTarget->MaximumLength, Tag1); if (!PTarget->Buffer) { ZwClose(Handle); return STATUS_INSUFFICIENT_RESOURCES; } Status = ZwQuerySymbolicLinkObject(Handle, PTarget, NULL); ZwClose(Handle); if (Status != STATUS_SUCCESS) { KdPrint(("ZwQuerySymbolicLinkObject failed (0x%08X)\n", Status)); ExFreePool(PTarget->Buffer); return Status; } return STATUS_SUCCESS; }
核心错误点分析
InputUnicodeString初始化错误
循环中仅分配了Input.Buffer,但未将符号链接字符串复制到其中,且Input.Length计算错误(取了指针大小而非字符串字节长度),导致传入SymbolicLinkTarget的是空的Unicode字符串。未初始化的
PTarget指针PUNICODE_STRING PTarget {};定义了一个空指针,直接在SymbolicLinkTarget中访问PTarget->MaximumLength会触发内存访问违规,直接导致BSOD。KdPrint格式化符错误ObjectAtiribute.ObjectName->Buffer是宽字符类型,需用%S格式化符,用%s会打印乱码或空值。多余的
UNREFERENCED_PARAMETER(PTarget)
该语句会屏蔽编译器对空指针的警告,但后续直接使用空指针的操作依然存在,属于严重逻辑错误。
修正后的代码示例
修正后的DeviceInterfaceTest_Func循环部分
for (SymbolicLinkListPtr = SymbolicLinkList; SymbolicLinkListPtr[0] != 0 && SymbolicLinkListPtr[1] != 0; SymbolicLinkListPtr += wcslen(SymbolicLinkListPtr) + 1) { KdPrint(("Symbolic Link: %S\n", SymbolicLinkListPtr)); UNICODE_STRING Target = {0}; // 在栈上分配合法的UNICODE_STRING结构体 UNICODE_STRING Input; NTSTATUS s = 0; Input.MaximumLength = 200 * sizeof(WCHAR); Input.Buffer = (PWSTR)ExAllocatePool2(PagedPool, Input.MaximumLength, 'DevL'); // 使用有意义的池标签 if (!Input.Buffer) { continue; } // 复制符号链接字符串到Input并设置正确长度 wcscpy_s(Input.Buffer, Input.MaximumLength / sizeof(WCHAR), SymbolicLinkListPtr); Input.Length = wcslen(SymbolicLinkListPtr) * sizeof(WCHAR); s = SymbolicLinkTarget(&Input, &Target); if (s == STATUS_SUCCESS) { KdPrint(("Target Path: %S\n", Target.Buffer)); ExFreePool(Target.Buffer); // 释放Target的缓冲区 KdPrint(("Finished!\n")); } ExFreePool(Input.Buffer); // 释放Input的缓冲区 }
修正后的SymbolicLinkTarget函数
NTSTATUS SymbolicLinkTarget(_In_ PUNICODE_STRING SymbolicLinkStr, _Out_ PUNICODE_STRING PTarget) { OBJECT_ATTRIBUTES ObjectAttributes = {0}; NTSTATUS Status = STATUS_SUCCESS; HANDLE Handle = nullptr; // 先校验输入参数合法性 if (!SymbolicLinkStr || !SymbolicLinkStr->Buffer || !PTarget) { return STATUS_INVALID_PARAMETER; } InitializeObjectAttributes(&ObjectAttributes, SymbolicLinkStr, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); KdPrint(("Object length:%u \n", ObjectAttributes.Length)); KdPrint(("Object name:%S \n", ObjectAttributes.ObjectName->Buffer)); Status = ZwOpenSymbolicLinkObject(&Handle, GENERIC_READ, &ObjectAttributes); if (!NT_SUCCESS(Status)) { KdPrint(("ZwOpenSymbolicLinkObject failed (0x%08X)\n", Status)); return Status; } ULONG Tag1 = 'Tag1'; PTarget->MaximumLength = 200 * sizeof(WCHAR); PTarget->Length = 0; PTarget->Buffer = (PWCH)ExAllocatePool2(PagedPool, PTarget->MaximumLength, Tag1); if (!PTarget->Buffer) { ZwClose(Handle); return STATUS_INSUFFICIENT_RESOURCES; } ULONG ReturnedLength = 0; Status = ZwQuerySymbolicLinkObject(Handle, PTarget, &ReturnedLength); ZwClose(Handle); if (!NT_SUCCESS(Status)) { KdPrint(("ZwQuerySymbolicLinkObject failed (0x%08X)\n", Status)); ExFreePool(PTarget->Buffer); PTarget->Buffer = nullptr; PTarget->Length = PTarget->MaximumLength = 0; return Status; } return STATUS_SUCCESS; }
替代方案:直接获取磁盘属性
如果目标是获取硬盘名称、序列号等信息,解析符号链接并非最优路径,更可靠的方式是:
- 通过
IoGetDeviceInterfaces获取设备接口路径后,直接用ZwCreateFile打开设备 - 发送
IOCTL_STORAGE_QUERY_PROPERTY控制码查询存储设备属性(含序列号、友好名称) - 发送
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX获取磁盘几何信息
这种方式无需解析符号链接,直接与磁盘设备通信,逻辑更简洁可靠。
内容的提问来源于stack exchange,提问作者user19819208
相关产品推荐
相关产品推荐

