KMDF drivers (!wdfkd)

The !wdfkd.* commands read the Kernel-Mode Driver Framework’s own state from Wdf01000.sys, using the types in Microsoft’s public Wdf01000.pdb. They need that module loaded with its PDB, which the symbol server supplies, but you do not need the private PDB of a driver. The commands do not support UMDF drivers.

Command

Shows

!wdfkd.wdfldr

All KMDF client drivers, with each driver’s name, the KMDF version that it bound to, its _FX_DRIVER_GLOBALS, WDFDRIVER handle, and DRIVER_OBJECT, and whether it has an In-Flight Recorder (IFR) log.

!wdfkd.wdfdriverinfo <driver>

One client driver and each of its device objects, with the WDFDEVICE for each device object.

!wdfkd.wdfhandle <handle>

The object behind a handle: its type, size, reference count, state, owner, parent, and contexts.

!wdfkd.wdfdevice <WDFDEVICE>

A device’s WDM device objects, its PnP, power, and power policy states, and its queues.

!wdfkd.wdfqueue <WDFQUEUE>

A queue’s dispatch type, state, and callbacks, the requests that wait in it, and the requests that the driver owns.

!wdfkd.wdflogdump <driver>

A driver’s IFR log, with the oldest record first.

To name a driver, use the name that !wdfkd.wdfldr shows. The name is not case-sensitive, and the commands ignore a trailing .sys.

A client whose DriverName is empty, because it bound to KMDF without creating an FxDriver, is listed and named by the name of its DRIVER_OBJECT. For example, the name for \Driver\kdnic is kdnic.

A typical walk starts at the driver list and follows the handles down:

!wdfkd.wdfldr
!wdfkd.wdfdriverinfo kmdfsample
!wdfkd.wdfdevice 00003ef5edcba988
!wdfkd.wdfqueue 00003ef5edcb1238

Handles

A WDF handle is the address of its object XORed with ~7.

A handle with bit 0 set is an offset handle, which points to a WDFOBJECT_OFFSET inside a larger object, such as the buffer of a request. The commands subtract this offset to get the larger object.

If you give the address of an object instead of its handle, the command gives an error that shows the handle of that object.

Before a command shows an object, it checks the object’s FxObject header, and all of these conditions must be true:

  • m_Type is an FX_OBJECT_TYPES value.

  • m_ObjectState is an FxObjectState value.

  • m_ObjectSize is aligned, and it is not smaller than the class of the type.

  • The context header after the object points back to the object.

  • m_Globals belongs to a registered client driver.

If a check fails, the command gives the reason and does not show the object. The same checks apply to each object that a command reaches through a list or a pointer. When one of these objects fails, the list ends at that object, and the output shows where and why.

The client driver list is an exception, because its integrity comes from its links: the Blink of each entry must point back to the entry before it. A client whose name or FxDriver fails the checks therefore stays in the list, and the list shows the problem.

The In-Flight Recorder

For each client driver, KMDF writes its own trace messages to a small ring buffer called the IFR.

!wdfkd.wdflogdump reads the IFR backward from the newest record and shows, for each record:

  • the sequence number

  • the UTC time, if the log keeps timestamps

  • the function

  • the message

The command formats the messages from the trace message format (TMF) annotations in Wdf01000.pdb, so you do not need .tmf files. If no loaded PDB declares the message of a record, the command shows the record’s message GUID, number, and argument bytes instead.

The walk ends at the first record that KMDF wrote, or where newer records overwrote older ones. The command checks the header and each record as it reads them:

  • the signature

  • the length

  • the position

  • the sequence numbers, which must decrease

If a check fails, the walk stops and reports the corruption, but keeps the records that it already read.

From Python

dbg.inspect has one method for each command, and each method returns the decoded fields as typed records:

for client in dbg.inspect.wdf_loader().clients:
    print(client.name, client.version)
for record in dbg.inspect.wdf_log("kmdfsample").records:
    print(record.sequence, record.function, record.text or record.error)