How to debug missing imports at driver load time

Debugging when your driver fails to load can be exasperating, especially if it is due to a missing import.  Windows 2000 would put up a dialog box telling the user which import was missing, but the user can't do anything about it (unless she is the driver developer), so that dialog was removed post Windows 2000.  You can run depends.exe against your driver on the OS that you are targeting, but that's no fun ;).  Instead, let's see you how can debug unresolved imports at load time. 

I added a call to KeEnterGuardedRegion into my test driver wdfrawbusenumtest.  This function was added in Windows Server 2003 SP1, so it is a perfect API to import and then try to load on XP.  First, you must set your symbols to the public symbol server

0: kd> !symfix
0: kd> .sympath
Symbol search path is: srv*https://msdl.microsoft.com/download/symbols

0: kd> .reload ntoskrnl.exe
Force unload of ntkrnlmp.exe
Loading symbols for 804d7000 ntkrnlmp.exe -> ntkrnlmp.exe
ModLoad: 804d7000 806fd000 ntkrnlmp.exe

0: kd> !lmi nt
...
Symbol Type: PDB - Symbols loaded successfully from symbol server.
ntkrnlmp.pdb\AA1EE1B2A63A4232A379F3EFDDC4CFE82\ntkrnlmp.pdb
Load Report: public symbols , not source indexed
ntkrnlmp.pdb\AA1EE1B2A63A4232A379F3EFDDC4CFE82\ntkrnlmp.pdb

then set a bp on MmLoadSystemImage
0: kd> bp nt!MmLoadSystemImage
0: kd> g

then attempt to load the driver (I used device manager, you can use devcon or net start if you are writing a legacy style driver which does not support PnP).  And we hit our breakpoint

Breakpoint 0 hit
nt!MmLoadSystemImage:
805a86ff 6874010000 push 0x174
0: kd> kb
ChildEBP RetAddr Args to Child
f88f2820 805a40d2 f88f28a4 00000000 00000000 nt!MmLoadSystemImage

The first parameter passed to MmLoadSystemImage is a PUNICODE_STRING, so let's make sure that this is our driver we are going to debug

0: kd> dt f88f28a4 _UNICODE_STRING
"\SystemRoot\system32\DRIVERS\wdfrawbusenumtest.sys"
+0x000 Length : 0x64
+0x002 MaximumLength : 0x66
+0x004 Buffer : 0xe1600c78 "\SystemRoot\system32\DRIVERS\wdfrawbusenumtest.sys"

Now, we need to set a breakpoint on MiResolveImageReferences, which will return the unresolved import on failure.

0: kd> bp nt!MiResolveImageReferences
0: kd> g

nt!MiResolveImageReferences:
805a223d 8bff mov edi,edi
0: kd> kb
ChildEBP RetAddr Args to Child
f88f2670 805a3ecf f1e47000 f88f27c4 00000000 nt!MiResolveImageReferences
f88f2820 805a40d2 f88f28a4 00000000 00000000 nt!MmLoadSystemImage+0x8c8

This is an internal function so this can change at anytime, but currently the fourth parameter is a PCHAR* which will be filled with name of the resolved import on failure.  First, we will look at the 4th (on Windows 2000 it is the 5th, but we are debugging XP here) parameter by dumping the EBP and then looking at what it points to (since it is a PCHAR*).

0: kd> dp f88f2670
f88f2670 00000246 805a3ecf f1e47000 f88f27c4
f88f2680 00000000 f88f27d4 f88f27d8 f88f27dc

0: kd> dp f88f27d4 l1
f88f27d4 81ffa250

and then we will jump back to the caller and verify failure

0: kd> g 805a3ecf
nt!MmLoadSystemImage+0x8c8:
0: kd> reax
eax=c0000263
0: kd> !error c0000263
Error code: (NTSTATUS) 0xc0000263 (3221226083) - {Driver Entry Point Not Found} The %hs device driver could not locate the entry point %hs in driver %hs.

and now let's see the missing import!

0: kd> dc 81ffa250
81ffa250 6e45654b 47726574 64726175 65526465 KeEnterGuardedRe
81ffa260 6e6f6967 00000000 00000000 00000000 gion............