windowkerneldriverwindows-kernel

Unable to get all hard disks information with kernel mode driver on windows 11


I am fairly new to kernel programming and I have a little problem getting all disk drives information like name,serialnumber from kernel mode. I use below code to get all disks symbolic links which works perfectly fine.

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

However when i try to use InitializeObjectAttributes function to extract data of symbolic link inside for loop I checking their names with KdPrint and all them are null as a result i can't use ZwOpenSymbolicLinkObject, because when i use it i get BSOD. What am I doing wrong? Is my method valid to get disk information or I should use another method? Below is the code of 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;
}

Thank you very much for helping.


Solution

  • There are multiple problems in your functions. Let start with he main one:

    In SymbolicLinkTarget():

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

    You are going to initialize ObjectAtiribute from SymbolicLinkStr (and the other parameters) but in DeviceInterfaceTest_Func() you actually never set Input to contain a string!

        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

    This is wrong:

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

    Input.Length will be set to the size of a pointer. According to the UNICODE_STRING (ntdef.h; subauth.h) the length is:

    Specifies the length, in bytes, of the string pointed to by the Buffer member, not including the terminating NULL character, if any.

    So:

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

    Notice the wcslen() is already in the init-statement of the for loop, I would train to extract it to have it in the loop body.

    Input.MaximumLength

    Input.MaximumLength = 200 * sizeof(WCHAR);
    

    What if the string is more lager than 200 characters?

    MaximumLength is defined as such:

    Specifies the total size, in bytes, of memory allocated for Buffer. Up to MaximumLength bytes may be written into the buffer without trampling memory.

    Thus it's safe to just do:

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

    The allocation for the Buffer member can be kept in place. Now you need to copy the string into the buffer.

    UNICODE_STRING init

    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
    

    Now that you know how to do it manually, throw your code and use RtlUnicodeStringInit

    Other things & hints

    KdPrint(("Object name:%s \n", ObjectAtiribute.ObjectName - > Buffer));
    

    It's %S (wide char) not %s (char); see format specification. you can pass a UNICODE_STRING and use the %Z formatter. Also be wary of - > which is strange (you probably meant ->).

    InitializeObjectAttributes( & ObjectAtiribute, SymbolicLinkStr, OBJ_CASE_INSENSITIVE, 0, 0);
    

    This is also required when you call ZwOpenSymbolicLinkObject and you are not running in a system thread:

    If the caller is not running in a system thread context, it must set the OBJ_KERNEL_HANDLE attribute when it calls InitializeObjectAttributes.