Enumerating Disk Drives

I am having trouble looping through the hard drives located on a system in order to view their STORAGE_DEVICE_DESCRIPTOR. So far my code correctly works with the first device but it crashes on status = IoCallDriver(currentDeviceObject, irp) during the second iteration and I can't figure out what is going on. I am verifying that everything is non NULL and that the NextDevice is also non null yet I am still getting this BSOD.

static NTSTATUS EnumeratePhysicalDisks() {
    NTSTATUS status;
    UNICODE_STRING diskClassString;
    OBJECT_ATTRIBUTES attributes;
    HANDLE diskClassHandle = NULL;
    PFILE_OBJECT diskClassFileObject = NULL;
    PDEVICE_OBJECT diskClassDeviceObject = NULL;
    PDEVICE_OBJECT currentDeviceObject = NULL;
    IO_STATUS_BLOCK ioStatus; 

    RtlInitUnicodeString(&diskClassString, L"\\Device\\Harddisk0\\DR0");
    InitializeObjectAttributes(&attributes, &diskClassString, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
    DebugMessage("Starting EnumeratePhysicalDisks 1 \n ");
    
    status = ZwOpenFile(&diskClassHandle, SYNCHRONIZE | FILE_READ_DATA, &attributes, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE);
    if (!NT_SUCCESS(status)) {
        DebugMessage("EnumeratePhysicalDisks Fail 1 \n");
        return status;
    }
    DebugMessage("Starting EnumeratePhysicalDisks 2 ValidHandle:%d \n", (diskClassHandle == NULL));

    status = ObReferenceObjectByHandle(diskClassHandle, 0, *IoFileObjectType, KernelMode, (PVOID*)&diskClassFileObject, NULL);
    if (!NT_SUCCESS(status)) {
        DebugMessage("EnumeratePhysicalDisks Fail 2 \n");
        ZwClose(diskClassHandle);
        return status;
    }
    DebugMessage("Starting EnumeratePhysicalDisks 3 \n");
 
    diskClassDeviceObject = IoGetRelatedDeviceObject(diskClassFileObject);
    if (!diskClassDeviceObject) {
        DebugMessage("EnumeratePhysicalDisks Fail 3 \n");
        ObDereferenceObject(diskClassFileObject);
        ZwClose(diskClassHandle);
        return STATUS_UNSUCCESSFUL;
    }
    DebugMessage("Starting EnumeratePhysicalDisks 4 Type:%d Size:%d RefCount:%li DOnull:%d NDnull:%d ADnull:%d CInull:%d \n", diskClassDeviceObject->Type, 
        diskClassDeviceObject->Size, diskClassDeviceObject->ReferenceCount, (diskClassDeviceObject->DriverObject == NULL),
        (diskClassDeviceObject->NextDevice == NULL), (diskClassDeviceObject->AttachedDevice == NULL), (diskClassDeviceObject->CurrentIrp == NULL));

    for (currentDeviceObject = diskClassDeviceObject; currentDeviceObject != NULL; currentDeviceObject = currentDeviceObject->NextDevice) {
        ULONG outputLength = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512; // Extra space for strings
        PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = NULL;
        PSTORAGE_PROPERTY_QUERY query = NULL;
        IO_STATUS_BLOCK ioStat;
        KEVENT event;
        PIRP irp;
        DebugMessage("Starting EnumeratePhysicalDisks 5 \n");

        DebugMessage("Type:%d Size:%d RefCount:%li DOnull:%d NDnull:%d ADnull:%d CInull:%d \n", 
            currentDeviceObject->Type, currentDeviceObject->Size, currentDeviceObject->ReferenceCount, (currentDeviceObject->DriverObject == NULL), 
            (currentDeviceObject->NextDevice == NULL), (currentDeviceObject->AttachedDevice == NULL), (currentDeviceObject->CurrentIrp == NULL));

        query = (PSTORAGE_PROPERTY_QUERY)ExAllocatePoolWithTag(NonPagedPool, sizeof(STORAGE_PROPERTY_QUERY), 'qTag');
        if (!query) {
            DebugMessage("EnumeratePhysicalDisks Fail 5 \n");
            status = STATUS_INSUFFICIENT_RESOURCES;
            break; 
        }
        DebugMessage("Starting EnumeratePhysicalDisks 6 \n");
        RtlZeroMemory(query, sizeof(STORAGE_PROPERTY_QUERY));
        query->PropertyId = StorageDeviceProperty;
        query->QueryType = PropertyStandardQuery;

        deviceDescriptor = (PSTORAGE_DEVICE_DESCRIPTOR)ExAllocatePoolWithTag(NonPagedPool, outputLength, 'dTag');
        if (!deviceDescriptor) {
            DebugMessage("EnumeratePhysicalDisks Fail 6 \n");
            status = STATUS_INSUFFICIENT_RESOURCES;
            ExFreePoolWithTag(query, 'qTag');
            break;
        }
        DebugMessage("Starting EnumeratePhysicalDisks 7 \n");
        KeInitializeEvent(&event, NotificationEvent, FALSE);

        irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_QUERY_PROPERTY, currentDeviceObject, query, sizeof(STORAGE_PROPERTY_QUERY), deviceDescriptor, outputLength, FALSE, &event, &ioStat);
        if (!irp || !deviceDescriptor || !currentDeviceObject) {
            DebugMessage("outputLength:%lu EnumeratePhysicalDisks Fail 7 \n", outputLength);
            ExFreePoolWithTag(deviceDescriptor, 'dTag');
            ExFreePoolWithTag(query, 'qTag');
            status = STATUS_INSUFFICIENT_RESOURCES;
            break; 
        }
        DebugMessage("outputLength:%lu IrpType:%d IrpSize:%d StackCount:%d \n", outputLength, irp->Type, irp->Size, irp->StackCount);

        status = IoCallDriver(currentDeviceObject, irp);
        DebugMessage("Starting EnumeratePhysicalDisks 8.5 \n");
        if (status == STATUS_PENDING) {
            DebugMessage("EnumeratePhysicalDisks Fail 8 \n");
            KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
            status = ioStatus.Status;
        }
        DebugMessage("Starting EnumeratePhysicalDisks 8.9 \n");

        if (NT_SUCCESS(status)) { 
            DebugMessage("Starting EnumeratePhysicalDisks 9 SerialNumberOffset:%lu \n", deviceDescriptor->SerialNumberOffset);
        }
        else  DebugMessage("EnumeratePhysicalDisks FAIL 9 \n");

    Clean:
        // Cleanup for the next iteration or exit
        if (query) {
            ExFreePoolWithTag(query, 'qTag');
            query = NULL;
        }
        if (deviceDescriptor) {
            ExFreePoolWithTag(deviceDescriptor, 'dTag');
            deviceDescriptor = NULL;
        }
    }

    // Cleanup
    if (diskClassFileObject) {
        ObDereferenceObject(diskClassFileObject);
    }
    if (diskClassHandle) {
        ZwClose(diskClassHandle);
    }
    return STATUS_SUCCESS;
}

Now here are my debug messages and the BSOD crash report which might be inaccurate since it did not have my pdb file but either way my code is correct and the location of the crash is easily visible.

Starting EnumeratePhysicalDisks 1 
 Starting EnumeratePhysicalDisks 2 ValidHandle:0 
 Starting EnumeratePhysicalDisks 3 
 Starting EnumeratePhysicalDisks 4 Type:3 Size:424 RefCount:0 DOnull:0 NDnull:0 ADnull:1 CInull:1 
Starting EnumeratePhysicalDisks 5 
 Type:3 Size:424 RefCount:0 DOnull:0 NDnull:0 ADnull:1 CInull:1 
Starting EnumeratePhysicalDisks 6 
 Starting EnumeratePhysicalDisks 7 
 outputLength:552 IrpType:6 IrpSize:712 StackCount:5 
Starting EnumeratePhysicalDisks 8.5 
Starting EnumeratePhysicalDisks 8.9 
Starting EnumeratePhysicalDisks 9 SerialNumberOffset:139 
Starting EnumeratePhysicalDisks 5 
 Type:3 Size:424 RefCount:0 DOnull:0 NDnull:0 ADnull:1 CInull:1 
Starting EnumeratePhysicalDisks 6 
 Starting EnumeratePhysicalDisks 7 
 outputLength:552 IrpType:6 IrpSize:784 StackCount:6 

KDTARGET: Refreshing KD connection

*** Fatal System Error: 0x0000009b
                       (0x0000000000140327,0xFFFFB58DAEC4BA28,0xFFFFB58DAEC4B260,0xFFFFF805ABD6B710)

Break instruction exception - code 80000003 (first chance)

A fatal system error has occurred.
Debugger entered on first try; Bugcheck callbacks have not been invoked.

A fatal system error has occurred.

For analysis of this file, run !analyze -v
nt!DbgBreakPointWithStatus:
fffff805`05c1f150 cc              int     3
kd> !analyze -v
Connected to Windows 10 19041 x64 target at (Thu Apr  4 10:35:04.894 2024 (UTC - 7:00)), ptr64 TRUE
Loading Kernel Symbols
...............................................................
................................................................
................................................................

Loading User Symbols
................................................................
..........
Loading unloaded module list
......
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

UDFS_FILE_SYSTEM (9b)
    If you see UdfExceptionFilter on the stack then the 2nd and 3rd
    parameters are the exception record and context record. Do a .cxr
    on the 3rd parameter and then kb to obtain a more helpful stack
    trace.
Arguments:
Arg1: 0000000000140327
Arg2: ffffb58daec4ba28
Arg3: ffffb58daec4b260
Arg4: fffff805abd6b710

Debugging Details:
------------------


KEY_VALUES_STRING: 1

    Key  : AV.Dereference
    Value: NullClassPtr

    Key  : AV.Fault
    Value: Read

    Key  : Analysis.CPU.Sec
    Value: 3

    Key  : Analysis.DebugAnalysisProvider.CPP
    Value: Create: 8007007e on DESKTOP-SBQN4VR

    Key  : Analysis.DebugData
    Value: CreateObject

    Key  : Analysis.DebugModel
    Value: CreateObject

    Key  : Analysis.Elapsed.Sec
    Value: 4

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 105

    Key  : Analysis.System
    Value: CreateObject


BUGCHECK_CODE:  9b

BUGCHECK_P1: 140327

BUGCHECK_P2: ffffb58daec4ba28

BUGCHECK_P3: ffffb58daec4b260

BUGCHECK_P4: fffff805abd6b710

EXCEPTION_RECORD:  ffffb58daec4ba28 -- (.exr 0xffffb58daec4ba28)
ExceptionAddress: fffff805abd6b710 (udfs!UdfDecodeFileObject)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 0000000000000000
   Parameter[1]: 0000000000000020
Attempt to read from address 0000000000000020

CONTEXT:  ffffb58daec4b260 -- (.cxr 0xffffb58daec4b260)
rax=ffffb58daec4bcd8 rbx=ffffb68dd57d3a10 rcx=0000000000000000
rdx=ffffb58daec4bcf0 rsi=ffffb68dda8decf0 rdi=ffffb68dda8decf0
rip=fffff805abd6b710 rsp=ffffb58daec4bc68 rbp=0000000000000000
 r8=ffffb58daec4bce8  r9=ffffb58daec4bce0 r10=0000000040004412
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=ffffb68dd56b91d0 r15=ffffb68dda8def28
iopl=0         nv up ei pl zr na po nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00050246
udfs!UdfDecodeFileObject:
fffff805`abd6b710 8b4120          mov     eax,dword ptr [rcx+20h] ds:002b:00000000`00000020=????????
Resetting default scope

PROCESS_NAME:  MyApplication.exe

READ_ADDRESS:  0000000000000020 

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  0000000000000000

EXCEPTION_PARAMETER2:  0000000000000020

EXCEPTION_STR:  0xc0000005

This is not how you would enumerate disks...You need to use IoGetDeviceInterfaces and IoRegisterPlugPlayNotification.

Do you have a link or some other info regarding how to enumerate disks and query attributes correctly? Because the method above does seem to work for finding the first disk device it just fails on the second.

Windows-driver-samples/general/toaster/toastDrv/kmdf/toastmon/toastmon.c at main · microsoft/Windows-driver-samples · GitHub

But you want disks not toasters so you specify GUID_DEVINTERFACE_DISK.