WinDbg-kd, Windows Kernel Debugging¶
To debug a Windows kernel, here is what is needed:
WinDbg (for example with VisualStudio Express Edition)
A kernel booted in debug mode. For local debugging, the boot can be configured with these commands (on Windows<=7, the second one fails but WinDbg still supports local kernel debugging):
bcdedit /debug on # Since Windows 8, otherwise debugging is enabled on serial port COM2 bcdedit /dbgsettings local
(bcdedit
configures the Boot Configuration Database)
It is then possible to run windbg -kl
as administrator to start a Local
Kernel debugging session.
To verify whether local kernel debugging is enabled:
cd C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\
kdbgctrl -c
In order to configure kernel debugging on a virtual machine, it is possible to use network debugging, with a key which consists in 3 words separated with dots:
bcdedit /dbgsettings NET HOSTIP:172.16.0.1 PORT:50000 KEY:a.b.c.d
# On the debugger host (breaking happens in nt!DbgBreakPointWithStatus)
WinDbg -k net:port=52000,key:a.b.c.d,target:DESKTOP-AB01CDE
To load kernel symbols, it is possible to download a Windows Symbol Package from MSDN, but it is simpler to make WinDbg download them automatically. This can be done either with an environment variable (cf. https://support.microsoft.com/en-us/kb/311503):
_NT_SYMBOL_PATH=SRV*C:\DebugSymbols*http://msdl.microsoft.com/download/symbols
# To make it persistent:
setx _NT_SYMBOL_PATH srv*C:\DebugSymbols*http://msdl.microsoft.com/download/symbols
or with WinDbg commands:
.sympath SRV*C:\DebugSymbols*http://msdl.microsoft.com/download/symbols
.reload /f
Usual commands¶
Get help about a command:
.hh x
(examine),.hh uu
(unassemble)Examine symbols starting with
NtCreate
in the kernel:x nt!NtCreate*
.Display values at an address, with optional argument
L size
:db
: display hexadecimal bytes (128 by default)dc
: display DWORD values and their ASCII equivalents (32 by default)dd
: display DWORD values (32 by default)dp
: display ULONG_PTR values (128 bytes by default)dps
: display pointers and symbols (dp
with symbols)dpp
: likedps
, with an extra pointer dereferencedq
: display ULONG64_PTR values (128 bytes by default)du
: display UNICODE characters values (16 2-byte characters by default)dw
: display WORD values (64 by default)ds /c width addr
: display width characters at addrdS /c width addr
: display width unicode characters at addr
Disassemble the beginning of a function (use
L2a
to show 42 instructions):lkd> u nt!KiSystemCall64 nt!KiSystemCall64: fffff800`29f9d040 0f01f8 swapgs fffff800`29f9d043 654889242510000000 mov qword ptr gs:[10h],rsp fffff800`29f9d04c 65488b2425a8010000 mov rsp,qword ptr gs:[1A8h] fffff800`29f9d055 6a2b push 2Bh fffff800`29f9d057 65ff342510000000 push qword ptr gs:[10h] fffff800`29f9d05f 4153 push r11 fffff800`29f9d061 6a33 push 33h fffff800`29f9d063 51 push rcx
Display the types of fields of a structure:
dt nt!_KUSER_SHARED_DATA
x86-specific commands¶
Dump the Interrupt Descriptor Table, which contains Interrupt Service Routines:
lkd> !idt Dumping IDT: fffff8002b8e4080 00: fffff80029f9aa00 nt!KiDivideErrorFault 01: fffff80029f9ab00 nt!KiDebugTrapOrFault 02: fffff80029f9acc0 nt!KiNmiInterrupt Stack = 0xFFFFF8002B8FF000 03: fffff80029f9b040 nt!KiBreakpointTrap 04: fffff80029f9b140 nt!KiOverflowTrap 05: fffff80029f9b240 nt!KiBoundFault 06: fffff80029f9b340 nt!KiInvalidOpcodeFault 07: fffff80029f9b580 nt!KiNpxNotAvailableFault 08: fffff80029f9b640 nt!KiDoubleFaultAbort Stack = 0xFFFFF8002B8FD000 09: fffff80029f9b700 nt!KiNpxSegmentOverrunAbort 0a: fffff80029f9b7c0 nt!KiInvalidTssFault 0b: fffff80029f9b880 nt!KiSegmentNotPresentFault 0c: fffff80029f9b9c0 nt!KiStackFault 0d: fffff80029f9bb00 nt!KiGeneralProtectionFault 0e: fffff80029f9bc00 nt!KiPageFault 10: fffff80029f9bfc0 nt!KiFloatingErrorFault 11: fffff80029f9c140 nt!KiAlignmentFault 12: fffff80029f9c240 nt!KiMcheckAbort Stack = 0xFFFFF8002B901000 13: fffff80029f9c8c0 nt!KiXmmException 1f: fffff80029f964a0 nt!KiApcInterrupt 20: fffff80029f99fc0 nt!KiSwInterrupt 29: fffff80029f9ca80 nt!KiRaiseSecurityCheckFailure 2c: fffff80029f9cb80 nt!KiRaiseAssertion 2d: fffff80029f9cc80 nt!KiDebugServiceTrap 2f: fffff80029f96770 nt!KiDpcInterrupt 30: fffff80029f969a0 nt!KiHvInterrupt 31: fffff80029f96d10 nt!KiVmbusInterrupt0 32: fffff80029f97070 nt!KiVmbusInterrupt1 33: fffff80029f973d0 nt!KiVmbusInterrupt2 34: fffff80029f97730 nt!KiVmbusInterrupt3 35: fffff80029e53090 hal!HalpInterruptCmciService (KINTERRUPT fffff80029e53000) 50: ffffd0017e709bd0 ataport!IdePortInterrupt (KINTERRUPT ffffd0017e709b40) 60: ffffd0017e709d10 ataport!IdePortInterrupt (KINTERRUPT ffffd0017e709c80) 80: ffffd0017e709810 i8042prt!I8042MouseInterruptService (KINTERRUPT ffffd0017e709780) 81: ffffd0017e709590 ndis!ndisMiniportIsr (KINTERRUPT ffffd0017e709500) 90: ffffd0017e7096d0 i8042prt!I8042KeyboardInterruptService (KINTERRUPT ffffd0017e709640) 91: ffffd0017e709310 USBPORT!USBPORT_InterruptService (KINTERRUPT ffffd0017e709280) a0: ffffd0017e7091d0 serial!SerialCIsrSw (KINTERRUPT ffffd0017e709140) b0: ffffd0017e709e50 ACPI!ACPIInterruptServiceRoutine (KINTERRUPT ffffd0017e709dc0) b1: ffffd0017e709a90 storport!RaidpAdapterInterruptRoutine (KINTERRUPT ffffd0017e709a00) HDAudBus!HdaController::Isr (KINTERRUPT ffffd0017e7093c0) ce: fffff80029e53a90 hal!HalpIommuInterruptRoutine (KINTERRUPT fffff80029e53a00) d1: fffff80029e53890 hal!HalpTimerClockInterrupt (KINTERRUPT fffff80029e53800) d2: fffff80029e53790 hal!HalpTimerClockIpiRoutine (KINTERRUPT fffff80029e53700) d7: fffff80029e53590 hal!HalpInterruptRebootService (KINTERRUPT fffff80029e53500) d8: fffff80029e53390 hal!HalpInterruptStubService (KINTERRUPT fffff80029e53300) df: fffff80029e53290 hal!HalpInterruptSpuriousService (KINTERRUPT fffff80029e53200) e1: fffff80029f97aa0 nt!KiIpiInterrupt e2: fffff80029e53490 hal!HalpInterruptLocalErrorService (KINTERRUPT fffff80029e53400) e3: fffff80029e53190 hal!HalpInterruptDeferredRecoveryService (KINTERRUPT fffff80029e53100) fd: fffff80029e53990 hal!HalpTimerProfileInterrupt (KINTERRUPT fffff80029e53900) fe: fffff80029e53690 hal!HalpPerfInterrupt (KINTERRUPT fffff80029e53600)
Read a MSR register, like STAR (syscall target):
lkd> rdmsr c0000082 msr[c0000082] = fffff800`29f9d040 kd> u fffff800`29f9d040 L1 nt!KiSystemCall64: fffff800`29f9d040 0f01f8 swapgs
To get the Kernel Processor Control Region (KPCR), use
fs_base
MSR (0xc0000100) on x86-32 andgs_base
(0xc0000101) on x86-64:lkd> rdmsr c0000101 msr[c0000101] = ffffd001`aef40000 lkd> dt nt!_KPCR ffffd001aef40000
The GDT (Global Descriptor Table) can be read by a remote debugger with
r gdtr
and the detail of each descriptors can be seen with commands such asdp @cs
. When using a local debugger, it is nevertheless possible to compile a userland program which shows the result ofsgdt
instruction, which is not privileged, and then use the pointer in WinDBG withnt!_KGDTENTRY
(ornt!_KGDTENTRY64
) structure.
NatVis and JavaScript (since Windows 10)¶
Since Windows 10, WinDbg supports NatVis (Native Visualization, which was added to Visual Studio 2013).
This allows such a syntax to dump a structure such as an EPROCESS
:
lkd> dx *(nt!_EPROCESS**)&nt!PsInitialSystemProcess
*(nt!_EPROCESS**)&nt!PsInitialSystemProcess : 0xffff9c8708c83040 [Type: _EPROCESS *]
[+0x000] Pcb [Type: _KPROCESS]
[+0x2e0] ProcessLock [Type: _EX_PUSH_LOCK]
[+0x2e8] UniqueProcessId : 0x4 [Type: void *]
[+0x2f0] ActiveProcessLinks [Type: _LIST_ENTRY]
[+0x300] RundownProtect [Type: _EX_RUNDOWN_REF]
...
NatVis files are in C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\Visualizers
(for example there is stl.natvis
in order to represent types such as
std::string
, std::list
, std::map
, etc.)
NatVis commands:
# Define variables
dx @$myVar = "My string"
dx @$myVar
dx @$myVar.Length
# Show methods
dx -v @$myVar
# Show a variable with 2 levels of recursion
dx -r2 @$myVar
# Create an array from memory
dx @$testArray = (int(*)[10])0x7ffe0010
dx @$testPointersArray = (int*(**)[10])0x7ffe0010
# Copy a variable into some memory
dx *(TYPE*)0x10000 = *@$myNewContent
# Show the processor blocks
dx (nt!_KPRCB**)&nt!KiProcessorBlock, [*(int*)&nt!KeNumberProcessors]
# Enumerate all variables
dx @$vars
# Remove a variable
@$vars.Remove("myVar")
# Create an anonymous structure
dx @$myobject = new { Type = 5, Name = "Process Object" }
# Create a new function (with anonymous function syntax)
dx @$mul = (num1, num2) => num * num2
# Load a NatVis file
.nvload C:\path\to\myfile.natvis
In WinDbg, there are 3 default NatVis variables: @$curprocess
,
@$curthread
and @$cursession
:
lkd> dx @$cursession
@$cursession : Local KD
Processes
Devices
lkd> dx @$cursession.Processes
@$cursession.Processes
[0x0] : <Unknown Image>
[0x4] : <Unknown Image>
[0x38] : <Unknown Image>
[0x68] : <Unknown Image>
[0x1bc] : smss.exe
With LINQ (Language INtegrated Query) it is possible to process results using SQL-like functions:
# Find processes named smss.exe
dx @$cursession.Processes.Where(p=>p.Name == "smss.exe")
# Get the name of the process
dx @$cursession.Processes.Where(p=>p.Name == "csrss.exe").First()
.KernelObject.SeAuditProcessCreationInfo.ImageFileName->Name
# List processes with their protection levels
dx -g @$cursession.Processes.Where(p=>p.KernelObject.Protection.Level != 0)
.Select(p => new{name=p.Name, Level=p.KernelObject.Protection.Level})
# Get the names of handles opened by csrss
dx -r2 @$cursession.Processes.Where(p=>p.Name=="csrss.exe").Select(
p => p.Io.Handles.Select(h => h.ObjectName))
In order to build complex structures from data, there are some helpers:
dx -r0 @$processList = *(nt!_LIST_ENTRY*)&nt!PsActiveProcessHead
dx -r0 @$processes = Debugger.Utility.Collections.FromListEntry(
@$processList, "nt!_EPROCESS", "ActiveProcessLinks")
dx Debugger.Utility.Control.ExecuteCommand("!filetime 0").First()
Using JavaScript provider:
.load jsprovider.dll
# call initializeScript()
.scriptload C:\path\to\MyScriptName.js
# like .scriptload and call invokeScript()
.scriptrun C:\path\to\MyScriptName.js
dx Debugger.State.Scripts.MyScriptName.Contents.my_function(args)
dx @$scripts.MyScriptName.Contents.my_function(args)
dx @$scriptContents.my_function(args)
# call uninitializeScript() and unload
.scriptunload MyScriptName.js
In JavaScript:
host
is a COM object for the Deguggerhost.memory
represents the target memory (methodreadMemoryValues(ptr, size)
returns anArrayBuffer
with memory)host.diagnostics.debugLog("...")
prints logshost.typeSystem.marshalAs(variable, "nt", "SOME_TYPE")
to castnew host.metadata.valueWithMetadata(myInteger, {PreferredRadix:10})
to print an integer as decimalhost.evaluateExpression(expr)
orhost.namespace.Debugger.Utility.Control.ExecuteCommand
to run a debugger command (like.printf %y
to convert a pointer to a symbol using MASM syntax)
To add new aliases in Javascript:
function initializeScript()
{
return [new host.functionAlias(my_function, "myfct") /*, ... */];
}
// In WinDBG:
// !myfct arg1, arg2
// dx @$myfct(arg1, arg2)
Links¶
https://msdn.microsoft.com/en-us/library/windows/hardware/ff551063(v=vs.85).aspx Debugging Tools for Windows (WinDbg, KD, CDB, NTSD)
https://msdn.microsoft.com/en-us/windows/hardware/gg463028 Download Windows Symbol Packages
https://msdn.microsoft.com/en-us/library/windows/hardware/ff551891(v=vs.85).aspx Debugger Commands - Kernel-Mode Extensions
http://windbg.info/doc/1-common-cmds.html Common WinDbg Commands
https://github.com/microsoft/WinDbg-Samples Sample extensions, scripts, and API uses for WinDbg