Fuzzing Windows drivers

Fuzzing Windows drivers

TL;DR: Hypervisor-assisted snapshot fuzzers (kAFL, WTF, Jackalope) drive DeviceIoControl and IRP entry points at native speed while restoring kernel state per iteration — the practical way to find IOCTL bugs in modern third-party drivers.

What it is

Driver fuzzing targets the kernel-mode entry points exposed to user-mode callers — primarily IRP_MJ_DEVICE_CONTROL, IRP_MJ_READ, IRP_MJ_WRITE, and FastIO callbacks (see kernel-objects-and-irps). Naïve in-VM fuzzing crashes the kernel each finding, so the modern approach is to snapshot a known-good VM, replay inputs from that snapshot, and rewind on crash or timeout. Coverage feedback is gathered via Intel PT, KVM, or instrumented hypervisor traps.

Preconditions / where it applies

  • Target driver loaded on a controlled Windows VM (test-signing or attestation-bypass build for unsigned modules)
  • Known set of accepted IOCTL codes — extract via static analysis of IoCreateDevice/IoCreateSymbolicLink + the dispatch table, or via runtime hooking with WinDbg
  • Hardware support for the chosen engine: Intel PT for kAFL/WTF; bochscpu for WTF’s emulated mode

Technique

Typical pipeline:

  1. Reverse the dispatch table. MajorFunction[IRP_MJ_DEVICE_CONTROL] → identify the IOCTL switch. IDA scripts (drvhunter) or Ghidra automate enumeration of METHOD_* codes and expected buffer sizes.
  2. Build a harness. A user-mode stub that opens \\.\DeviceName, calls DeviceIoControl(handle, ioctl, in, in_len, out, out_len, &ret), and surfaces the input buffer to the fuzzer.
  3. Capture a snapshot. WTF uses bdump.js inside WinDbg to dump CPU + physical memory; kAFL relies on KVM-Nyx. The snapshot taken at the harness’s “just before DeviceIoControl” point.
  4. Fuzz. Mutate input buffer (and IOCTL code, if dispatch supports many). Each iteration restores the snapshot, applies the input, and steps the kernel until the syscall returns or a bug check fires.
  5. Triage. Bug check 0xBE, 0x1E, 0x3B, etc. → tag, dedupe by faulting RIP, minimise input, replay under live WinDbg with kernel-debugging-with-windbg.

Specific tooling:

1
2
3
4
kAFL          — KVM-Nyx + AFL frontend; Intel PT coverage; great for x86_64 Windows
WTF (Snapshot Fuzzer, Microsoft) — supports bochscpu / kvm / whv backends; replay-friendly
Jackalope     — TinyInst-based black-box coverage; useful when source-level instrumentation is impossible
HEVD + IOCTLpus — practice ground for bug-class triggers

Wins: Driverquery + symbol-aware grammars (e.g. specifying buffer layouts) dramatically improve hit rate vs flat byte mutation. Hooking ETW kernel-mode allocator events further accelerates UAF/double-fetch triage.

Detection and defence

  • Driver hardening: IoValidateDeviceIoControlAccess, ProbeForRead/Write on all user buffers, capture once into local stack copy to avoid double-fetch
  • Use METHOD_BUFFERED where possible — least foot-gun. Document METHOD_NEITHER paths and pen-test them aggressively
  • HVCI (hvci-vbs) and kernel CFG (cfg-cet-kernel) cut exploitability of discovered bugs
  • WHQL + Microsoft Driver Verifier (verifier /standard /driver foo.sys) catches many fuzzable bug classes pre-shipping

References

Related: kernel-objects-and-irps, kernel-debugging-with-windbg, double-fetch, symbolic-execution-for-windows-bugs