Bug on my Boot driver! Now what?

July 16th, 2007 - Fernando Roberto

Writing drivers is a task that must be done with little care. After all, any unresolved situation between your code and the operating system will result in a beautiful blue screen. But everything in this life has a cure and fortunately God has created the debugger to deal with these situations. After fixing the problem, it is just to change the .sys file and that’s it. Just changing the file means that the machine is expected to be in a stable condition: then, replace the file on the system and reboot the machine. But life is a surprising box and you may not believe, but most drivers are automagically started. Well, in this case, the only think we can do is to pray that the error does not happen until the driver is replaced. Otherwise, we would have to use some resources, such as putting the hard drive of victim’s machine on another computer to replace the sick driver. When there is no one else’s computer, use the Recovery Console in XP to avoid the sick driver being loaded. With some luck, this driver is not a filter attached to something important like disk, video or File System. You know, if a filter is not able to be loaded, then the main driver, either. In summary, there is a lot of juggling you can invent to replace a buggy driver that is automatically loaded. Is there anything that does not depend much on luck or even on the creativity? Today, I will talk a bit about the replacing driver system with WinDbg.

Mapping driver files

You can map the driver files so the WinDbg can replace it at the time it’d be loaded. Is not that beautiful? For this, you must use the .kdfiles command. With this command, you make a link between the driver that runs on the Target machine and the one that has been fixed on the Host side. One way to do this is to initially create a file that lists these two drivers. This file should be a simple text file where the syntax is shown below. This file can have any name and extension.

map 
\??\C:\Windows\System32\drivers\MyDriver.sys 
C:\My_Driver_Project_Folder\MyDriver.sys

The word map marks the beginning of a mapping; this does not change. Next line is the driver file path to be replaced from. This line should have the same format used in the ImagePath value under the driver key that is on registry. The last line is another driver path. This path could point at a driver in the Host machine itself or on the network.

This mapping works only in Windows XP or higher, in the Target machine, obviously. If you’re not familiar with terms like Host and Target, check this post out.

Once created the file, you will use WinDbg to launch the following command:

kd>.kdfiles C:\Any_Folder\MyFileMap.txt

Thereafter, whenever the driver is loaded by the system, the kernel checks whether this file is mapped in the debugger, and if so, WinDbg sends the new driver through the debugger connection, such as serial, USB or firewire. For large files, I recommend using firewire. You must understand that the file on Target system’s disk is replaced by the new version. This means that in the forthcoming launches of our driver, the new version will still be loaded, even if the debugger is not connected to the system.

Does this really work?

To make things a little clearer, let’s see a practical example. To do this, we need a very simple driver source, or even useless, which can be found here. Let’s change its DriverEntry function, so that it will be as shown below:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObject,
                     IN PUNICODE_STRING pusRegistryPath)
{
    //-f--> Let's use __DATE__ and __TIME__ to change this message
    //      each time this driver is built.
    DbgPrint("This driver was built on "__DATE__" "__TIME__"\n");
 
...

So, we build an initial version of the driver and we install it on the Target machine in the way you best think, but we’ll assume that after the driver has been installed, the registry will be as shown below.

If we start the driver, we will have something like the following string in the debugger output:

This driver was built on Jul 16 2007 00:04:03

Now, we’ll create the mapping file. Here I will save it as Z:\Sources\DriverEntry\Useless\map.txt. Following the same format the driver was registered in the registry, our mapping file should have the following content:

map
\??\C:\Windows\System32\drivers\Useless.sys
Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys

Notice that the folder where I saved my file map.txt is the same where the driver sources are. This is just a matter of organization. The mapping file could be in any folder. Then, use the command .kdfiles as shown below. Notice that we list the existing maps simply using the same command without any parameters.

kd> .kdfiles Z:\Sources\DriverEntry\Useless\map.txt
KD file assocations loaded from 'Z:\Sources\DriverEntry\Useless\map.txt'
 
kd> .kdfiles
KD file assocations loaded from 'Z:\Sources\DriverEntry\Useless\map.txt'
\??\C:\Windows\System32\drivers\Useless.sys ->
Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys

Afterwards, we’ll Rebuild the driver (that should change that time stamp) and we will restart it. If everything is right there by your side, you should have an output with a time stamp different from what we had before.

kd> g
But now? I did nothing so far...
KD: Accessing 'Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys'
 (\??\C:\Windows\system32\drivers\Useless.sys)
  File size 2K.
MmLoadSystemImage: Pulled \??\C:\Windows\system32\drivers\Useless.sys from kd
This driver was built on Jul 16 2007 00:17:04

Well, if this works for a driver with manual start value, then the automatic one also should work. To see this, change your driver start value to System or Automatic, do a Rebuild again in the driver, and finally, we will restart the Target machine. When the driver is loaded, we have the automatic replacement of its image. In WinDbg, we will have the following:

Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
Windows XP Kernel Version 2600 UP Free x86 compatible
Built by: 2600.xpsp_sp2_gdr.070227-2254
Kernel base = 0x804d7000 PsLoadedModuleList = 0x805533a0
System Uptime: not available
KD: Accessing 'Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys'
 (\??\C:\Windows\system32\drivers\Useless.sys)
  File size 2K.
MmLoadSystemImage: Pulled \??\C:\Windows\system32\drivers\Useless.sys from kd
This driver was built on Jul 16 2007 00:25:38

Yeah, it really works! But what if was my driver a Boot driver? This means that the driver image will be loaded even before the connection is established with WinDbg. Have you heard that what has no remedy, it is remedied? Fortunately, this is not applied here yet. There is a way to make this work, even with Boot drivers.

Mapping Boot drivers

To establish a connection with WinDbg, we need to replace the system loader by a special version. This version makes this connection with the Kernel Debugger, even before the Boot.ini file is read. For this reason, the connection parameters are fixed with COM1 and baud rate of 115200. This loader version is located in the directory C:\winddk\3790\debug of the DDK with its ntldr_dbg name. This file should replace the original loader system version that is at the boot drive root with the ntldr name. The debug version must stay with the same name as the original loader.

Before rebooting, we need to change the driver’s start value to Boot and remove the ImagePath value from the registry. As you may know, boot drivers do not have that luxury of using file paths that have drive letters. At the end of the changes, we should have the registry as shown below.

As I said earlier, the driver file path format should be the same as it is on registry, but knowing that now there’s no file path in the registry, so we adopt the same forms adopted by the system. Oh! okay! The same format. And what would it be? To see this format, simply restart the system with the debug loader, which should give us the following screen at boot-up.

This is the time to connect to Windbg using pre-determined parameters connection. This should result in the following output in the debugger.

Microsoft (R) Windows Debugger  Version 6.7.0005.1
Copyright (c) Microsoft Corporation. All rights reserved.
 
Opened \\.\pipe\com_1
Waiting to reconnect...
BD: Boot Debugger Initialized
BD: osloader.exe base address 00400000
Connected to Windows Boot Debugger 3790 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
Windows Boot Debugger Kernel Version 3790 UP Checked x86 compatible
Primary image base = 0x00000000 Loaded module list = 0x00000000
System Uptime: not available
The call to LoadLibrary(bootext) failed, Win32 error 0n2
    "The system cannot find the file specified."
Please check your debugger configuration and/or network access.

Now you see the Boot.ini selection menu. Select your option and continue loading the system. A list of boot drivers should be presented, which are the ones loaded before the first breakpoint that the debugger can stop.

BD: \WINDOWS\system32\ntoskrnl.exe base address 804EA000
BD: \WINDOWS\system32\hal.dll base address 806FF000
BD: \WINDOWS\system32\KDCOM.DLL base address 80720000
BD: \WINDOWS\system32\BOOTVID.dll base address 80010000
BD: \WINDOWS\system32\DRIVERS\ACPI.sys base address 80124000
BD: \WINDOWS\system32\DRIVERS\WMILIB.SYS base address 80001000
BD: \WINDOWS\system32\DRIVERS\pci.sys base address 80062000
BD: \WINDOWS\system32\DRIVERS\isapnp.sys base address 80003000
BD: \WINDOWS\system32\DRIVERS\compbatt.sys base address 8000C000
BD: \WINDOWS\system32\DRIVERS\BATTC.SYS base address 80013000
BD: \WINDOWS\system32\DRIVERS\intelide.sys base address 80017000
BD: \WINDOWS\system32\DRIVERS\PCIIDEX.SYS base address 80019000
BD: \WINDOWS\System32\Drivers\MountMgr.sys base address 80152000
BD: \WINDOWS\system32\DRIVERS\ftdisk.sys base address 8015D000
BD: \WINDOWS\System32\drivers\dmload.sys base address 80073000
BD: \WINDOWS\System32\drivers\dmio.sys base address 8017C000
BD: \WINDOWS\System32\Drivers\PartMgr.sys base address 801A2000
BD: \WINDOWS\System32\Drivers\VolSnap.sys base address 801A7000
BD: \WINDOWS\system32\DRIVERS\atapi.sys base address 801B4000
BD: \WINDOWS\system32\DRIVERS\vmscsi.sys base address 801CC000
BD: \WINDOWS\system32\DRIVERS\SCSIPORT.SYS base address 801CF000
BD: \WINDOWS\system32\DRIVERS\disk.sys base address 801E7000
BD: \WINDOWS\system32\DRIVERS\CLASSPNP.SYS base address 801F0000
BD: \WINDOWS\system32\DRIVERS\fltMgr.sys base address 801FD000
BD: \WINDOWS\system32\DRIVERS\sr.sys base address 8021D000
BD: \WINDOWS\System32\Drivers\KSecDD.sys base address 8022F000
BD: \WINDOWS\System32\Drivers\Ntfs.sys base address 80246000
BD: \WINDOWS\System32\Drivers\NDIS.sys base address 802D3000
BD: \WINDOWS\System32\Drivers\Useless.sys base address 8000F000
BD: \WINDOWS\System32\Drivers\Mup.sys base address 80300000
BD: \WINDOWS\system32\DRIVERS\agp440.sys base address 8031B000
Shutdown occurred...unloading all symbol tables.
Waiting to reconnect...

Here the connection to the loader is closed. A new connection would be established if you had selected the Debug entry from Boot.ini, but what we have to note here, is the file path format used to load drivers during system boot. Notice that our test driver is among the drivers from the above list. Let us adopt this same file path format on our mapping file.

map
\WINDOWS\System32\Drivers\Useless.sys
Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys

After modifying the mapping file and save its contents to disk, we update the WinDbg, so it takes this change. Then, we will restart the system.

kd> .kdfiles Z:\Sources\DriverEntry\Useless\map.txt
KD file assocations loaded from 'Z:\Sources\DriverEntry\Useless\map.txt'
 
kd> .reboot
Shutdown occurred...unloading all symbol tables.
Waiting to reconnect...
BD: Boot Debugger Initialized
Connected to Windows Boot Debugger 3790 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.  (Initial Breakpoint requested)
Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
Module List address is NULL - debugger not initialized properly.
WARNING: .reload failed, module list may be incomplete
KdDebuggerData.KernBase < SystemRangeStart
Windows Boot Debugger Kernel Version 3790 UP Checked x86 compatible
Primary image base = 0x00000000 Loaded module list = 0x00000000
System Uptime: not available
The call to LoadLibrary(bootext) failed, Win32 error 0n2
    "The system cannot find the file specified."
Please check your debugger configuration and/or network access.

The same message sequence happens, but this time, the mapping is done as it should and we have the following output when the boot drivers are loaded.

BD: osloader.exe base address 00400000
BD: \WINDOWS\system32\ntoskrnl.exe base address 804EA000
BD: \WINDOWS\system32\hal.dll base address 806FF000
BD: \WINDOWS\system32\KDCOM.DLL base address 80720000
BD: \WINDOWS\system32\BOOTVID.dll base address 80010000
BD: \WINDOWS\system32\DRIVERS\ACPI.sys base address 80124000
BD: \WINDOWS\system32\DRIVERS\WMILIB.SYS base address 80001000
BD: \WINDOWS\system32\DRIVERS\pci.sys base address 80062000
BD: \WINDOWS\system32\DRIVERS\isapnp.sys base address 80003000
BD: \WINDOWS\system32\DRIVERS\compbatt.sys base address 8000C000
BD: \WINDOWS\system32\DRIVERS\BATTC.SYS base address 80013000
BD: \WINDOWS\system32\DRIVERS\intelide.sys base address 80017000
BD: \WINDOWS\system32\DRIVERS\PCIIDEX.SYS base address 80019000
BD: \WINDOWS\System32\Drivers\MountMgr.sys base address 80152000
BD: \WINDOWS\system32\DRIVERS\ftdisk.sys base address 8015D000
BD: \WINDOWS\System32\drivers\dmload.sys base address 80073000
BD: \WINDOWS\System32\drivers\dmio.sys base address 8017C000
BD: \WINDOWS\System32\Drivers\PartMgr.sys base address 801A2000
BD: \WINDOWS\System32\Drivers\VolSnap.sys base address 801A7000
BD: \WINDOWS\system32\DRIVERS\atapi.sys base address 801B4000
BD: \WINDOWS\system32\DRIVERS\vmscsi.sys base address 801CC000
BD: \WINDOWS\system32\DRIVERS\SCSIPORT.SYS base address 801CF000
BD: \WINDOWS\system32\DRIVERS\disk.sys base address 801E7000
BD: \WINDOWS\system32\DRIVERS\CLASSPNP.SYS base address 801F0000
BD: \WINDOWS\system32\DRIVERS\fltMgr.sys base address 801FD000
BD: \WINDOWS\system32\DRIVERS\sr.sys base address 8021D000
BD: \WINDOWS\System32\Drivers\KSecDD.sys base address 8022F000
BD: \WINDOWS\System32\Drivers\Ntfs.sys base address 80246000
BD: \WINDOWS\System32\Drivers\NDIS.sys base address 802D3000
KD: Accessing 'Z:\Sources\DriverEntry\Useless\objchk_wxp_x86\i386\Useless.sys'
 (\WINDOWS\System32\Drivers\Useless.sys)
  File size 2K.BD: Loaded remote file \WINDOWS\System32\Drivers\Useless.sys
 
BlLoadImageEx: Pulled \WINDOWS\System32\Drivers\Useless.sys from Kernel Debugger
BD: \WINDOWS\System32\Drivers\Useless.sys base address 8000F000
BD: \WINDOWS\System32\Drivers\Mup.sys base address 80300000
BD: \WINDOWS\system32\DRIVERS\agp440.sys base address 8031B000
Shutdown occurred...unloading all symbol tables.
Waiting to reconnect...

And later, our proof that the driver file has been successfully replaced.

Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.  (Initial Breakpoint requested)
Symbol search path is: SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
Windows XP Kernel Version 2600 UP Free x86 compatible
Built by: 2600.xpsp_sp2_gdr.070227-2254
Kernel base = 0x804ea000 PsLoadedModuleList = 0x8056d620
System Uptime: not available
This driver was built on Jul 16 2007 01:18:53

I assure you that this could still save your life if you had a File System filter with a bug in its DriverEntry on a customer's machine. The expression of panic when the customer sees his machine restarting in an endless loop is interesting, but keeping your job is a little more interesting.

See you!

One Response to “Bug on my Boot driver! Now what?”

  1. Scott says:

    Nice article! Note that for Win7 you can replace boot start drivers by enabling boot debug in the BCD:

    bcdedit /set bootdebug on

    -scott

Leave a Reply