Bios Device Name from PDO

I am trying catch a BDN (Bios Device Name) from PDO in a driver on Kernel Mode, but alwalys trigger a BSOD after call IoGetDevicePropertyData function, the PDO was created with IoGetDeviceObjectPointer.

Here a snipet from the code

NTSTATUS GetBiosDeviceNameFromPDO(PDEVICE_OBJECT pdo) {
    NTSTATUS status;
    DEVPROPTYPE type = DEVPROP_TYPE_STRING;
    const DEVPROPKEY devPropKey = DEVPKEY_Device_BiosDeviceName;
    PVOID buffer = nullptr;
    ULONG bufferSize = 0;
    ULONG bytesWritten = 0;
    KIRQL irql = KeRaiseIrqlToDpcLevel();
    DWORD size = 8;
    UCHAR* data = (PUCHAR)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(UCHAR) * size, 'ONEL');
    if (IoGetDevicePropertyData(pdo, &devPropKey, LOCALE_NEUTRAL, 0, 8, data, &size, &type) == STATUS_BUFFER_TOO_SMALL) {
        ExFreePool(data);
        UCHAR* data = (PUCHAR)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(UCHAR) * size, 'ONEL');
        if (NTSTATUS(IoGetDevicePropertyData(pdo, &devPropKey, LOCALE_NEUTRAL, 0, 8, data, &size, &type))) {
            if (type == DEVPROP_TYPE_STRING) {
                DbgPrint("BIOS Device Name: %s\n", (char*)buffer);
            }
            else {
                status = STATUS_INVALID_PARAMETER;
            }
        }
        else {
            DbgPrint("Error:\n");
        }
    }
    KeLowerIrql(irql);    

    return status;
}

Is there something left out in the code?

First, if you are asking about a BSOD you should, at the minimum, include what the bugcheck code and bugcheck parameters were. Ideally your should paste the output from the debugger !analyze -v command.

You appear to be calling IoGetDevicePropertyData at DISPATCH_LEVEL, which is prohibited by the docs for IoGetDevicePropertyData.


    KIRQL irql = KeRaiseIrqlToDpcLevel(); // <<<< why are you doing this?
    DWORD size = 8;
    UCHAR* data = (PUCHAR)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(UCHAR) * size, 'ONEL');
    if (IoGetDevicePropertyData(pdo, &devPropKey, LOCALE_NEUTRAL, 0, 8, data, &size, &type) == STATUS_BUFFER_TOO_SMALL) {

In practice, it is virtually NEVER necessary to call KeRaiseIrql. Why are you doing that at all?

The second call to IoGetDevicePropertyData is interesting in that it has at least three obvious bugs:

  1. An error return will satisfy the if condition, STATUS_SUCCESS will branch to the else statement.
  2. You hardcoded the data size to 8, instead of using the value of ‘size’, guaranteeing failure if 8 is not large enough.
  3. You preset the output ‘type’ parameter to the value you expect, consequently you are going to pass the test for this value after ignoring the error return from the second call, due to defect (1).

I added this out of sheer desperation, haha

after unsuccessful attempts without this level change

Error in debug

*** Fatal System Error: 0x000000ca
(0x0000000000000002,0xFFFFDC0704FEDDE0,0x0000000000000000,0x0000000000000000)

Did you look up the bugcheck? Along with all the code defects already noted, you are not passing the address of a PDO as required.

Parameter 1 has value '2' which indicates:

Blockquote
Invalid PDO: An API which requires a PDO has been called with random memory, or with an FDO, or with a PDO which hasn't been initialized.

I have this function here

NTSTATUS GetPDOFromPDName(PUNICODE_STRING pPDOName) {
    NTSTATUS status;
    PFILE_OBJECT fileObject = NULL;
    PDEVICE_OBJECT pdo;

    status = IoGetDeviceObjectPointer(pPDOName, FILE_READ_ACCESS, &fileObject, &pdo);

    if (NT_SUCCESS(status) && pdo) {
        DbgPrint("Successfully obtained PDO: %p\n", pdo);        
        ObDereferenceObject(fileObject);
        GetBiosDeviceNameFromPDO(pdo);
    }
    else {
        DbgPrint("Failed to obtain PDO, status: 0x%x\n", status);
    }
    return status;
}

and then using the PDO calling GetBiosDeviceNameFromPDO, this way the PDO will be NULL?

You don't have a PDO. There is a device stack with an FDO layered on top of the PDO (with any number of filter DOs layered between the PDO and FDO and also above the FDO. You get a pointer to the device object that happens to be on the top of the stack of device objects.

and this would all be easier in WDF, but you can use IoGetAttachedDeviceReference and IoGetLowerDeviceObject to 'walk the stack' and find the PDO.

Thank you so much!

After walk the stack and get the last object, the function IoGetDevicePropertyData stops trigger BSOD

Then, after this, the BDN was in data under a WCHAR array

Thank you

You don’t have to walk down the stack manually. Send a irp mn query device relations/ targetdevicerelations to retrieve the pdo. Remember to Ob Deref the pdo upon success.