Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTDEV

Before Posting...

Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


HDD serial number

OSR_Community_UserOSR_Community_User Member Posts: 110,217
Hi ALL!

I have a question:
How to read HDD serial number from device driver?

Best regards,
Dmitry.

Comments

  • iFengHuangiFengHuang Member Posts: 63

    Same question after 19 years...

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 9,046
    edited June 2019

    And the same answer I've been giving to such questions for that time: Did you try Google? There are, literally, 197,000 results when I entered this query.

    Peter

    (OK... after rethinking my wise-guy answer: Have you tried IOCTL_STORAGE_QUERY_PROPERTY?)

    Peter Viscarola
    OSR
    @OSRDrivers

  • anton_bassovanton_bassov Member MODERATED Posts: 5,281

    There are, literally, 197,000 results when I entered this query.

    ......and, out of these 197,000 results, IFengHuang somehow managed to find the one that points to the question that, for some reason, was left unanswered on NTDEV, and decided to "revive" the thread that had been resting in peace for 19(!!!) fucking years. Sounds incredible, but that's the way it is.......

    Anton Bassov

  • iFengHuangiFengHuang Member Posts: 63
    edited June 2019

    I'm so sorry for "Dig Grave".

    After referring to this article, I got a solution.

    The following is my code, leave it for those who need it in the future.

    [MODS: Updated sample provided by author below]

    Post edited by Peter_Viscarola_(OSR) on
  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 9,046

    Thank you for coming back to us and showing the solution. That's very nice of you! MUCH appreciated.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 14,411

    Where did you get this code? The "AllocRetry" thing here is an abomination that shows a fundamental misunderstanding. If your system is so short on non-paged pool that you can't allocate these small structures, then your system is going down. Retries won't help, and they are silly for a request as trivial as this one. Some people added these retry functions to try to work around the "low-memory simulation" in Driver Verifier, but the WHOLE POINT of that test is to make sure your driver handles a low-memory situation gracefully. By retrying like this, you never test your low-memory path.

    You should eliminate AllocRetry and replace it with ExAllocatePoolXxx. It's not clear to me why you need non-paged pool here, either. ZwCreateFile requires PASSIVE_LEVEL, so you'll be able to handle paged pool.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • iFengHuangiFengHuang Member Posts: 63

    @Tim_Roberts said:
    Where did you get this code? The "AllocRetry" thing here is an abomination that shows a fundamental misunderstanding. If your system is so short on non-paged pool that you can't allocate these small structures, then your system is going down. Retries won't help, and they are silly for a request as trivial as this one. Some people added these retry functions to try to work around the "low-memory simulation" in Driver Verifier, but the WHOLE POINT of that test is to make sure your driver handles a low-memory situation gracefully. By retrying like this, you never test your low-memory path.

    You should eliminate AllocRetry and replace it with ExAllocatePoolXxx. It's not clear to me why you need non-paged pool here, either. ZwCreateFile requires PASSIVE_LEVEL, so you'll be able to handle paged pool.

    The code is referring to this article, then I modify it into kernel mode.

    Thank you for pointing out the shortcomings. I'll modify the code later.

  • iFengHuangiFengHuang Member Posts: 63

    @Tim_Roberts said:
    Where did you get this code? The "AllocRetry" thing here is an abomination that shows a fundamental misunderstanding. If your system is so short on non-paged pool that you can't allocate these small structures, then your system is going down. Retries won't help, and they are silly for a request as trivial as this one. Some people added these retry functions to try to work around the "low-memory simulation" in Driver Verifier, but the WHOLE POINT of that test is to make sure your driver handles a low-memory situation gracefully. By retrying like this, you never test your low-memory path.

    You should eliminate AllocRetry and replace it with ExAllocatePoolXxx. It's not clear to me why you need non-paged pool here, either. ZwCreateFile requires PASSIVE_LEVEL, so you'll be able to handle paged pool.

    By the way, but I saw many alloc-retry-like macro inside WFPSampler codes, why do they write like that?

  • iFengHuangiFengHuang Member Posts: 63

    I find that I can't edit my comment anymore. :'(

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 9,046

    I can't edit my comment anymore

    Yup! By design. You have one hour to edit something you post. After that, you've got to me a mod to edit anything.

    alloc-retry-like macro inside WFPSampler codes, why do they write like that

    If the screen cap you posted is what you're referring to, this isn't an alloc-retry... it's using a "safe multiplier" function to detect/avoid an overrun during the multiply.

    Having said that, I'm not sure why -- after calling RtlSizeTMult and checking for success -- he needs to check that the allocated size is >= the plain multiplied (count * sizeof) size... but "whatever"...

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 9,046

    OTOH, if you're referring to this:

    That is some of the dumbest code I've seen in a very long time. Whoever wrote that tight loop calling ExAllocatePool over and over again should be .... I dunno. Something.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • iFengHuangiFengHuang Member Posts: 63

    @Peter_Viscarola_(OSR) said:

    I can't edit my comment anymore

    Yup! By design. You have one hour to edit something you post. After that, you've got to me a mod to edit anything.

    I want to fix my code above for removing AllocRetry.. How can I do that now ?

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 9,046

    Just call ExAllocatePool instead of AllocRetry. I think Mr. Roberts May have suggested this already.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • iFengHuangiFengHuang Member Posts: 63

    @Peter_Viscarola_(OSR) said:
    Just call ExAllocatePool instead of AllocRetry. I think Mr. Roberts May have suggested this already.

    Peter

    :D I means I want to fix my comment above for those who need it in the future easy to read.

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 9,046

    That is very considerate. Why not post an updated version below?

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • iFengHuangiFengHuang Member Posts: 63
    edited June 2019

    Concise, neat and centralized code which showing in the forum is very important for me, especially for those who are eager to find an off-the-shelf solution (like me).

    Here is the fixed version:

    ...
    #include <ntddstor.h>
    #define DEVICE_DESCRIPTOR 'CDVD'
    #define SERIAL_NUMBER 'BNRS'
    ...
    NTSTATUS GetHardDiskDriveSerialNumber(_Out_ PCHAR *pSerialNumber, _Out_ SIZE_T *pLength) {
        NTSTATUS status = STATUS_SUCCESS;
        STORAGE_PROPERTY_QUERY storagePropertyQuery = { StorageDeviceProperty, PropertyStandardQuery, 0 };
        STORAGE_DESCRIPTOR_HEADER storageDescriptorHeader = { 0 };
        PSTORAGE_DEVICE_DESCRIPTOR pDeviceDescriptor = nullptr;
    
        UNICODE_STRING fileName;
        RtlInitUnicodeString(&fileName, L"\\DosDevices\\PhysicalDrive0");
    
        OBJECT_ATTRIBUTES attributes;
        InitializeObjectAttributes(&attributes, &fileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, nullptr, nullptr);
    
        HANDLE fileHandle = nullptr;
        IO_STATUS_BLOCK ioStatus;
    
        // open PhysicalDrive0
        status = ZwCreateFile(
            &fileHandle,
            GENERIC_READ,
            &attributes,
            &ioStatus,
            nullptr,
            FILE_ATTRIBUTE_NORMAL,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            FILE_OPEN,
            FILE_NON_DIRECTORY_FILE,
            nullptr,
            0);
    
        if (!NT_SUCCESS(status) || !NT_SUCCESS(ioStatus.Status)) {
            KdPrint(("Open PhysicalDrive0 failed, status: %!STATUS!, ioStatus: %!STATUS!, information: %llu.\n", 
                status, ioStatus.Status, ioStatus.Information));
    
            if (!NT_SUCCESS(ioStatus.Status))
                status = ioStatus.Status;
    
            goto END;
        }
    
        KdPrint(("Open PhysicalDrive0 success: %p.\n", fileHandle));
    
        // first call for getting size
        status = ZwDeviceIoControlFile(
            fileHandle,
            nullptr,
            nullptr,
            nullptr,
            &ioStatus,
            IOCTL_STORAGE_QUERY_PROPERTY,
            &storagePropertyQuery,
            sizeof(STORAGE_PROPERTY_QUERY),
            &storageDescriptorHeader,
            sizeof(STORAGE_DESCRIPTOR_HEADER));
    
        if (!NT_SUCCESS(status) || !NT_SUCCESS(ioStatus.Status)) {
            KdPrint(("ZwDeviceIoControlFile first call failed, status: %!STATUS!, ioStatus: %!STATUS!, information: %llu.\n", 
                status, ioStatus.Status, ioStatus.Information));
    
            if (!NT_SUCCESS(ioStatus.Status))
                status = ioStatus.Status;
    
            goto END;
        }
    
        // alloc for descriptor
        pDeviceDescriptor = (PSTORAGE_DEVICE_DESCRIPTOR)ExAllocatePoolWithTag(NonPagedPoolNx, storageDescriptorHeader.Size, DEVICE_DESCRIPTOR);
        if (!pDeviceDescriptor) {
            status = STATUS_INSUFFICIENT_RESOURCES;
            goto END;
        }
        RtlZeroMemory(pDeviceDescriptor, storageDescriptorHeader.Size);
    
        // second call for getting serial number
        status = ZwDeviceIoControlFile(
            fileHandle,
            nullptr,
            nullptr,
            nullptr,
            &ioStatus,
            IOCTL_STORAGE_QUERY_PROPERTY,
            &storagePropertyQuery,
            sizeof(STORAGE_PROPERTY_QUERY),
            pDeviceDescriptor,
            storageDescriptorHeader.Size);
    
        if (!NT_SUCCESS(status) || !NT_SUCCESS(ioStatus.Status)) {
            KdPrint(("ZwDeviceIoControlFile second call failed, status: %!STATUS!, ioStatus: %!STATUS!, information: %llu.\n", 
                status, ioStatus.Status, ioStatus.Information));
    
            if (!NT_SUCCESS(ioStatus.Status))
                status = ioStatus.Status;
    
            goto END;
        }
    
        if (pDeviceDescriptor->SerialNumberOffset > 0) {
            PCHAR originSerialNumber = WDF_PTR_ADD_OFFSET_TYPE(pDeviceDescriptor, pDeviceDescriptor->SerialNumberOffset, PCHAR);
    
            KdPrint(("SerialNumber: %s.\n", originSerialNumber));
    
            status = RtlStringCbLengthA(originSerialNumber, 128, pLength);
            if (!NT_SUCCESS(status)) {
                KdPrint(("RtlStringCbLengthA failed, string: %s.\n", originSerialNumber));
                goto END;
            }
    
            SIZE_T bufferSize = (*pLength) + 1; // include terminating null character
            PCHAR serialNumber = (PCHAR)ExAllocatePoolWithTag(NonPagedPoolNx, bufferSize, SERIAL_NUMBER);
            if (!serialNumber) {
                status = STATUS_INSUFFICIENT_RESOURCES;
                goto END;
            }
            RtlZeroMemory(serialNumber, bufferSize);
    
            status = RtlStringCbCopyA(serialNumber, bufferSize, originSerialNumber);
            if (!NT_SUCCESS(status)) {
                KdPrint(("RtlStringCbCopyA failed, status: %!STATUS!, string: %s.\n", status, originSerialNumber));
    
                if (serialNumber)
                    ExFreePoolWithTag(serialNumber, SERIAL_NUMBER);
    
                goto END;
            }
    
            *pSerialNumber = serialNumber;
    
        } else {
            KdPrint(("SerialNumberOffset: %u.\n", pDeviceDescriptor->SerialNumberOffset));
            status = STATUS_QUERY_STORAGE_ERROR;
    
            *pSerialNumber = nullptr;
            *pLength = 0;
        }
    
    END:
        if (fileHandle)
            ZwClose(fileHandle);
        if (pDeviceDescriptor)
            ExFreePoolWithTag(pDeviceDescriptor, DEVICE_DESCRIPTOR);
    
        return status;
    }
    
    VOID FreeHardDiskDriveSerialNumber(_In_ PCHAR serialNumber) {
        ExFreePoolWithTag(serialNumber, SERIAL_NUMBER);
    }
    
  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 9,046

    And I have removed the code sample from your earlier post and pointed people to this post.

    Thank you for your concern for future people searching for this info. It is very good of you to do this... too few people are willing to share with others. They only care for their question to be answered. So.... thanks!

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • anton_bassovanton_bassov Member MODERATED Posts: 5,281

    Concise, neat and centralized code which showing in the forum is very important for me, especially for those who are eager
    to find an off-the-shelf solution (like me).

    Well, the example of AllocRetry() in your code seems to be a pretty good demo of why copy-pasting "off-the-shelf solutions" may be not- so- good idea - after all, you did not come up with this "pearl" yourself, did you. Instead, you have modeled it upon some existing sample......

    Anton Bassov

  • SweetLowSweetLow Member Posts: 40

    @Peter_Viscarola_(OSR) said:

    (OK... after rethinking my wise-guy answer: Have you tried IOCTL_STORAGE_QUERY_PROPERTY?)

    It's practically right but slightly imperfect.
    In some cases (i can said - in MANY cases) you will have not the exact serial number but its decoration in form of hexadecimal representation.
    P.S. If we need this number just for some unique persistent value it's good idea to concatenate it with Vendor and Product...

  • pulppulp Member Posts: 2

    Hello tried this code and works ok on windows 8 vmware but on windows 10 vmware had to modify it a bit to work because of bsod. But on real windows 10 got bsod - page fault in non paged area. Tested it on vmware is ok but on real host this bsod do you have an idea why it is very hard to debug on real pc.

    !analyze -v


    • *
    • Bugcheck Analysis *
    • *

    PAGE_FAULT_IN_NONPAGED_AREA (50)
    Invalid system memory was referenced. This cannot be protected by try-except.
    Typically the address is just plain bad or it is pointing at freed memory.
    Arguments:
    Arg1: ffffffff80002c70, memory referenced.
    Arg2: 0000000000000000, value 0 = read operation, 1 = write operation.
    Arg3: fffff8041c9d23d5, If non-zero, the instruction address which referenced the bad memory
    address.
    Arg4: 0000000000000002, (reserved)

    Debugging Details:

    *** WARNING: Unable to verify timestamp for gethwid.sys

    Could not read faulting driver name
    *** WARNING: Unable to verify timestamp for win32k.sys

    KEY_VALUES_STRING: 1

    Key  : Analysis.CPU.Sec
    Value: 5
    
    Key  : Analysis.DebugAnalysisProvider.CPP
    Value: Create: 8007007e on DESKTOP-9IDCT4T
    
    Key  : Analysis.DebugData
    Value: CreateObject
    
    Key  : Analysis.DebugModel
    Value: CreateObject
    
    Key  : Analysis.Elapsed.Sec
    Value: 38
    
    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 79
    
    Key  : Analysis.System
    Value: CreateObject
    

    BUGCHECK_CODE: 50

    BUGCHECK_P1: ffffffff80002c70

    BUGCHECK_P2: 0

    BUGCHECK_P3: fffff8041c9d23d5

    BUGCHECK_P4: 2

    READ_ADDRESS: fffff8041d2fb390: Unable to get MiVisibleState
    Unable to get NonPagedPoolStart
    Unable to get NonPagedPoolEnd
    Unable to get PagedPoolStart
    Unable to get PagedPoolEnd
    fffff8041d20f380: Unable to get Flags value from nt!KdVersionBlock
    fffff8041d20f380: Unable to get Flags value from nt!KdVersionBlock
    unable to get nt!MmSpecialPagesInUse
    ffffffff80002c70

    MM_INTERNAL_CODE: 2

    BLACKBOXBSD: 1 (!blackboxbsd)

    BLACKBOXNTFS: 1 (!blackboxntfs)

    BLACKBOXPNP: 1 (!blackboxpnp)

    BLACKBOXWINLOGON: 1

    CUSTOMER_CRASH_COUNT: 2

    PROCESS_NAME: System

    TRAP_FRAME: ffff858082d8d8c0 -- (.trap 0xffff858082d8d8c0)
    NOTE: The trap frame does not contain all registers.
    Some register values may be zeroed or incorrect.
    rax=ffff858082d8dac0 rbx=0000000000000000 rcx=ffff858082d8dcc0
    rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
    rip=fffff8041c9d23d5 rsp=ffff858082d8da50 rbp=ffff858082d8db50
    r8=0000000000000000 r9=0000000000000000 r10=0000000000000000
    r11=ffff858082d8df38 r12=0000000000000000 r13=0000000000000000
    r14=0000000000000000 r15=0000000000000000
    iopl=0 nv up ei ng nz na pe nc
    nt!output_l+0x89:
    fffff8041c9d23d5 458a0c24 mov r9b,byte ptr [r12] ds:0000000000000000=??
    Resetting default scope

    STACK_TEXT:
    ffff858082d8d618 fffff8041ca4e27f : 0000000000000050 ffffffff80002c70 0000000000000000 ffff858082d8d8c0 : nt!KeBugCheckEx
    ffff858082d8d620 fffff8041c8a6960 : 0000000000000000 0000000000000000 ffff858082d8d940 0000000000000000 : nt!MiSystemFault+0x1898cf
    ffff858082d8d720 fffff8041ca05f5e : 0000000000000000 ffff858000000000 ffffb70917a9b1c0 ffff88070d1dcae0 : nt!MmAccessFault+0x400
    ffff858082d8d8c0 fffff8041c9d23d5 : 0000000000000000 0000000000000000 0000000000000000 0000000000000000 : nt!KiPageFault+0x35e
    ffff858082d8da50 fffff8041c9ce19a : 0000000000000020 0000000000000000 ffff858082d8de10 fffff8041ca097b8 : nt!output_l+0x89
    ffff858082d8dd10 fffff8041c9ce121 : 000000000000007f ffff858082d8de10 ffff37403f844682 fffff8041ca09620 : nt!vsnprintf_l+0x6a
    ffff858082d8dd80 fffff8041c96f46b : ffff858082d8e470 ffff880700bd3500 ffff858082d8de40 fffff8041c9ce08c : nt!vsnprintf+0x11
    ffff858082d8ddc0 fffff8041c962c64 : 0000000000000000 0000000000000080 0000000000000003 ffffb709254925e0 : nt!RtlStringCbVPrintfA+0x3f
    ffff858082d8ddf0 fffff8041c962b3c : ffff8807002d0000 ffff880700bd3510 ffff858082d8e470 fffff8041c9fbc30 : nt!vDbgPrintExWithPrefixInternal+0xe4
    ffff858082d8def0 fffff8041d904c70 : ffffffff80002c70 0000000000000000 ffff88070d1dcae0 ffffb7090e652880 : nt!DbgPrint+0x3c
    ffff858082d8df40 ffffffff80002c70 : 0000000000000000 ffff88070d1dcae0 ffffb7090e652880 0000000000000000 : gethwid+0x4c70
    ffff858082d8df48 0000000000000000 : ffff88070d1dcae0 ffffb7090e652880 0000000000000000 ffff858000000080 : 0xffffffff`80002c70

    SYMBOL_NAME: gethwid+4c70

    MODULE_NAME: gethwid

    IMAGE_NAME: gethwid.sys

    STACK_COMMAND: .thread ; .cxr ; kb

    BUCKET_ID_FUNC_OFFSET: 4c70

    FAILURE_BUCKET_ID: AV_R_INVALID_gethwid!unknown_function

    OS_VERSION: 10.0.19041.1

    BUILDLAB_STR: vb_release

    OSPLATFORM_TYPE: x64

    OSNAME: Windows 10

    FAILURE_ID_HASH: {5c1ae45c-6bee-516e-d466-f1bd94725f76}

    Followup: MachineOwner

  • Jason_T.Jason_T. Member Posts: 98

    @pulp said:
    Hello tried this code and works ok on windows 8 vmware but on windows 10 vmware had to modify it a bit to work because of bsod. But on real windows 10 got bsod - page fault in non paged area. Tested it on vmware is ok but on real host this bsod do you have an idea why it is very hard to debug on real pc.

    A bit difficult to see your screen from over here, but it appears to have blown up in a print. Looking at the code, this one seems most likely:

    KdPrint(("SerialNumber: %s.\n", originSerialNumber));

    Well, actually, the most likely detonation site is the part where you "had to modify it a bit".

    In any case, you should load your driver symbols (.reload -f gethwid.sys) and then open the call stack window, double click on this function, open the locals and see what's what...

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 14,411

    If you used the debug build, windbg would be happy to show you the line of code that failed.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • pulppulp Member Posts: 2
    edited June 20
    Yes it was the debug string indeed. When removed the first debug string : KdPrint(("Open PhysicalDrive0 success
    and the \n it was ok. But can you tell me why this happens with dbgprint?
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. Sign in or register to get started.

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Kernel Debugging 30 January 2023 Live, Online
Developing Minifilters 20 March 2023 Live, Online
Writing WDF Drivers TBD 2023 Live, Online
Internals & Software Drivers 17 April 2023 Live, Online