Windows 2000 introduced Driver Verifier, which was the single greatest gift ever to be bestowed upon driver writers. This set the bar pretty high for what Windows XP would bring, but I must say it delivered by introducing three awesome features:
- The triumphant return of Microsoft Bob’s Rover the dog
- A complete driver build environment. Remember what a mess it was when all you got were the headers and the libs from the WDK and the compiler and linker came from Visual Studio? Oh, wait…
- Automatic driver replacement with KDFiles
The first two features are now drifting off into history, but driver replacement using KDFiles is still supported and is, in fact, even more useful today (we’ll get to why later). Unfortunately, we find that people either don’t know about KDFiles or tried it but gave up after battling with the syntax.
In this article, we’ll revisit the need for KDFiles and provide a bulletproof method for determining the correct mapping syntax for driver replacement.
Why Driver Mapping?
Developing drivers on Windows requires two machines: a host machine for development and a target machine for running the driver under test. We never, everwant to run our driver on our development machine for fear of corrupting our system from the incessant crashing of our buggy driver.
This then leads to the practical problem of copying a driver that we built on our development machine to our target machine for testing. Without KDFiles, there is no maximally convenient way to do this. You can copy the file over the network or via a USB drive, each of which has their own annoyances. Virtual Machine debugging has the benefit of drag and drop, but that assumes that VMware hasn’t suddenly decided that it doesn’t want to do that anymore. And, of course, all of these assume that you actually rememberto copy the file over. Nothing kills the satisfaction of fixing a bug more than rebuilding your driver just to restart your tests using the old version.
KDFiles provides the ability to automatically update a driver on the target with a new file from the host each time the driver loads. This means you can always be running the latest version of your driver on the target without any additional post build steps. All you need to do is use the .kdfiles command to provide WinDbg with a “replacement map”, indicating which drivers on the target should be replaced by files from the host. The target O/S and WinDbg will then do the work to copy the updated driver over the kernel debug connection on each driver load.
Registering Replacements with Mapping Files
There are a couple of different ways that you can indicate a replacement mapping, but by far the most convenient is through a Mapping File. This is a simple text file with the following syntax:
Module path of driver on target
File system path of driver on host
The Mapping File can contain multiple mappings, each of which is preceded by the mandatory map keyword.
The second component of the mapping file is the tricky one. This is where we must specify the module path of the driver on the target, which is notthe same as the driver image’s location on disk. Some examples of common, equally valid module paths are:
The path used greatly depends on how the driver was installed and the current data of its ImagePath value. This effectively makes it impossible to know a prioriwhat the module path of the driver will be on the target.
This is by far the number one usage problem with KDFiles and we’ve never found a documented, foolproof way of determining the module path. There is however an undocumented way that is guaranteed to work until, well, it doesn’t, which is what we’ll demonstrate here.
The undocumented API KdPullRemoteFile is called by the target O/S during each driver load. This API is responsible for querying the host to determine if a mapping exists and, if it does, transferring the data to the local machine. By way of observation, this API takes as the first parameter a pointer to UNICODE_STRING that indicates the module path on the target. This is the string that must exactlymatch the path in your Mapping File, thus we can leverage this to determine what path we must specify in our mapping.
Let’s see an example. Say we would like to replace the driver nothing_kmdf.sys with a file from the host machine’s local disk. The key piece of information that we need is the module path of the driver on the target. To find this information, we can first set a breakpoint on KdPullRemoteFile:
kd> bp nt!KdPullRemoteFile
We then need to trigger a load of the driver, for example by disabling and then enabling the device. This hits our breakpoint, at which point we can decode the first parameter using the Display Unicode String command (dS).
Breakpoint 0 hit
35870d9c mov rax,rsp50282fa0 "\SystemRoot\System32\drivers\Not"
kd> dS @rcx
Note that for an x86 system, the first parameter would be located either on the stack at ESP+4, or in EAX (because on newer versions of Windows the function uses a non-standard calling convention) and not in RCX.
We can now generate the Mapping File by saving the following to a text file:
Next, we provide WinDbg with this replacement map by executing .kdfiles and specifying the path to the mapping file:
kd> .kdfiles e:\mapfile.ini
KD file assocations loaded from 'e:\mapfile.ini'
For sanity, we can execute .kdfiles again with no parameters to confirm the mapping:
KD file assocations loaded from 'e:\mapfile.ini'
\SystemRoot\System32\drivers\Nothing_KMDF.sys -> e:\nothing_kmdf.sys
The next time we load the driver, we should see output in the debugger indicating that the updated file is being “pulled” from the host to the target:
KD: Accessing 'e:\nothing_kmdf.sys' ...
KdPullRemoteFile: About to overwrite... Nothing_KMDF.sys and preallocate to 2670
KdPullRemoteFile: Return from ZwCreateFile with status 0
Voila!We’re now running the version of the driver that was located in the specified directory on our host.
If at any point we want to clear the current set of mappings, we can use the /c switch to the .kdfiles command. Beware though that KDFiles overwrites the driver image on the target, so clearing the mapping does not actually revert the version of the driver.
The Boot Debugger Supports KDFiles Too!
We mentioned at the start that KDFiles is now even more useful, but how is that? Well, it turns out that the Boot Debugger also supports driver replacement. This means that you can automatically update your boot start drivers, which are otherwise a pain to update during development. To do this, you simply need to make sure that the boot debugger is enabled on your target machine using bcdedit:
bcdedit.exe /set bootdebug on
And provide a mapping for the boot start driver in your mapping file.
KDFiles is one of those things that we would now have a hard time living without, though it certainly isn’t entirely user friendly in getting configured. If you’ve never heard of it before, you’re certainly in for a treat. If you have tried but gave up, give it another shot with the steps we outlined in this article and let us know how it goes!