无法使用 Windows 11 上的内核模式驱动程序获取所有硬盘信息



我对内核编程相当陌生,从内核模式获取所有磁盘驱动器信息(如名称,序列号)时遇到了一点问题。我使用以下代码来获取所有磁盘符号链接,效果非常好。

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: %Sn", 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(("%Sn", PTarget->Buffer));
KdPrint(("Finished!n"));
}
}
ExFreePool(SymbolicLinkList);
}

但是,当我尝试使用初始化对象属性函数提取循环内部符号链接的数据时,我使用 KdPrint 检查它们的名称,因此它们都是空的,因此我无法使用 ZwOpenSymbolicLinkObject,因为当我使用它时,我会得到蓝屏。我做错了什么?我的方法是否有效获取磁盘信息,还是应该使用其他方法?下面是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;
}

非常感谢您的帮助。

您的函数中存在多个问题。让我们从他的主要一个开始:

SymbolicLinkTarget()

OBJECT_ATTRIBUTES ObjectAtiribute {};
InitializeObjectAttributes( & ObjectAtiribute, SymbolicLinkStr, OBJ_CASE_INSENSITIVE, 0, 0);

您将从SymbolicLinkStr(和其他参数)初始化ObjectAtiribute,但实际上DeviceInterfaceTest_Func()您实际上从未将Input设置为包含字符串!

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);

输入长度

这是错误的:

Input.Length = sizeof((PWSTR) & SymbolicLinkListPtr);

Input.Length将设置为指针的大小。根据UNICODE_STRING(ntdef.h; subauth.h),length是:

指定 Buffer 成员指向的字符串的长度(以字节为单位),不包括终止 NULL 字符(如果有)。

所以:

size_t str_len_no_null = wcslen(SymbolicLinkListPtr); // number of chars, not bytes!
Input.Length = str_len_no_null * sizeof(WCHAR);

请注意,wcslen()已经在 for 循环的 init 语句中,我会训练它提取它以将其放在循环体中。

输入最大长度

Input.MaximumLength = 200 * sizeof(WCHAR);

如果字符串超过 200 个字符怎么办?

MaximumLength的定义如下:

指定分配给缓冲区的内存的总大小(以字节为单位)。最多可以将最大长度字节写入缓冲区,而不会占用内存。

因此,只做以下操作是安全的:

size_t max_length_bytes = Input.Length + (1 * sizeof(WCHAR)); // add room for possible null.
Input.MaximumLength = max_length_bytes; 

Buffer成员的分配可以保持不变。现在,您需要将字符串复制到缓冲区中。

UNICODE_STRING初始化

size_t str_len_no_null = wcslen(SymbolicLinkListPtr); // number of chars, not bytes!
Input.Length = str_len_no_null * sizeof(WCHAR);
size_t max_length_bytes = Input.Length + (1 * sizeof(WCHAR)); // add room for possible null.
Input.MaximumLength = max_length_bytes; 
Input.Buffer = (PWSTR) ExAllocatePool2(PagedPool, Input.MaximumLength, 0); // note: you should define a Tag for your Driver.
if(Input.buffer == NULL) {
// not enough memory.
return;
}
status = RtlStringCbCopyW(Input.Buffer, max_length_bytes, SymbolicLinkListPtr);
// TODO: check status

现在您知道如何手动执行此操作,请抛出代码并使用RtlUnicodeStringInit

其他内容和提示

  • 始终检查您使用的函数的返回状态/值。在内核模式下,这非常重要。
  • NTSTATUS检查始终使用状态宏之一(通常NT_SUCCESS)
  • 使用字符串安全函数。
  • 吹毛求疵:成功返回值IoGetDeviceInterfaces也可能表示缓冲区为空。尽管您在 for 循环 init 语句中检查了这一点,但我会在函数之后立即检查它,以便意图更清晰。
KdPrint(("Object name:%s n", ObjectAtiribute.ObjectName - > Buffer));

它是%S(宽字符)而不是%s(字符);请参阅格式规范。 您可以传递UNICODE_STRING并使用%Z格式化程序。还要警惕- >这很奇怪(你可能的意思是->)。

InitializeObjectAttributes( & ObjectAtiribute, SymbolicLinkStr, OBJ_CASE_INSENSITIVE, 0, 0);
  • 如果生成的句柄不打算跨越内核<>用户模式边界(在您的情况下,它不必跨越该边界),请使用OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE。否则,会将内核句柄泄漏到用户模式,这会产生安全隐患。

当您调用ZwOpenSymbolicLinkObject并且未在系统线程中运行时,这也是必需的:

如果调用方未在系统线程上下文中运行,则必须在调用 InitializeObjectAttributes 时设置 OBJ_KERNEL_HANDLE 属性。

  • 可以使用DEFINE_GUID定义 GUID ;请参阅定义和导出新的 GUID 和在驱动程序代码中包含 GUID。在您的情况下,您不需要导出它。

  • 这可能是吹毛求疵,但使用nullptr(c++) 或NULL(c) 而不是 0 来传达您正在检查指针而不仅仅是整数值 0 的想法。

相关内容

  • 没有找到相关文章

最新更新