Search code examples
cwindowswmidrivers

Getting Serial Number using IOCTL_STORAGE_QUERY_PROPERTY


So I am trying to write a minifilter driver which only attaches to specific USB devices, to differentiate said devices I have use the combination of Product ID + Vendor ID + Serial Number.

I can successfully send IOCTL_STORAGE_QUERY_PROPERTY to the device which returns the Product ID,Vendor ID, Serial number.

The issue I have is the serial number which is returned to my minifilter is correct for some USB s but not all.

eg: when i call

C:\Windows\system32>wmic diskdrive get pnpdeviceid PNPDeviceID USBSTOR\DISK&VEN_SONY&PROD_STORAGE_MEDIA&REV_PMAP\5C3000637C2070A595&0 USBSTOR\DISK&VEN_BM&PROD_&REV_1.10\070007AA1F02CF40063F&0

And these are the serial numbers returned from my minifilter :

Serial Number found 57C03A050905. Serial Number found 070007AA1F02CF400630.

As it can be seen that the second device's serial number is successfully returned but not for the first one. So what is the serial number that my minifilter is receiving? Is this stored somewhere which can be queried?

I can attach the code if required but since I get some Serial Numbers correctly i doubt that my code is wrong.

Edit: Code

STORAGE_PROPERTY_QUERY query;
pQuery.PropertyId = StorageDeviceProperty;
pQuery.QueryType = PropertyStandardQuery

KeInitializeEvent(&event, NotificationEvent, FALSE);
Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_QUERY_PROPERTY, pDeviceObject, (PVOID)&query, sizeof(query), infoBuffer,
                                        sizeof(infoBuffer), FALSE, &event, &ioStatusBlock);
if (Irp) {
    if(!NT_SUCCESS(IoCallDriver(pDeviceObject, Irp)))
        return STATUS_FLT_DO_NOT_ATTACH;
}
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
pDescriptor = (PSTORAGE_DEVICE_DESCRIPTOR)infoBuffer;

ULONG offset = pDescriptor->SerialNumberOfffset;
size_t size;
if (offset == 0) 
    return;

PCHAR c = offset + &buffer[0];
size = strlen(c);
*dest = ExAllocatePoolWithTag(PagedPool, size + 1, 'DIcI');
RtlZeroMemory(*dest, size + 1);
RtlCopyMemory(*dest, c, size + 1);

DbgPrint("Serial Number Found %s \n", *dest);
// String comparison of serial number and more processing

tested on my external harddisk and this is what i get from device manager

575834314137363534565656 

and from my minifilter:

WX41A7654VVV 

it seems that the serial number in the device manager is hex representation of the serial number which i got from my minifilter

57 58 34 31 41 37 36 35 34 56 56 56 

W  X  4  1  A  7  6  5  4  V  V  V 

so for some devices it is represented in Hex format while others is Char format?

So is there anyway to get serial number from kernel level or it would just be easier calling a user application?


Solution

  • the STORAGE_DEVICE_DESCRIPTOR is variable length structure. after success call IOCTL_STORAGE_QUERY_PROPERTY need

    check its Size member to determine the number of bytes the structure actually requires.

    but in your code i view infoBuffer, sizeof(infoBuffer) - this mean that you use hard-coded size for STORAGE_DEVICE_DESCRIPTOR.

    really need send IOCTL_STORAGE_QUERY_PROPERTY and compare Size member with your OutputBufferLength and if it greater - send IOCTL_STORAGE_QUERY_PROPERTY again with larger OutputBufferLength .

    void PrintSerial(PDEVICE_OBJECT DeviceObject)
    {
        STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery }; 
    
        union {
            PVOID buf;
            PSTR psz;
            PSTORAGE_DEVICE_DESCRIPTOR psdd;
        };
    
        ULONG size = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x100;
    
        NTSTATUS status;
    
        do 
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
    
            if (buf = ExAllocatePool(PagedPool, size))
            {
                switch (status = IoControlDevice(DeviceObject, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, size))
                {
                case STATUS_SUCCESS:
                case STATUS_BUFFER_OVERFLOW:
    
                    if (psdd->Version == sizeof(STORAGE_DEVICE_DESCRIPTOR))
                    {
                        if (psdd->Size > size)
                        {
                            size = psdd->Size;
                            status = STATUS_BUFFER_OVERFLOW;
                        }
                        else
                        {
                            if (psdd->SerialNumberOffset)
                            {
                                DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
                            }
                            else
                            {
                                DbgPrint("SerialNumberOffset==0\n");
                            }
                        }
                    }
                    else
                    {
                        status = STATUS_INVALID_PARAMETER;
                    }
                    break;
                }
    
                ExFreePool(buf);
            }
        } while (status == STATUS_BUFFER_OVERFLOW);
    }
    
    NTSTATUS IoControlDevice(
                           PDEVICE_OBJECT DeviceObject,
                           ULONG IoControlCode,
                           PVOID InputBuffer,
                           ULONG InputBufferLength,
                           PVOID OutputBuffer,
                           ULONG OutputBufferLength,
                           BOOLEAN InternalDeviceIoControl = FALSE
                           )
    {
        KEVENT Event;
        KeInitializeEvent(&Event, NotificationEvent, FALSE);
    
        IO_STATUS_BLOCK  IoStatusBlock;
    
        if (PIRP Irp = IoBuildDeviceIoControlRequest(IoControlCode, DeviceObject, InputBuffer, InputBufferLength,
            OutputBuffer, OutputBufferLength, InternalDeviceIoControl, &Event, &IoStatusBlock))
        {
            NTSTATUS status = IofCallDriver(DeviceObject, Irp);
    
            if (status == STATUS_PENDING)
            {
                KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, 0);
    
                status = IoStatusBlock.Status;
            }
    
            return status;
        }
    
        return STATUS_INSUFFICIENT_RESOURCES;
    }