Getting started with Windows kernel development

I’m diving into Windows development here in MSR Redmond’s OS group. I have mainly a Linux background and know much less about Windows, so I’ve been taking notes. I’ll be publishing an assortment of these notes/tips. Today I’ll talk about kernel-mode development and debugging basics.

Windows kernel module/driver binaries are .sys files (like .ko files on Linux), which may be accompanied by a .pdb file containing the debug symbols for the binary. Kernel Programming 101 is a nice introductory tutorial on how to write and build these drivers, but the section on loading them manually by editing the registry doesn’t work in Windows 7 (perhaps this was XP-and-earlier). You should use the DriverLoader tool from OSR Online to register your driver as a service and start it; it probably uses the Windows API to accomplish this. By the way, OSR Online is a useful website and community for Windows kernel hacking that puts out other tools as well.

windbg (“windbag”) is a handy multi-purpose debugger, included in the Windows Driver Kit. It used to be a userland-only debugger, whereas kd was the kernel debugger, but all debugging functionality has been merged into one place, used by both windbg and ntsd. ntsd is a command-line debugger, whereas windbg has a GUI and a bunch of other nifty features, such as the ability to pull symbols (those .pdb files) and sources automatically from symbol servers and source servers. I used to use windbg to analyze those crash dumps that are produced whenever you get a BSOD; the stack trace may provide hints as to the source of the problem.

In the 64-bit versions of Windows Vista and Windows 7, you can normally only load signed drivers. To disable this requirement, start Windows in debug mode. Typically you’ll want to debug a Windows environment running in a VM like Virtual PC. To debug Windows over the virtual COM serial port, the first step is to run the following from an Administrator cmd and reboot:

bcdedit -debug on
bcdedit -dbgsettings serial debugport:1 baudrate:115200

bcdedit is a program that edits boot settings, which once upon a time were configured in a file called boot.ini. There are alternative instructions on the web mentioning other things to try with bcdedit, which I haven’t looked carefully at, but if you’re doing kernel development then you may want debugging mode turned on anyway.

The next step is to go to your VM settings and configure the COM1 port to map to a named pipe, \\.\pipe\<pipe name>. Now you can start the debugger:

set _NT_SYMBOL_PATH=srv*c:\syms*\\symbols\symbols
windbg -k com:port=\\<vpc_host_machine>\pipe\<pipe name>,pipe,resets=0,reconnect

The environment variables tell windbg about the servers from which to automatically pull symbols/sources; you’ll need to adjust this, since \\symbols\symbols is a Microsoft-internal share. resets=0 is required for Virtual PC; use resets=2 for a competing VM product. reconnect waits for a named pipe if it’s not found on the target and waits to reconnect if disconnected. vpc_host_machine is the name of the host running the VM; if you’re debugging locally, use \\.\pipe\<pipe name>.

Side note: symbol files contain information about variable and function names. Public symbols include globals, while private symbols include everything else (locals, structs, etc.). Full symbol files contain both, whereas stripped symbol files contain just public symbols. Microsoft’s Internet symbol server is at To use it, you can just leave the symbol server variable alone; I believe the default behavior is to use this public server. (I also believe the .symfix windbg command without arguments restores the default symbol path.) In my case, to also allow symbols for my own driver to be resolved, I needed to set the symbol path to the following:


To break down what just happened: the symbol path syntax consists of multiple semi-colon-separated paths, each of which can be a local path, a cache, or a symbol server. Here are some example components:


uses C:\syms as a cache for all components to its right.


uses http://symserver/symbols as a symbol server. This can also be a share, i.e. \\symserver\symbols.


are (I think) equivalent, and use C:\syms as a local cache for symbols from the remote symbol server. You can specify any number of cascading intermediate caches:


When debugging issues with symbols, use !sym noisy to enable verbose symbol debugging info.

The source path is used differently. I’m not sure how source lookup works for built-in Windows modules, but when debugging your own drivers, the .sys and .dll files themselves contain the full local paths to the source files, so that you can list and step through your code out-of-the-box if you’re running windbg from the same machine where you built.

Anyway, back to actual debugging: you can attach to the system by restarting the VM. Note that windbg can only attach when the system starts up; you can’t attach to a running system. Once you’re in the debugger, you can hit ctrl-break to immediately break the target machine. You can try setting a breakpoint with bp, e.g., bp nt!NtReadFile. The word before the ! is the name of the module/driver (in this case we’re addressing the kernel), and the word after it is the name of the subroutine to break in. Your symbol path must be set up correctly to resolve these function names to addresses. You can also set breakpoints in your own driver, such as with bp mydriver!DriverEntry. It’s OK to set this before the driver is even loaded; bp will automatically behave like bu, which creates an unresolved breakpoint, to be resolved once the matching module is loaded. Note that if you update your sources while they’re open in windbg, you must close the file to refresh it.

To continue, enter g. Play around with the debugger, and consult the documentation – the WDK is well-documented.

Follow me on Twitter for stuff far more interesting than what I blog.