WinDbg cheatsheet
Content
- WinDbg cheatsheet
Setup
Symbol Path
In a command prompt:
C:\> setx _NT_SYMBOL_PATH srv*C:\Symbols*https://msdl.microsoft.com/download/symbolsIn WinDbg, Ctrl+S then
srv*C:\Symbols*https://msdl.microsoft.com/download/symbolsProviders
In WinDbg
0:000> .scriptproviders
Should display something like
Available Script Providers:
NatVis (extension '.NatVis')
JavaScript (extension '.js')
VS Code linting
Download JsProvider.d.ts to the root of your script and add the following at its top:
/// <reference path="JSProvider.d.ts" />
"use strict";Kernel Debugging
- Increase the kernel verbosity level from calls to
KdPrintEx()- temporarily during runtime from WinDbg (lost once session is closed)
kd> ed nt!Kd_Default_Mask 0xf
- permanently from registry hive (in Admin prompt on Debuggee)
C:\> reg add "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Debug Print Filter" /v DEFAULT /t REG_DWORD /d 0xf
Commands
Basic commands
| Action | Command | Examples |
|---|---|---|
| Help / Manual | .hh <command> |
.hh .hh !process |
| Clear screen | .cls |
|
| Dynamic evaluation | ? |
? 40004141 – nt ? 2 + 2 ? nt!ObTypeArrayIndex |
| Comment | $$ |
$$ this is a useful comment |
| Print a string | .echo |
.echo "Hello world" |
| Print a formatted string (see printf formatters) |
.printf |
.printf "Hello %ma\n" , @$esp |
| Command separator | ; |
command1 ; command2 |
| Attach (Detach) to (from) process | .attach |
.detach |
| Display parameter value under different formats (hexadcimal, decimal, octal) | .formats |
.formats 0x42 |
| Change default base | n |
n 8 |
| Quit WinDbg (will kill the process if not detached) | q |
|
| Restart debugging session | .restart |
|
| Reboot system (KD) | .reboot |
.printf formatters
| Description | Formatter | Examples |
|---|---|---|
| ASCII C string (i.e. NULL terminated) | %ma |
|
| Wide C string (i.e. NULL terminated) | %mu |
|
| UNICODE_STRING** string | %msu |
|
| Print the symbol pointed by address | %y |
.printf “%y\n”,ffff8009bc2010 // returns nt!PsLoadedModuleList |
| Print a Pointer | %p |
.printf “%p\n”,nt!PsLoadedModuleList // returns 0xffff8009bc2010 |
Execution flow
| Action | Command | Examples |
|---|---|---|
| Start or resume execution (go) | g |
|
| Dump register(s) | r |
r r eax r rax=42 |
| Step over | p |
pa 0xaddr (step over until 0xaddr is reached) pt (step over until return) pc (step over until next call) |
| Step into | t |
Same as above, replace p with t |
| Execute until reaching current frame return address (go upper) | gu |
|
| List module(s) | lm |
lm (UM: display all modules) lm (KM: display all drivers and sections) lm m *MOD* (show module with pattern 'MOD' ) |
| Get information about current debugging status | .lastevent!analyze |
|
| Show stack call | kkp |
Registers / Memory access
| Action | Command | Examples |
|---|---|---|
| Read memory As | bytes: dbword: dwdword: ddqword: dqpointer: dpunicode string: dW |
db @sp 41 41 41 41dw @ripdd @rax l4dyb @ripdps @espdW @rsp |
| Write memory As | bytes: ebword: ewdword: edqword: eqascii string: eaUnicode string: eu |
ea @pc "AAAA" |
| Read register(s) | rr [[REG0],REG1,...] |
r rax,rbp |
| Write register(s) | r [REG]=[VALUE] |
r rip=4141414141414141 |
| Show register(s) modified by the current instruction | r. |
|
| Dump memory to file | .writemem |
.writemem C:\mem.raw @eip l1000 |
| Load memory from file | .readmem |
.readmem C:\mem.raw @rip l1000 |
| Dump MZ/PE header info | !dh |
!dh kernel32!dh @rax |
| Read / write physical memory (syntax similar to dX/eX commands) |
!db / !eb !dw / !ew !dd / !ed !dq / !eq |
|
| Fill / Compare memory | fc |
f @rsp l8 41c @rsp l8 @rip |
| Dereference memory | poi(<AddrOrSymbol>): dereference pointer sizedwo(): dereference DWORDqwo(): dereference QWORD |
db poi( @$rax ) |
Memory search
| Action | Command | Examples |
|---|---|---|
| Search | byte: s [RANGE] [VALUE]dword: s -d [RANGE] [DWORD_VALUE] |
s @eip @eip+100 90 90 90 ccs -d @eax l100 41424344 |
| Search ASCII (Unicode) | s –a <AddrStart> L<NbByte> "Pattern"s –a <AddrStart> <AddrEnd> "Pattern"(for Unicode – change –a with –u) |
|
| Search for pattern in command | .shell |
.shell -ci "<windbg command>" batch command.shell -ci "!address" findstr PAGE_EXECUTE_READWRITE |
Breakpoints
| Action | Command | Examples |
|---|---|---|
| Examine | x |
x nt!*CreateProcess* |
| Display types | dt |
dt ntdll!_PEB @$pebdt ntdll!_TEB –r @$teb |
| Display Type Extended - with Debugger Object Model | dtx |
dtx nt!_PEB 0x000008614a7a000which is equivalent to dx (nt!_PEB*)0x000008614a7a000 |
| Set breakpoint | bp bp 0xaddr (or mod!symbol) |
|
| List breakpoints | bl |
|
| Disable breakpoint(s) | bd [IDX] (IDX is returned by bl) |
bd 1bd * |
| Delete breakpoint(s) | bc [IDX] (IDX is returned by bl) |
bc 0bc * |
| (Un)Set exception on event | sx |
sxe ld mydll.dll |
| Break on memory access | ba |
ba r 4 @esp |
| Define breakpoint command | bp … [Command]Where [Command] can be - an action: " r ; g"- a condition: " .if (@$rax == 1) {.printf \"rcx=%p\\\n\", @rcx }" |
bp kernel32!CreateFileA "da @rcx; g" " |
| Enable breakpoint after N hit(s) | bp <address> N+1 |
bp /1 0xaddr (temporary breakpoint)bp 0xaddr 7 (disable after 6 hits) |
| Set "undefined" breakpoint | bu <address> |
Symbols
| Action | Command | Examples |
|---|---|---|
| Examine | x |
x /t /v ntdll!*CreateProcess* |
| Display types | dt |
dt ntdll!_PEB @$peb |
| List nearest symbols | ln |
ln 0xaddr |
| Set/update symbol path | .sympath |
|
| Load module symbols | ld |
ld Moduleld * |
Convenience variables and functions
| Action | Command | Examples |
|---|---|---|
| Program entry point | $exentry |
bp $exentry |
| Process Environment Block | $peb |
dt _PEB @$peb |
| Thread Environment Block | $teb |
dt _TEB @$teb |
| Return Address | $ra |
g @ra |
| Instruction Pointer | $ip |
|
| Size of Page | $pagesize |
|
| Size of Pointer | $ptrsize |
|
| Process ID | $tpid |
|
| Thread ID | $tid |
Useful extensions
| Action | Command | Examples |
|---|---|---|
| Detailed information about loaded DLLs | !dlls!dlls -I (show load order)!dlls -c 0xaddr (show DLL containing0xaddr) |
|
| Get mapping information | !address |
!address -f:MEM_COMMIT |
| Change verbosity of symbol loader | !sym |
!sym noisy!sym quiet |
| Dump PEB/TEB information | !peb !teb |
|
| Analyze the reason of a crash | !analyze |
!analyze -v |
| Convert an NTSTATUS code to text | !error |
!error c0000048 |
| Perform heuristic checks to the exploitability of a bug |
!exploitable |
|
| Encode/decode pointer encoded by KernelBase API EncodePointer() |
!encodeptr32 (or 64)!decodeptr32 (or 64) |
|
| Display the current exception handler | !exchain |
|
| Dump UM heap information | !heap |
.NET Debugging
| Action | Command | Examples |
|---|---|---|
| Load the CLR extensions | .loadby sos clr |
sxe ld clr; g to make sure clr.dll is loaded, then .loadby sos clr |
| Get help | !help |
|
| Set managed code breakpoint | !bpmd <module> Path.To.Function |
!bpmd mscorlib.dll System.Reflection.Assembly.Load !bpmd System.dll System.Diagnostics.Process.Start !bpmd System.dll System.Net.WebClient.DownloadFile |
| List all managed code breakpoints | !bpmd -list |
|
| Clear specific managed code breakpoint | !bpmd -clear $BreakpointNumber |
|
| Clear all managed code breakpoints | !bpmd -clearall |
|
| Dump objects | !DumpObj |
!DumpObj /d 0x<address> |
| Dump the .NET stack | !CLRStack |
!CLRStack -p |
LINQ & Debugger Data Model
| Variable description | Command | Examples |
|---|---|---|
| Create a variable | dx @$myVar = VALUE |
dx @$ps = @$cursession.Processes |
| Delete a variable | dx @$vars.Remove("VarName") |
dx @$vars.Remove("ps") |
| List user defined variable | dx @$vars dx Debugger.State.UserVariables |
|
Bind address Address to a N-entry array of type T |
dx (T* [N])0xAddress |
dx (void** [5]) Debugger.State.PseudoRegisters.General.csp |
| Function description | Command | Examples |
| :--- | --- | --- |
| Create a "lambda" inline function | dx @$my_function = ([arg0, arg1] => Code) |
dx @$add = (x, y => x + y) |
| Filtering objects | [Object].Where( [FILTER PATTERN] ) |
dx @$cursession.Processes.Where( x => x.Name == "notepad.exe") |
| Sorting objects | - asc: [Object].OrderBy([Sort Expression])- desc: [Object].OrderByDescending([Sort Expression]) |
dx @$cursession.Processes.OrderByDescending(x => x.KernelObject.UniqueProcessId) |
| Projecting | .Select( [PROJECTION KEYS] ) |
.Select( p => new { Item1 = p.Name, Item2 = p.Id } ) |
Access n-th element of iterable |
$Object[n] |
@$cursession.Processes[4] |
Get the number of objects in iterable |
$Object.Count() |
@$cursession.Processes.Count() |
Create a iterator from a LIST_ENTRY structure |
dx Debugger.Utility.Collections.FromListEntry(Address, TypeAsString, "TypeMemberNameAsString") |
dx @$ProcessList = Debugger.Utility.Collections.FromListEntry( *(nt!_LIST_ENTRY*)&(nt!PsActiveProcessHead), "nt!_EPROCESS", "ActiveProcessLinks") dx @$HandleList = Debugger.Utility.Collections.FromListEntry( *(nt!_LIST_ENTRY*)&(nt!PspCidTable), "nt!_HANDLE_TABLE", "HandleTableList") |
Apply a structure S to memory (dt-like) |
dx (S*)0xAddress |
dx (nt!_EPROCESS*)&@$curprocess.KernelObject |
WinDbg JavaScript reference
| Action | Command | Examples |
|---|---|---|
| Print message | host.diagnostics.debugLog(Message) |
|
| Read data from memory | host.memory.readMemoryValues(0xAddr, Length) |
|
| Read string from memory | host.memory.readString(0xAddr)host.memory.readWideString(0xAddr) |
|
| Evaluate expression | host.evaluateExpression([EXPR]) |
var res=host.evaluateExpression("sizeof(_LIST_ENTRY)")dx @$scriptContents.host.evaluateExpression("sizeof(_LIST_ENTRY)") |
| Resolve symbol | host.getModuleSymbolAddress(mod, sym) |
var pRtlAllocateHeap = host.getModuleSymbolAddress('ntdll', 'RtlAllocateHeap'); |
| Dereference a pointer as an object | host.createPointerObject(...).dereference() |
var pPsLoadedModuleHead = host.createPointerObject(host.getModuleSymbolAddress("nt", "PsLoadedModuleList"), "nt", "_LIST_ENTRY *"); |
| Create typed variable from address | host.createTypedObject(addr, module, symbol) |
var loader_data_entry = host.createTypedObject(0xAddress,"nt","_LDR_DATA_TABLE_ENTRY") |
| Dereference memory | host.evaluateExpression('(int*)0xADDRESS').dereference() |
|
| Get access to the Pseudo-Registers | host.namespace.Debugger.State.PseudoRegisters |
var entrypoint = host.namespace.Debugger.State.PseudoRegisters.General.exentry.address; |
| Execute WinDbg command | host.namespace.Debugger.Utility.Control.ExecuteCommand |
var modules=host.namespace.Debugger.Utility.Control.ExecuteCommand("lm"); |
| Set Breakpoint | host.namespace.Debugger.Utility.Control.SetBreakpointAtSourceLocationhost.namespace.Debugger.Utility.Control.SetBreakpointAtOffsethost.namespace.Debugger.Utility.Control.SetBreakpointForReadWrite |
|
Iterate through LIST_ENTRYs |
host.namespace.Debugger.Utility.Collections.FromListEntry() |
var process_iterator = host.namespace.Debugger.Utility.Collections.FromListEntry( pAddrOfPsActiveProcessHead, "nt!_EPROCESS", "ActiveProcessLinks") |
Dealing with host.Int64
| Action | Command | Examples |
|---|---|---|
Create/Convert an Int64 object |
host.parseInt64('value')host.parseInt64('value', 16 ) |
host.parseInt64('42');host.parseInt64('0x1337', 16); |
| Add / Subtract | [Int64Obj].add($int)[Int64Obj].subtract($int) |
var NextPage = BasePage.add(0x1000);var NextPage = BasePage.subtract(0x1000); |
| Multiply / Divide | [Int64Obj].multiply($int)[Int64Obj].divide($int) |
|
| Compare | [Int64Obj1].compareTo([Int64Obj2]) |
BasicBlock.StartAddress.compareTo(Address1) <= 0 |
| Bitwise operation | and: [Int64Obj].bitwiseAnd($int)or: [Int64Obj].bitwiseOr($int)xor: [Int64Obj].bitwiseXor($int)lsh: [Int64Obj].bitwiseShiftLeft($shift)rsh: [Int64Obj].bitwiseShiftRight($shift) |
var PageBase = Address.bitwiseAnd(0xfffff000);Address.bitwiseShiftLeft(12).bitwiseShiftRight(12); |
Convert Int64 to native number |
- with exception if precision loss: [Int64Obj].asNumber()- no exception if precision loss: [Int64Obj].convertToNumber() |
WinDbg gallery skeleton
Only 3 files are needed (see [5] for more details):
config.xml
<?xml version="1.0" encoding="UTF-8"?>
<Settings Version="1">
<Namespace Name="Extensions">
<Setting Name="ExtensionRepository" Type="VT_BSTR" Value="Implicit"></Setting>
<Namespace Name="ExtensionRepositories">
<Namespace Name="My Awesome Gallery">
<Setting Name="Id" Type="VT_BSTR" Value="any-guid-will-do"></Setting>
<Setting Name="LocalCacheRootFolder" Type="VT_BSTR" Value="\absolute\path\to\the\xmlmanifest\directory"></Setting>
<Setting Name="IsEnabled" Type="VT_BOOL" Value="true"></Setting>
</Namespace>
</Namespace>
</Namespace>
</Settings>ManifestVersion.txt
1
1.0.0.0
1
Manifest.X.xml(whereXis the version number, let's just use1so it isManifest.1.xml)
<?xml version="1.0" encoding="utf-8"?>
<ExtensionPackages Version="1.0.0.0" Compression="none">
<ExtensionPackage>
<Name>Script1</Name>
<Version>1.0.0.0</Version>
<Description>Description of Script1.</Description>
<Components>
<ScriptComponent Name="Script1" Type="Engine" File=".\relative\path\to\Script1.js" FilePathKind="RepositoryRelative">
<FunctionAliases>
<FunctionAlias Name="AliasCreatedByScript`">
<AliasItem>
<Syntax><![CDATA[!AliasCreatedByScript]]></Syntax>
<Description><![CDATA[Quick description of AliasCreatedByScript.]]></Description>
</AliasItem>
</FunctionAlias>
</FunctionAliases>
</ScriptComponent>
</Components>
</ExtensionPackage>
</ExtensionPackages>Then in WinDbg load & save:
0:000> .settings load \path\to\config.xml
0:000> .settings save
Time-Travel Debugging
| Action | Command | Examples |
|---|---|---|
| DDM Objects | @$curprocess.TTD@$cursession.TTD |
dx @$curprocess.TTD.Threads.First().Lifetime dx @$cursession.TTD.Calls("ntdll!Nt*File").Count() |
| Run execution back | g- |
|
| Reverse Step Over | p- |
|
| Reverse Step Into | t- |
|
| Regenerate the index | !ttdext.index |
|
Jump to position XX:YY (WinDbg) |
!tt XX:YY |
!tt 1B:0 |
Jump to position XX:YY (DDM) |
<TtdPosition>.SeekTo() |
dx @$curprocess.TTD.Lifetime.MinPosition.SeekTo() |