Home » Posts tagged 'Windbg'

Tag Archives: Windbg

Windbg: Installation, Interface, Symbols, Remote/Local Debugging, Help, Modules, and Registers


Microsoft has changed things slightly in WinDBG’s installation from Windows 7 to Windows 8. In this section we’ll walk through the install on both.

Windows 8

For Windows 8, Microsoft includes WinDBG in the Windows Driver Kit (WDK) You can install Visual Studio and the WDK or just install the standalone "Debugging Tools for Windows 8.1" package that includes WinDBG.
This is basically a thin installer that needs to download WinDBG after you walk through a few screens. The install will ask you if you’d like to install locally or download the development kit for another computer. The later will be the equivalent of an offline installer, which is my preference so that you can install on other systems easily in the future.

From there just Next your way to the features page and deselect everything but "Debugging Tools for Windows" and click "Download".

Once the installer completes you can navigate to your download directory, which isc:\Users\Username\Downloads\Windows Kits\8.1\StandaloneSDK by default, and then next through that install. Then you’re all ready to go!

Windows 7 and Below

For Windows 7 and below, Microsoft offers WinDBG as part of the "Debugging Tools for Windows" package that is included within the Windows SDK and .Net Framework. This requires you to download the online/offline installer, then specifically choose the "Debugging Tools for Windows" install option.
My preference is to check the "Debugging Tools" option under "Redistributable Packages" and create a standalone installer which makes future debugging efforts a heck of lot easier. That’s what I’ll do here.

Once the installation completes, you’ll should have the redistributable for various platforms (x86/x64) in the c:\Program Files\Microsoft SDKs\Windows\v7.1\Redist\Debugging Tools for Windows\ directory.

From there the installation is pretty simple, just copy the appropriate redistributable to the system you’re debugging and then click through the installation.


When you run WinDBG for the first time, you’ll realize that its intimidatingly simple. Most of WinDBG’s interface is experienced while you’re actually debugging a process. So you’re not going to do to much with WinDBG until you attach it to a process. Rather then having a section dedicated to the interface (too late!) we’ll point out the important parts in the upcoming sections.
The most basic thing about the interface you should know is the Command window. It’s the default window opened once you’re attached to a process. The Command window is mostly an output only window, with a small input field on the bottom which you’ll enter commands into to control WinDBG.


WinDBG doesn’t really need much of a configuration, most things work right out of the box. The one important thing to do is set up Symbols. Symbols are basically special files that are generated with the program binary at compile time that provide debugging information such as function and variable names. This can really help demystify a lot of the functionality of an application when debugging or disassembling. Many Microsoft components are compiled with Symbols which are distributed via the Microsoft Symbol Server. For non-Microsoft binaries, you’re usually out of luck – sometimes you’ll find them laying around somewhere but mostly all companies keep that stuff protected.
To configure WinDBG to use the Microsoft Symbol server go to File:Symbol File Path and set the path appropriately to the one below. The syntax is a little weird, asterisks are the delimiter, so in the value below, we’ll download symbols to the C:\Symbols directory.


WinDBG will automatically load Symbols for binaries that it has them for when needed. To add a file containing symbols you can just append it to the path:


Adding Symbols during Debugging

If you do run into a situation where you have Symbols and would like to import them while the debugging, you can do so via the .sympath command option within the command window (this requires you to be attached to a process). For instance to append c:\SomeOtherSymbolFolder you can:

0:025> .sympath+ c:\SomeOtherSymbolFolder
Symbol search path is: SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols;c:\SomeOtherSymbolFolder
Expanded Symbol search path is: srv*c:\symbols*http://msdl.microsoft.com/download/symbols;c:\someothersymbolfolder

It’s always good to reload the symbols after you make changes to the path:

0:025> .reload
Reloading current modules

Checking Symbols

To view what modules have symbols loaded, you can use the x*! command. However, WinDBG doesn’t load Symbols until it needs them so x*! will show most of the module symbols are deferred. We can force WinDBG to load symbols, with the ld * command (which may take a little time, you can stop it by going to Debug:Break):

Now we can view the symbols for each for the modules:

Debugging a Local Process

You have a couple options when debugging a local process. You can start the process then attach to it, or have WinDBG launch the process for you. I’m really sure of all the advantages/disadvantages of each – I know that when you launch a program with WinDBG, it enables some special debugging options (e.g. debug heap) that the program may not like, and it will crash. Additionally, those debugging options alters the code paths in which the programs takes which modifies how things are arranges in memory. That being said, there are also programs that will crash when you attach the debugger, so ymmv. Some applications (malware in particular) will look for the presence of the debugger at launch and may not later on, which would be a reason why you’d attach. And sometimes you’re debugging a service that is controlled by Windows which sets up a variety of things during its launch, so to simplify things, you’d attach rather then launch via the debugger. Some people say there is a significant performance impact when launching a process via the debugger. Test it out yourself, and see what works best for you. If you have any particular reasons why you’d do one over the other, please let me know the comments!

Starting a Process

If you’re debugging a self contained application that just runs locally and doesn’t communicate via the network, you may want to have WinDBG start the application. However, that’s not to say you can’t attach to these programs after they’ve been launched.

Starting a process is pretty straight forward, go to "File:Open Executable". From there, select the executable you’d like to debug. You can also provide command line arguments and define the start directory:

Attaching to a Process

Attaching to an already running process is just as simple. Note, that in some cases, you’ll need to need to spend a little time identifying the true process you’re looking to target. For instance, some web browsers will create one parent process, then an additional process for each tab. So depending on the crash you’re debugging, you might want to attach to the tab process rather than the parent.

To attach to an already existing process, go to "File:Attach to a Process" then select the PID or process name to attach to. Keep in mind you’ll also need the appropriate rights to attach to your target process.

If the program has stopped responding, you can noninvasively by using the "Noninvaise" checkbox.

Debugging a Remote Process

Now there may be times where you have to debug a process on a remote system. For instance, it may just be more convenient to use a local debugger rather than one within a VM or via RDP. Or perhaps you are debugging LoginUI.exe – which is only available while the system is locked. In these situations you can have a local WinDBG instance running then remotely connect to it. There are a couple ways to do this as well – we’ll cover two of the most common ways.

Existing Debugging Sessions

If you’ve already started to debug the program locally (via attaching or launching mentioned above) you can use the command input field to have WinDBG launch a listener that a remote debugger can connect to. This is done with the .server command:

.server tcp:port=5005

You’ll likely get a security alert that you should allow:

Then a positive message within WinDBG telling you the server has started:

0:005> .server tcp:port=5005
Server started.  Client can connect with any of these command lines
0: <debugger> -remote tcp:Port=5005,Server=USER-PC

Then from the remote host, you can connect to the existing debugging session via "File:Connect to a Remote Session":


Once connected you’ll get a confirmation on remote client:

Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.

Server started.  Client can connect with any of these command lines
0: <debugger> -remote tcp:Port=5005,Server=USER-PC
MACHINENAME\User (tcp connected at Mon Dec 16 09:03:03 2013

and the locally debugging instance:

MACHINENAME\User (tcp connected at Mon Dec 16 09:03:03 2013

Remote Server

You can also have a standalone WinDBG server running on a system, remotely connect to it, then have the ability to select what process to attach to. This can be done using the dbgsrv.exe

executable on the system where the process is (or will be) running:

 dbgsrv.exe -t tcp:port=5005

And you’ll likely get a Windows Firewall notice, which you should allow:

From the remote system, you can connect by going to "File: Connect to Remote Stub" and defining the server:


You won’t get any obvious indicator that you’re connected, but when you go to "File:Attach to a Process", you’ll see the process list of the system you’re running dbgsrv.exe on. Now you can attach to a process as you normally would as if the process was local.


WinDBG’s help system is awesome. As with all new things, you should become familiar with how to get help on a specific command or concept. From the command input you can use the .hhcommand to access WinDBG’s help:

windbg> .hh 

You can also use .hh on a specific command. For instance, to get more information on the .reloadcommand, you can use:

windbg> .hh .reload

Or just go to "Help:Contents".


As program runs it pulls in a number of modules that provide functionality – thus if you’re able to gain insight into what modules are imported by the application, it can help identify what the application does and how it may work. In many scenarios, you’ll be debugging a particular module loaded by a program, rather than the program executable itself.

When you attach to process, WinDBG will automatically list the loaded modules, for instance, here’s what WinDBG’s output when I attached to calc.exe:

Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.

*** wait with pending attach
Symbol search path is: SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols
Executable search path is: 
ModLoad: 00a70000 00b30000   C:\Windows\system32\calc.exe
ModLoad: 77630000 7776c000   C:\Windows\SYSTEM32\ntdll.dll
ModLoad: 77550000 77624000   C:\Windows\system32\kernel32.dll
ModLoad: 75920000 7596a000   C:\Windows\system32\KERNELBASE.dll
ModLoad: 76410000 77059000   C:\Windows\system32\SHELL32.dll
ModLoad: 77240000 772ec000   C:\Windows\system32\msvcrt.dll
ModLoad: 76300000 76357000   C:\Windows\system32\SHLWAPI.dll
ModLoad: 75cd0000 75d1e000   C:\Windows\system32\GDI32.dll
ModLoad: 75fa0000 76069000   C:\Windows\system32\USER32.dll
ModLoad: 777b0000 777ba000   C:\Windows\system32\LPK.dll
ModLoad: 774b0000 7754d000   C:\Windows\system32\USP10.dll
ModLoad: 73110000 732a0000   C:\Windows\WinSxS\x86_microsoft.windows.gdiplus_6595b64144ccf1df_1.1.7600.16385_none_72fc7cbf861225ca\gdiplus.dll
ModLoad: 75a80000 75bdc000   C:\Windows\system32\ole32.dll
ModLoad: 76360000 76401000   C:\Windows\system32\RPCRT4.dll
ModLoad: 777c0000 77860000   C:\Windows\system32\ADVAPI32.dll
ModLoad: 75be0000 75bf9000   C:\Windows\SYSTEM32\sechost.dll
ModLoad: 76270000 762ff000   C:\Windows\system32\OLEAUT32.dll
ModLoad: 74590000 745d0000   C:\Windows\system32\UxTheme.dll
ModLoad: 74710000 748ae000   C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7600.16385_none_421189da2b7fabfc\COMCTL32.dll
ModLoad: 703d0000 70402000   C:\Windows\system32\WINMM.dll
ModLoad: 74c80000 74c89000   C:\Windows\system32\VERSION.dll
ModLoad: 77770000 7778f000   C:\Windows\system32\IMM32.DLL
ModLoad: 75c00000 75ccc000   C:\Windows\system32\MSCTF.dll
ModLoad: 74130000 7422b000   C:\Windows\system32\WindowsCodecs.dll
ModLoad: 74260000 74273000   C:\Windows\system32\dwmapi.dll
ModLoad: 756d0000 756dc000   C:\Windows\system32\CRYPTBASE.dll
ModLoad: 75e60000 75ee3000   C:\Windows\system32\CLBCatQ.DLL
ModLoad: 6ef10000 6ef4c000   C:\Windows\system32\oleacc.dll

Later on in a debugging session you can reproduce these results with the lmf command:

0:005> lmf
start    end        module name
00a70000 00b30000   calc     C:\Windows\system32\calc.exe
6ef10000 6ef4c000   oleacc   C:\Windows\system32\oleacc.dll
703d0000 70402000   WINMM    C:\Windows\system32\WINMM.dll
73110000 732a0000   gdiplus  C:\Windows\WinSxS\x86_microsoft.windows.gdiplus_6595b64144ccf1df_1.1.7600.16385_none_72fc7cbf861225ca\gdiplus.dll
74130000 7422b000   WindowsCodecs C:\Windows\system32\WindowsCodecs.dll
74260000 74273000   dwmapi   C:\Windows\system32\dwmapi.dll
74590000 745d0000   UxTheme  C:\Windows\system32\UxTheme.dll
74710000 748ae000   COMCTL32 C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7600.16385_none_421189da2b7fabfc\COMCTL32.dll
74c80000 74c89000   VERSION  C:\Windows\system32\VERSION.dll
756d0000 756dc000   CRYPTBASE C:\Windows\system32\CRYPTBASE.dll
75920000 7596a000   KERNELBASE C:\Windows\system32\KERNELBASE.dll
75a80000 75bdc000   ole32    C:\Windows\system32\ole32.dll
75be0000 75bf9000   sechost  C:\Windows\SYSTEM32\sechost.dll
75c00000 75ccc000   MSCTF    C:\Windows\system32\MSCTF.dll
75cd0000 75d1e000   GDI32    C:\Windows\system32\GDI32.dll
75e60000 75ee3000   CLBCatQ  C:\Windows\system32\CLBCatQ.DLL
75fa0000 76069000   USER32   C:\Windows\system32\USER32.dll
76270000 762ff000   OLEAUT32 C:\Windows\system32\OLEAUT32.dll
76300000 76357000   SHLWAPI  C:\Windows\system32\SHLWAPI.dll
76360000 76401000   RPCRT4   C:\Windows\system32\RPCRT4.dll
76410000 77059000   SHELL32  C:\Windows\system32\SHELL32.dll
77240000 772ec000   msvcrt   C:\Windows\system32\msvcrt.dll
774b0000 7754d000   USP10    C:\Windows\system32\USP10.dll
77550000 77624000   kernel32 C:\Windows\system32\kernel32.dll
77630000 7776c000   ntdll    C:\Windows\SYSTEM32\ntdll.dll
77770000 7778f000   IMM32    C:\Windows\system32\IMM32.DLL
777b0000 777ba000   LPK      C:\Windows\system32\LPK.dll
777c0000 77860000   ADVAPI32 C:\Windows\system32\ADVAPI32.dll

And you can get the load address for a specific module using the "lmf m" command:

0:005> lmf m kernel32
start    end        module name
77550000 77624000   kernel32 C:\Windows\system32\kernel32.dll

To get the image header information you can use the !dh extension (the exclamation mark denotes an extension) on a particular module.

0:005> !dh kernel32

File Type: DLL
     14C machine (i386)
       4 number of sections
4A5BDAAD time date stamp Mon Jul 13 21:09:01 2009

       0 file pointer to symbol table
       0 number of symbols
      E0 size of optional header
    2102 characteristics
            32 bit word machine

     10B magic #
    9.00 linker version
   C4600 size of code
    C800 size of initialized data
       0 size of uninitialized data
   510C5 address of entry point
    1000 base of code
         ----- new -----
77550000 image base
    1000 section alignment
     200 file alignment
       3 subsystem (Windows CUI)
    6.01 operating system version
    6.01 image version
    6.01 subsystem version
   D4000 size of image
     800 size of headers
   D5597 checksum
00040000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
     140  DLL characteristics
            Dynamic base
            NX compatible
   B4DA8 [    A915] address [size] of Export Directory
   BF6C0 [     1F4] address [size] of Import Directory
   C7000 [     520] address [size] of Resource Directory
       0 [       0] address [size] of Exception Directory
       0 [       0] address [size] of Security Directory
   C8000 [    B098] address [size] of Base Relocation Directory
   C5460 [      38] address [size] of Debug Directory
       0 [       0] address [size] of Description Directory
       0 [       0] address [size] of Special Directory
       0 [       0] address [size] of Thread Storage Directory
   816B8 [      40] address [size] of Load Configuration Directory
     278 [     408] address [size] of Bound Import Directory
    1000 [     DE8] address [size] of Import Address Table Directory
       0 [       0] address [size] of Delay Import Directory
       0 [       0] address [size] of COR20 Header Directory
       0 [       0] address [size] of Reserved Directory

   .text name
   C44C1 virtual size
    1000 virtual address
   C4600 size of raw data
     800 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
60000020 flags
         (no align specified)
         Execute Read

Debug Directories(2)
 Type       Size     Address  Pointer
 cv           25       c549c    c4c9c Format: RSDS, guid, 2, kernel32.pdb
 (    10)       4       c5498    c4c98

   .data name
     FEC virtual size
   C6000 virtual address
     E00 size of raw data
   C4E00 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
C0000040 flags
         Initialized Data
         (no align specified)
         Read Write

   .rsrc name
     520 virtual size
   C7000 virtual address
     600 size of raw data
   C5C00 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
40000040 flags
         Initialized Data
         (no align specified)
         Read Only

  .reloc name
    B098 virtual size
   C8000 virtual address
    B200 size of raw data
   C6200 file pointer to raw data
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
42000040 flags
         Initialized Data
         (no align specified)
         Read Only


When you attach to a process, the modules are displayed first, then WinDBG displays any applicable messages. When we attached to calc.exe, WinDBG automatically sets a breakpoint (which is just a marker that tells the debugger uses to pause the execution of a program). So our message is:

(da8.b44): Break instruction exception - code 80000003 (first chance)

This particular message is an exception, specifically a first chance exception. An exception is basically some special condition that occurred during the program’s operation. The first chance means that the progam was paused right after the exception occurred. A second chance exception is when an exception has occurred, some programming logic to handle exception was executed, and the program has paused.


After the messages/exceptions, the debugger will output the state of the CPU’s registers. Registers are basically special variables within the CPU that store a small amount of data or keep track of where something is in memory. The CPU can process the data in these registers very quickly, so its faster for the CPU to perform operations on the values in its registers rather then pulling information all the way down the bus from RAM.

WinDBG automatically outputted the following registers after we attached to calc.exe:

eax=7ffd9000 ebx=00000000 ecx=00000000 edx=776cd23d esi=00000000 edi=00000000
eip=77663540 esp=02affd9c ebp=02affdc8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

Later on down the line, we can reproduce this with the r command:

0:005> r
eax=7ffd9000 ebx=00000000 ecx=00000000 edx=776cd23d esi=00000000 edi=00000000
eip=77663540 esp=02affd9c ebp=02affdc8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
77663540 cc              int     3

And if we wanted to just retrieve a value of a specific register, we could by appending the register name:

0:005> r eax

and multiple registers:

0:005> r eax,ebp
eax=7ffd9000 ebp=02affdc8

Instruction Pointer

The final line is instruction to be executed. This is outputted as part of the r command and is what the EIP register contains. EIP is the instruction pointer, which is the register that contains the location of the next instruction for the CPU to execute. WinDBG’s output is equivalent of the u eip L1 command that basically tells WinDBG to go to the memory location pointed to by EIP, treat that memory as assembly, and print out one line.

77663540 cc              int     3

Source: http://blog.opensecurityresearch.com/2013/12/getting-started-with-windbg-part-1.html

WinDBG Debugger to troubleshoot a Blue Screen of Death

Steps in a nutshell

  1. Create and capture the memory dump associated with the BSOD you are trying to troubleshoot.
  2. Install and configure WinDBG and the Symbols path to the correct Symbols folder.
  3. Use WinDBG to Debug and analyze the screen dump, and then get to the root cause of the problem.

Create memory dump

Keep in mind that if you are not experiencing a blue screen fatal system error, there will be no memory dump to capture.

1. Press the WinKey + Pause.

2. Click Advanced, and under Start Up and Recovery, select Settings.

3. Uncheck Automatically Restart.

4. Click on the dropdown arrow under Write Debugging Information.

5. Select Small Memory Dump (64 KB) and make sure the output is%SystemRoot%\Minidump.

6. Restart the PC normally, as this will allow the System to error and Blue Screen and then create the Minidump.

The location of the Minidump files can be found here:


To download and install the Windows debugging tools for your version of Windows, visit the Microsoft Debugging Tools Web site.

Follow the prompts, and when you install, take note of your Symbols location, if you accept the default settings. I normally create a folder first and then direct the install to that folder because I use WinDBG for two operating systems, XP and Vista, and want to keep them separate and organized.

This Microsoft Support Knowledge Base article will explain how to read the small memory dump files that Windows creates for debugging purposes.

Setting up and using WinDBG

1. Click Start | All Programs | Debugging Tools for Windows, and open WinDBG. Select File | Symbol file path and modify it to suit your situation, then copy and paste it into the box, as shown in Figure A, and click OK. I suggest:


Or if you are using different Symbols:



Figure A
Symbol Path

2. Close the workspace and save the Workspace information, as shown inFigure B. This should lock in the Symbol path.

Figure B

3. Open WinDBG and select File and select Open Crash Dump and then navigate to the minidump file created earlier, highlight it, and select Open.

Click on:

! analyze -v

as shown in Figure C under Bugcheck Analysis.

Figure C
! analyze -v

Tips! If you look to the bottom of the screen, you will see kd>; to the right of that type !analyze -v or .lastevent and press the Enter key. It will then show you the exception record and stack trace of the function where the exception occurred.

You can also use the .exr, .cxr, and .ecxr commands to display the exception and context records.

When working with drivers, you can use kd> lm tn, as shown in Figure D, to get extra information.

[Ctrl]+[A] will let you copy the information and paste it into Notepad.

Figure D

For example, look to the bottom of the page for information similar to what is shown in Figure E.

Figure E
Stack trace


The problem creating the BSOD was caused by the installed dialer software for a USB modem. It turned out that uninstalling the software didn’t resolve the problem.

The answer to the problem was achieved by using the WinDBG tool to Debug and analyze the memory dump file. The fix was to rename theC:\Windows\System\fldevice.sys driver to C:\Windows\System\fldevice.sys.old. Windows was still referencing the file even though the software had been uninstalled. This tool is invaluable and will help you to resolve the problems that you may encounter when you get a BSOD.

Source: http://www.techrepublic.com/blog/windows-and-office/how-do-i-use-windbg-debugger-to-troubleshoot-a-blue-screen-of-death/

Some WinDbg tips

I’ve gathered some WinDbg tips over time (mostly for managed dump analysis) and this seems like as good a place as any to share them, so here you go.

Preparation (one time)

  • Install the latest debugging tools from the Dev Center
    • Let’s assume you install them to c:debuggers
  • Download sosex.dll and place it in c:debuggers
  • Create an environment variable named _NT_SYMBOL_PATH with the valueC:Symbols;srv*C:symbols*http://msdl.microsoft.com/download/symbols
    • Most Microsoft symbols should be found in the symbol server and will be cached in C:Symbols
    • You can copy any other symbols (PDBs) you have to the C:Symbols folder as well
  • Create an environment variable named _NT_SOURCE_PATH with the value srv*
    • This will enable source serving (when source indexing was included in the build) – simply double-click the relevant line in the Call Stack (calls) window and you should jump straight to the relevant line in the source code.
    • You might wonder how that’s possible when no server was specified. The answer is a bit surprising – strictly speaking, there is no such thing as a source server! The name is a bit misleading. What actually happens is that the source-indexed PDB contains the proper command(s) to retrieve the relevant source files. By default this “command” would be something like C:srcfoo.cs, and it would only work if the file in the correct version is actually there. However, if source-indexing was enabled in TFS build, it would look something like tf get MyClass.cs /version:C8 (i.e. the file will be retrieved directly from source, with the correct version).

Preparation (per debugging session)

  • Open the dump file in WinDbg
    • be sure to match the architecture to the analyzed process – use WinDbg x86 for 32-bit processes and WinDbg x64 for 64-bit processes
  • Enable Debugger Markup Language (DML) by issuing .prefer_dml 1
    • This will make the output of some commands contain convenient hyperlinks
  • Load the SOSEX extension by issuing the command .load sosex.dll
    • Don’t load SOS.dll manually – the first command below (analyze -v) will load it with the correct version automatically


  • Automatic exception analysis: !analyze -v
    • In many cases this will suffice to find the root cause!
  • Threads
    • !Threads – lists all the managed threads in the process
  • Stack commands
    • !clrstack – provides a true stack trace for managed code only
    • !dumpstack – provides a verbose stack trace
    • !eestac – runs !dumpStack on all threads in the process
    • !dso – displays any managed objects within the bounds of the current stack
    • !sosex.mk – produces and displays a merged stack trace of managed and unmanaged frames. Note that in addition to the native offset, a managed (IL) offset is specified – this is extremely useful for debuggingNullReferenceException’s in that the exact offending IL instruction is indicated (actually it will be one instruction before the offending one in the specific case of NullReferenceException)
    • !sosex.mdso – dumps object references on the stack and in CPU registers in the current context
    • !sosex.mdv – displays argument and local variable information for the current frame
    • !sosex.mframe – displays or sets the current managed frame for the !mdt and!mdv commands
  • Heap commands
    • !eeheap – enumerates process memory consumed by internal CLR data structures
    • !DumpHeap – traverses the garbage collected heap
  • Object commands
    • !do – allows you to examine the fields of an object, as well as learn important properties of the object
    • !dumparray – examines elements of an array object
    • !dumpvc – examines the fields of a value class
    • !sosex.mdt – displays the fields of the specified object or type
  • Method commands
    • !dumpmt – examines a MethodTable
    • !DumpIL – prints the IL code associated with a managed method
    • !U – presents an annotated disassembly of a managed method
    • !sosex.muf – disassembles the method specified by the given MD or code address with interleaved source, IL, and assembly code
  • Exception commands
    • !pe exceptionAddress – formats fields of any object derived fromSystem.Exception
  • GC commands
    • !GCRoot – looks for references (or roots) to an object
  • SOS Help
    • !sos.help
    • !sos.help FAQ
  • SOSEX Help
    • !sosexhelp or !sosex.help

Mismatched SOS.dll versions

!analyze -v  should get the correct SOS.dll version for you. Even if for some reason it doesn’t, SOS.dll warnings can be ignored most of the time, so long as themscordacwks.dll version is correct (see the next section if that’s not the case). However there may be cases where the correct SOS.dll version is needed (or perhaps you just want to get rid of the warning). Here are some places to look for it (once you find it, copy it to your debugger folder and issue .load sos.dll):

  • Your best bet is the machine on which the dump was taken. Provided that it’s accessible and wasn’t patched since the time the dump was taken, the correct version should be found atC:WindowsMicrosoft.NETFramework64v4.0.30319SOS.dll. Of course it should be there on your machine as well, and your local version may happen to match (or be close enough).
  • You may find the version you’re looking for here (you should be able to extract the file from the update package itself using 7-zip).
  • You may also want to try Psscor4.dll instead of SOS.dll (Psscor is a superset ofSOS) – its version need not match the dump (aside of being .NET 4.0). Note that it is less maintained than SOS.
  • For more information see http://stackoverflow.com/a/23244429/67824

Mismatched mscordacwks.dll versions

WinDBG should find the correct mscordacwks.dll automatically and download it from the symbol server. If that doesn’t happen, try and do it explicitly by running .cordll -ve -u -l(you’d might want to first run !sym noisy and/or !symfix in order to troubleshoot better – see the next section for details). Failing that, try and get the correct version from the following places, and run .cordll -u -ve -lp PathToFolderContainingMscorDAC once you have it.

  • Again, your best bet is the machine on which the dump was taken (under the same caveats as above). It should be found atC:WindowsMicrosoft.NETFramework64v4.0.30319mscordacwks.dll. As before, it will be there on your machine so you could luck out.
  • The following post lists many versions of CLR 4.0, you may be able to extract the correct version with the same method as above (use 7-zip to open the cab files inside the archive).
  • For more information see http://stackoverflow.com/a/23244429/67824

Troubleshooting missing symbols and sources

  • !sym noisy – increases symbol verbosity (always enable this when troubleshooting symbol issues)
  • .srcnoisy 3 – increases source resolution vebosity (use it when double-clicking lines in the callstack window doesn’t bring up the correct sources)
  • lm – displays the specified loaded modules
    • lme displays only modules that have a symbol problem (very useful)
  • .reload /f – forces the reloading of all symbols
    • .reload /f SomeAssembly.dll – forces the reloading of a specified DLL


Source: http://www.ohadsoft.com/2014/10/some-windbg-tips/

Common WinDbg Commands (Thematically Grouped)


1) Built-in help commands
Cmd Variants / Params Description


? /D

Display regular commands 
Display regular commands as DML


.help /D 
.help /D a*

Display . commands 
Display . commands in DML format (top bar of links is given) 
Display . commands that start with a* (wildcard) as DML


.chain /D

Lists all loaded debugger extensions 
Lists all loaded debugger extensions as DML (where extensions are linked to a .extmatch)


.extmatch /e ExtDLL FunctionFilter 
.extmatch /D /e ExtDLL FunctionFilter

Show all exported functions of an extension DLL. FunctionFilter = wildcard string 
Same in DML format (functions link to "!ExtName.help FuncName" commands) 

Example: .extmatch /D /e uext * (show all exported functions of uext.dll)


.hh Text

Open WinDbg’s help 
Text = text to look up in the help file index 
.hh dt

2) General WinDbg’s commands (show version, clear screen, etc.)
Cmd Variants / Params Description


Dump version info of debugger and loaded extension DLLs


Dump command line that was used to start the debugger


Version of target computer


Toggle verbose mode ON/OFF 
In verbose mode some commands (such as register dumping) have more detailed output.


n [8 | 10 | 16]

Set number base


.formats Expression

Show number formats = evaluates a numerical expression or symbol and displays it in multiple numerical formats (hex, decimal, octal, binary, time, ..) 
Example 1: .formats 5 
Example 2: .formats poi(nLocal1) == .formats @@($!nLocal1)


Clear screen


Displays the most recent exception or event that occurred (why the debugger is waiting?)


.effmach . 
.effmach # 
.effmach x86 | amd64 | ia64 | ebc

Dump effective machine (x86, amd64, ..): 
Use target computer’s native processor mode 
Use processor mode of the code that is executing for the most recent event 
Use x86, amd64, ia64, or ebc processor mode 

This setting influences many debugger features: 
-> which processor’s unwinder is used for stack tracing 
-> which processor’s register set is active


display time (system-up, process-up, kernel time, user time)

3) Debugging sessions (attach, detach, ..)
Cmd Variants / Params Description



attach to a process


ends the debugging session, but leaves any user-mode target application running


q, qq

Quit = ends the debugging session and terminates the target application 
Remote debugging: q= no effect; qq= terminates the debug server


Restart target application

4) Expressions and commands
Cmd Variants / Params Description


Command separator (cm1; cm2; ..)


? Expression 
?? Expression

Evaluate expression (use default evaluator) 
Evaluate c++ expression


.expr /q 
.expr /s c++ 
.expr /s masm

Choose default expression evaluator 
Show current evaluator 
Show available evaluators 
Set c++ as the default expression evaluator 
Set masm as the default expression evaluator


* [any text]

Comment Line Specifier 
Terminated by: end of line


$$ [any text]

Comment Specifier 
Terminated by: end of line OR semicolon


.echo String 
.echo "String"

Echo Comment -> comment text + echo it 
Terminated by: end of line OR semicolon 
With the $$ token or the * token the debugger will ignore the inputted text without echoing it.

5) Debugger markup language (DML) 

Starting with the 6.6.07 version of the debugger a new mechanism for enhancing output from the debugger and extensions was included: DML. 
DML allows output to include directives and extra non-display information in the form of tags. 
Debugger user interfaces parse out the extra information to provide new behaviors. 

DML is primarily intended to address two issues:

Linking of related information

Discoverability of debugger and extension functionality

Cmd Variants / Params Description


Kick of to other DML commands


.prefer_dml [1 | 0]

Global setting: should DML-enhanced commands default to DML? 
Note that many commands like k, lm, .. output DML content thereafter.

.help /D

.help has a new DML mode where a top bar of links is given

.chain /D

.chain has a new DML mode where extensions are linked to a .extmatch

.extmatch /D

.extmatch has a new DML format where exported functions link to "!ExtName.help FuncName" commands


lm has a new DML mode where module names link to lmv commands


k has a new DML mode where frame numbers link to a .frame/dv


.dml_flow StartAddr TargetAddr

Allows for interactive exploration of code flow for a function.

Builds a code flow graph for the function starting at the given start address (similar to uf)

Shows the basic block given the target address plus links to referring blocks and blocks referred to by the current block

Example: .dml_flow CreateRemoteThread CreateRemoteThread+30

6) Main extensions
Cmd Variants / Params Display supported commands for ..


General extensions




User-Mode Extensions (non-OS specific)


User-Mode Extensions (OS specific)


Logger Extensions


Debugging managed code


Wow64 debugger extensions


Kernel-Mode driver framework extensions


Graphics driver extensions




Display detailed help about an exported function 
NAME = placeholder for extension DLL 
FUNCTION = placeholder for exported function 

Example: !Ntsdexts.help handle (show detailed help about !Ntsdexts.handle)

7) Symbols
Cmd Variants / Params Description


ld ModuleName 
ld *

Load symbols for Module 
Load symbols for all modules


!sym noisy 
!sym quiet

Get state of symbol loading 
Set noisy symbol loading (debugger displays info about its search for symbols) 
Set quiet symbol loading (=default)


x [Options] Module!Symbol 
x /t .. 
x /v .. 
x /a .. 
x /n .. 
x /z ..

Examine symbols: displays symbols that match the specified pattern 
with data type 
verbose (symbol type and size) 
sort by address 
sort by name 
sort by size ("size" of a function symbol is the size of the function in memory)


ln Addr

List nearest symbols = display the symbols at or near the given Addr. Useful to:

determine what a pointer is pointing to

when looking at a corrupted stack to determine which procedure made a call



Display or set symbol search path 
Append directories to previous symbol path


.symopt+ Flags 

displays current symbol options 
add option 
remove option


.symfix+ DownstreamStore

Set symbol store path to automatically point to http://msdl.microsoft.com/download/symbols 
+ = append it to the existing path 
DownstreamStore = directory to be used as a downstream store. Default is WinDbgInstallationDir\Sym.


.reload [/f | /v] 
.reload [/f | /v] Module

Reload symbol information for all modules** 
f = force immediate symbol load (overrides lazy loading); v = verbose mode 
Module = for Module only 

**Note: The .reload command does not actually cause symbol information to be read. It just lets the debugger know that the symbol files may have changed, or that a new module should be added to the module list. To force actual symbol loading to occur use the /f option, or the ld (Load Symbols) command.

x *! list all modules
x ntdll!* list all symbols of ntdll
x /t /v MyDll!* list all symbol in MyDll with data type, symbol type and size
x kernel32!*LoadLib* list all symbols in kernel32 that contain the word LoadLib
.sympath+ C:\MoreSymbols add symbols from C:\MoreSymbols (folder location)
.reload /f @"ntdll.dll" Immediately reload symbols for ntdll.dll.
.reload /f @"C:\WINNT\System32\verifier.dll" Reload symbols for verifier. Use the given path.

Also check the "!lmi" command.

8) Sources
Cmd Variants / Params Description


.srcpath+ DIR

Display or set source search path 
Append directory to the searched source path



Controls noisy source loading


[-e | -d | -t]

Toggle source line support: enable; disable; toggle

l (small letter L)

l+l, l-l 
l+o, l-o 
l+s, l-s 
l+t, l-t

show line numbers 
suppress all but [s] 
source and line number 
source mode vs. assembly mode

9) Exceptions, events, and crash analysis
Cmd Variants / Params Description



Go exception handled 
Go not handled


What happened? Shows most recent event or exception


!analyze -v 
!analyze -hang 
!analyze -f

Display information about the current exception or bug check; verbose 
User mode: Analyzes the thread stack to determine whether any threads are blocking other threads. 
See an exception analysis even when the debugger does not detect an exception.



Show all event filters with break status and handling 
break first-chance 
break second-chance 
notify; don’t break 
ignore event 
reset filter settings to default values


.exr Addr

display most recent exception record 
display exception record at Addr



displays exception context record (registers) associated with the current exception



Display content and type of C++ exception

exr -1 display most recent exception
.exr 7c901230 display exception at address 7c901230
!cppexr 7c901230 display c++ exception at address 7c901230

10) Loaded modules and image information
Cmd Variants / Params Description


lm[ v | l | k | u | f ] [m Pattern] 

List modules; verbose | with loaded symbols | k-kernel or u-user only symbol info | image path; pattern that the module name must match 
DML mode of lm; lmv command links included in output


!dlls -i 
!dlls -l 
!dlls -m 
!dlls -v 
!dlls -c ModuleAddr 
!dlls -?

all loaded modules with load count 
by initialization order 
by load order (default) 
by memory order 
with version info 
only module at ModuleAddr 
brief help



information about relocated images



detailed info about a module (including exact symbol info)


!dh ImgBaseAddr 
!dh -f ImgBaseAddr 
!dh -s ImgBaseAddr 
!dh -h

Dump headers for ImgBaseAddr 
f = file headers only 
s = section headers only 
h = brief help 

The !lmi extension extracts the most important information from the image header and displays it in a concise summary format. It is often more useful than !dh.

lm display all loaded and unloaded modules
lmv m kernel32 display verbose (all possible) information for kernel32.dll
lmD DML variant of lm
!dlls -v -c kernel32 display information for kernel32.dll, including load-count
!lmi kernel32 display detailed information about kernel32, including symbol information
!dh kernel32 display headers for kernel32

11) Process related information
Cmd Variants / Params Description


(DML) displays current processes and allows drilling into processes for more information

| (pipe)

Print status of all processes being debugged


lists all processes running on the system


display formatted view of the process’s environment block (PEB)

!peb Dump formatted view of processes PEB (only some information)
r $peb Dump address ob PEB. $peb == pseudo-register
dt ntdll!_PEB Dump PEB struct
dt ntdll!_PEB @$peb -r Recursively (-r) dump PEB of our process

12) Thread related information
Cmd Variants / Params Description


~* [Command] 
~. [Command] 
~# [Command] 
~Number [Command] 
~~[TID] [Command] 

list threads 
all threads 
current thread 
thread that caused the current event or exception 
thread whose ordinal is Number 
thread whose thread ID is TID (the brackets are required) 
switch to thread N (new current thread) 

[Command]: works for a few regular commands such as k, r


~* e CommandString 
~. e CommandString 
~# e CommandString 
e CommandString

Execute thread-specific commands (CommandString = one or more commands to be executed) for: 
all threads 
current thread 
thread which caused the current event 
thread with ordinal


~Thread f

Freeze thread (see ~ for Thread syntax)


~Thread u

Unfreeze thread (see ~ for Thread syntax)


~Thread n

Suspend thread = increment thread’s suspend count


~Thread m

Resume thread = decrement thread’s suspend count


display formatted view of the thread’s environment block (TEB)


!tls -1 
!tls SlotIdx 
!tls [-1 | SlotIdx] TebAddr

-1 = dump all slots for current thread 
SlotIdx = dump only specified slot 
TebAddr = specify thread; if omitted, the current thread is used


display thread times (user + kernel mode)


[Flags: 0 | 1 | 2]

display information about time consumed by each thread (0-user time, 1-kernel time, 2-time elapsed since thread creation). quick way to find out which threads are spinning out of control or consuming too much CPU time


!gle -all

Dump last error for current thread 
Dump last error for all threads 

Point of interest: 
SetLastError( dwErrCode ) checks the value of kernel32!g_dwLastErrorToBreakOn and possibly executes a DbgBreakPoint. 

if ((g_dwLastErrorToBreakOn != 0 ) && (dwErrCode == g_dwLastErrorToBreakOn)) 

The downside is that SetLastError is only called from within KERNEL32.DLL. 
Other calls to SetLastError are redirected to a function located in NTDLL.DLL, RtlSetLastWin32Error.


!error ErrValue 
!error ErrValue 1

Decode and display information about an error value 
Treat ErrValue value as an NTSTATUS code

~* k call stack for all threads ~ !uniqstack
~2 f Freeze Thread TID=2
~# f Freeze the thread causing the current exception
~3 u Unfreeze Thread TID=3
~2e r; k; kd == ~2r; ~2k; ~2kd
~*e !gle will repeat every the extension command !gle for every single thread being debugged
!tls -1 Dump all TLS slots for current thread
!runaway 7 1 (user time) + 2 (kernel time) + 4 (time elapsed since thread start)
!teb Dump formatted view of our threads TEB (only some information)
dt ntdll!_TEB @$teb Dump TEB of current thread

13) Breakpoints
Cmd Variants / Params Description


List breakpoints


bc * 
bc # [#] [#]

Clear all breakpoints 
Clear breakpoint #


be * 
be # [#] [#]

Enable all bps 
Enable bp #


bd * 
bd # [#] [#]

Disable all bps 
Disable bp #


bp [Addr] 
bp [Addr] ["CmdString"] 

[~Thrd] bp[#] [Options] [Addr] [Passes] ["CmdString"]

Set breakpoint at address 
CmdString = Cmd1; Cmd2; .. Executed every time the BP is hit. 

~Thrd == thread that the bp applies too. 
# = Breakpoint ID 
Passes = Activate breakpoint after #Passes (it is ignored before)


bu [Addr] 

See bp ..

Set unresolved breakpoint. bp is set when the module gets loaded


bm SymPattern 
bm SymPattern ["CmdString"] 

[~Thrd] bm [Options] SymPattern [#Passes] ["CmdString"]

Set symbol breakpoint. SymPattern can contain wildcards 
CmdString = Cmd1; Cmd2; .. Executed every time the BP is hit. 

~Thrd == thread that the bp applies too. 
Passes = Activate breakpoint after #Passes (it is ignored before) 

The syntax bm SymPattern is equivalent to using x SymPattern and then using bu on each of the results.


ba [r|w|e] [Size] Addr 

[~Thrd] ba[#] [r|w|e] [Size] [Options] [Addr] [Passes] ["CmdString"]

Break on Access: [r=read/write, w=write, e=execute], Size=[1|2|4 bytes] 

[~Thrd] == thread that the bp applies too. 
# = Breakpoint ID 
Passes = Activate breakpoint after #Passes (it is ignored before)


br OldID NewID [OldID2 NewID2 …]

renumbers one or more breakpoints

With bp, the breakpoint location is always converted to an address. In contrast, a bu or a bm breakpoint is always associated with the symbolic value. 

Simple Examples

bp `mod!source.c:12` set breakpoint at specified source code
bm myprogram!mem* SymbolPattern is equivalent to using x SymbolPattern
bu myModule!func bp set as soon as myModule is loaded
ba w4 77a456a8 break on write access
bp @@( MyClass::MyMethod ) break on methods (useful if the same method is overloaded and thus present on several addresses)

Breakpoitns with options

Breakpoint that is triggered only once
bp mod!addr /1
Breakpoint that will start hitting after k-1 passes
bp mod!addr k

Breakpoints with commands: The command will be executed when the breakpoint is hit.

Produce a log every time the breakpoint is hit
ba w4 81a578a8 "k;g"
Create a dump every time BP is hit
bu myModule!func ".dump c:\dump.dmp; g"
DllMain called for MYDLL -> check reason
bu MYDLL!DllMain "j (dwo(@esp+8) == 1) ‘.echo MYDLL!DllMain -> DLL_PROCESS_ATTACH; kn’ ; ‘g’ "
LoadLibraryExW( anyDLL ) called -> display name of anyDLL
bu kernel32!LoadLibraryExW ".echo LoadLibraryExW for ->; du dwo(@esp+4); g"
LoadLibraryExW( MYDLL ) called? -> Break only if LoadLibrary is called for MyDLL
bu kernel32!LoadLibraryExW ";as /mu ${/v:MyAlias} poi(@esp+4); .if ( $spat( \"${MyAlias}\", \"*MYDLL*\" ) != 0 ) { kn; } .else { g }"

The first parameter to LoadLibrary (at address ESP + 4) is a string pointer to the DLL name in question.

The MASM $spat operator will compare this pointer to a predefined string-wildcard, this is *MYDLL* in our example.

Unfortunately $spat can accept aliases or constants, but no memory pointers. This is why we store our string in question to an alias (MyAlias) first.

Our kernel32!LoadLibraryExW breakpoint will hit only if the pattern compared by $spat matches. Otherwise the application will continue executing.

Skip execution of a function
bu sioctl!DriverEntry "r eip = poi(@esp); r esp = @esp + 0xC; .echo sioctl!DriverEntry skipped; g"

Right at a function’s entry point the value found on the top of the stack contains the return address 
r eip = poi(@esp) -> Set EIP (instruction pointer) to the value found at offset 0x0

DriverEntry has 2×4 byte parameters = 8 bytes + 4 bytes for the return address = 0xC 
r esp = @esp + 0xC -> Add 0xC to Esp (the stack pointer), effectively unwinding the stack pointer

bu MyApp!WinMain "r eip = poi(@esp); r esp = @esp + 0x14; .echo WinSpy!WinMain entered; g"

WinMain has 4×4 byte parameters = 0x10 bytes + 4 bytes for the return address = 0x14

Howto set a brekpoint in your code programatically?



__asm int 3 (x86 only)

14) Tracing and stepping (F10, F11) 

Each step executes either a single assembly instruction or a single source line, depending on whether the debugger is in assembly mode or source mode. 
Use the l+t and l-t commands or the buttons on the WinDbg toolbar to switch between these modes.

Cmd Variants / Params Description

g (F5)


Go (F5) 
Go up = execute until the current function is complete 
gu ~= g @$ra 
gu ~= bp /1 /c @$csp @$ra;g 
-> $csp = same as esp on x86 
-> $ra = The return address currently on the stack

p (F10)


p Count 
p [Count] "Command" 
p =StartAddress [Count] ["Command"] 

[~Thread] p [=StartAddress] [Count] ["Command"]

Single step – executes a single instruction or source line. Subroutines are treated as a single step. 

Toggle display of registers and flags 
Count = count of instructions or source lines to step through before stopping 
Command = debugger command to be executed after the step is performed 
StartAddress = Causes execution to begin at the specified address. Default is the current EIP. 

~Thread = The specified thread is thawed and all others frozen

t (F11)


Single trace – executes a single instruction or source line. For subroutines each step is traced as well.



Step to next return – similar to the GU (go up), but staying in context of the current function 
If EIP is already on a return instruction, the entire return is executed. After this return is returned, execution will continue until another return is reached.



Trace to next return – similar to the GU (go up), but staying in context of the current function 
If EIP is already on a return instruction, the debugger traces into the return and continues executing until another return is reached.



Step to next call – executes the program until a call instruction is reached 
If EIP is already on a call instruction, the entire call will be executed. After this call is returned execution will continue until another call is reached.



Trace to next call – executes the program until a call instruction is reached 
If EIP is already on a call instruction, the debugger will trace into the call and continue executing until another call is reached.


pa StopAddr 

pa StopAddr "Command" 
pa =StartAddress StopAddr ["Command"]

Step to address; StopAddr = address at which execution will stop 
Called functions are treated as a single unit 

Toggle display of registers and flags 
Command = debugger command to be executed after the step is performed 
StartAddress = Causes execution to begin at the specified address. Default is the current EIP.


ta StopAddr 

Trace to address; StopAddr = address at which execution will stop 
Called functions are traced as well



wt [Options] [= StartAddr] [EndAddr] 
wt -l Depth .. 
wt -m Module [-m Module2] .. 
wt -i Module [-i Module2] .. 
wt -oa .. 
wt -or .. 
wt -oR .. 
wt -nc .. 
wt -ns .. 
wt -nw ..

Trace and watch data. Go to the beginning of a function and do a wt. It will run through the entire function and display statistics. 

StartAddr = execution begin; EndAddr = address at which to end tracing (default = after RET of current function) 
l = maximum depth of traced calls 
m = restrict tracing to Module 
i = ignore code from Module 
oa = dump actual address of call sites 
or = dump return register values (EAX value) of sub-functions 
oR = dump return register values (EAX value) in the appropriate type 
nc = no info for individual calls 
ns = no summary info 
ns = no warnings


.step_filter "FilerList" 
.step_filter /c

Dump current filter list = functions that are skipped when tracing (t, ta, tc) 
FilterList = Filter 1; Filter 2; … symbols associated with functions to be stepped over (skipped) 
clear the filter list 

.step_filter is not very useful in assembly mode, as each function call is on a different line.

g go
g `:123`; ? poi(counter); g executes the current program to source line 123; print the value of counter; resume execution
p single step
pr toggle displaying of registers
p 5 "kb" 5x steps, execute "kb" thereafter
pc step to next CALL instruction
pa 7c801b0b step until 7c801b0b is reached
wt trace and watch sub-functions
wt -l 4 -oR trace sub-functions to depth 4, display their return values

15) Call stack
Cmd Variants / Params Description


k [n] [f] [L] [#Frames] 
kb … 
kp … 
kP … 
kv …

dump stack; n = with frame #; f = distance between adjacent frames; L = omit source lines; number of stack frames to display 
first 3 params 
all params: param type + name + value 
all params formatted (new line) 
FPO info, calling convention


kd [WordCnt]

display raw stack data + possible symbol info == dds esp


DML variant with links to .frame #;dv


Set stack length. The default is 20 (0x14).


.frame # 
.frame /r [#]

show current frame 
specify frame # 
show register values 

The .frame command specifies which local context (scope) will be used to interpret local variables, or displays the current local context. 
When executing a near call, the processor pushes the value of the EIP register (which contains the offset of the instruction following the CALL instruction) onto the stack (for use later as a return-instruction pointer). This is the first step in building a frame. Each time a function call is made, another frame is created so that the called function can access arguments, create local variables, and provide a mechanism to return to calling function. The composition of the frame is dependant on the function calling convention.


!uniqstack [b|v|p] [n] 
!uniqstack -?

show stacks for all threads 
[b = first 3 params, v = FPO + calling convention, p = all params: param type + name + value], [n = with frame #] 
brief help


!findstack Symbol 
!findstack Symbol [0|1|2] 
!findstack -?

locate all stacks that contain Symbol or module 
[0 = show only TID, 1 = TID + frames, 2 = entire thread stack] 
brief help

k display call stack
kn call stack with frame numbers
kb display call stack with first 3 params
kb 5 display first 5 frames only

To get more than 3 Function Arguments from the stack
dd ChildEBP+8 (Parameters start at ChildEBP+8) 
dd ChildEBP+8 (frame X) == dd ESP (frame X-1) 

!uniqstack get all stacks of our process (one for each thread)
!findstack kernel32 2 display all stacks that contain "kernel32"
.frame show current frame
.frame 2 set frame 2 for the local context
.frame /r 0d display registers in frame 0

16) Registers
Cmd Variants / Params Description


r Reg1, Reg2 
r Reg=Value 

r Reg:Type 

r Reg:[Num]Type 

~Thread r [Reg:[Num]Type]

Dump all registers 
Dump only specified registers (i.e.: r eax, edx) 
Value to assign to the register (i.e.: r eax=5, edx=6) 

Type = data format in which to display the register (i.e.: r eax:uw) 
ib = Signed byte 
ub = Unsigned byte 
iw = Signed word (2b) 
uw = Unsigned word (2b) 
id = Signed dword (4b) 
ud = Unsigned dword (4b) 
iq = Signed qword (8b) 
uq = Unsigned qword (8b) 
f = 32-bit floating-point 
d = 64-bit floating-point 

Num = number of elements to display (i.e.: r eax:1uw) 
Default is full register length, thus r eax:uw would display two values as EAX is a 32-bit register. 

Thread = thread from which the registers are to be read (i.e.: ~1 r eax)


rM Mask 
rM Mask Reg1, Reg2 
rM Mask Reg=Value 

Dump register types specified by Mask 
Dump only specified registers from current mask 
Value to assign to the register 

Flags for Mask 
0x1 = basic integer registers 
0x4 = floating-point registers == rF 
0x8 = segment registers 
0x10 = MMX registers 
0x20 = Debug registers 
0x40 = SSE XMM registers == rX


rF Reg1, Reg2 
rF Reg=Value 

Dump all floating-point registers == rM 0x4 
Dump only specified floating-point registers 
Value to assign to the register


rX Reg1, Reg2 
rX Reg=Value 

Dump all SSE XMM registers == rM 0x40 
Dump only specified SSE XMM registers 
Value to assign to the register


rm ? 
rm Mask

Dump default register mask. This mask controls how registers are displayed by the "r". 
Dump a list of possible Mask bits 
Specify the mask to use when displaying the registers.

rm ? show possible bit mask
rm 1 enable integer registers only
r dump all integer registers
r eax, edx dump only eax and edx
r eax=5, edx=6 assign new values to eax and edx
r eax:1ub dump only the first byte from eax
rm 0x20 enable debug register mask
r dump debug registers
rF dump all floating point register
rM 0x4 dump all floating point register
rm 0x4; r dump all floating point registers

17) Information about variables
Cmd Variants / Params Description


dt -h 
dt [mod!]Name 
dt [mod!]Name Field [Field] 
dt [mod!]Name [Field] Addr 
dt [mod!]Name* 

dt [-n|y] [mod!]Name [-n|y] [Field] [Addr] 

dt [-n|y] [mod!]Name [-n|y] [Field] [Addr] -abcehioprsv 

Brief help 
Dump variable info 
Dump only ‘field-name(s)’ (struct or unions) 
Addr of struct to be dumped 
list symbols (wildcard) 

-n Name = param is a name (use if name can be mistaken as an address) 
-y Name = partially match instead of default exact match 

-a = Shows array elements in new line with its index 
-b = Dump only contiguous block of struct 
-c = Compact output (all fields in one line) 
-i = Does not indent the subtypes 
-l ListField = Field which is pointer to the next element in list 
-o = Omit the offset value (fields of struct) 
-p = Dump from physical address 
-r[l] = Recursively dump subtypes/fields (up to l levels) 
-s [size] = For enumeration only, enumerate types only of given size. 
-v = Verbose output.


dv Pattern 
dv [/i /t /V] [Pattern] 
dv [/i /t /V /a /n /z] [Pattern]

display local variables and parameters 
vars matching Pattern 
i = type (local, global, parameter), t = data type, V = memory address or register location 
a = sort by Addr, n = sort by name, z = sort by size

dt ntdll!_PEB* list all variables that contain the word _PEB
dt ntdll!_PEB* -v list with verbose output (address and size included)
dt ntdll!_PEB* -v -s 9 list only symbols whose size is 9 bytes
dt ntdll!_PEB dump _PEB info
dt ntdll!_PEB @$peb dump _PEB for our process
dt ntdll!_PEB 7efde000 dump _PEB at Addr 7efde000 
You can get our process’s PEB address with "r @$peb" or with "!peb".
dt ntdll!_PEB Ldr SessionId dump only PEB’s Ldr and SessionId fields
dt ntdll!_PEB Ldr -y OS* dump Ldr field + all fields that start with OS*
dt mod!var m_cs. dump m_cs and expand its subfields
dt mod!var m_cs.. expand its subfields for 2 levels
dt ntdll!_PEB -r2 dump recursively (2 levels)
dv /t /i /V dump local variables with type information (/t), addresses and EBP offsets (/V), classify them into categories (/i) 
Note: dv will also display the value of a THIS pointer for methods called with the "this calling-convention". 
BUG: You must first execute a few commands before dv displays the correct value. 
Right at a function’s entry point the THIS pointer is present in ECX, so you can easily get it from there.

18) Memory
Cmd Variants / Params Description


d[a| u| b| w| W| d| c| q| f| D] [/c #] [Addr] 

dy[b | d] ..

Display memory [#columns to display] 
a = ascii chars 
u = Unicode chars 

b = byte + ascii 
w = word (2b) 
W = word (2b) + ascii 
d = dword (4b) 
c = dword (4b) + ascii 
q = qword (8b) 

f = floating point (single precision – 4b) 
D = floating point (double precision – 8b) 

b = binary + byte 
d = binary + dword


e[ b | w | d | q | f | D ] Addr Value 

e[ a | u | za | zu ] Addr "String"

Edit memory 
b = byte 
w = word (2b) 
d = dword (4b) 
q = qword (8b) 

f = floating point (single precision – 4b) 
D = floating point (double precision – 8b) 

a = ascii string 
za = ascii string (NULL-terminated) 
u = Unicode string 
zu = Unicode string (NULL-terminated)

ds, dS

ds [/c #] [Addr] 
[/c #] [Addr]

Dump string struct (struct! not null-delimited char sequence) 


dds [/c #] [Addr] 
dqs [/c #] [Addr]

Display words and symbols (memory at Addr is assumed to be a series of addresses in the symbol table) 
dds = dwords (4b) 
dqs = qwords (8b)

dd*, dq*, dp*



Display referenced memory = display pointer at specified Addr, dereference it, and then display the memory at the resulting location in a variety of formats. 

the 2nd char determines the pointer size used: 
dd* -> 32-bit pointer used 
dq* -> 64-bit pointer used 
dp* -> standard size: 32-bit or 64-bit, depending on the CPU architecture 

the 3rd char determines how the dereferenced memory is displayed: 
d*a -> dereferenced mem as asci chars 
d*u -> dereferenced mem as Unicode chars 
d*p -> dereferenced mem as dword or qword, depending on the CPU architecture. If this value matches any known symbol, this symbol is displayed as well.


dl[b] Addr MaxCount Size

Display linked list (LIST_ENTRY or SINGLE_LIST_ENTRY) 
b = dump in reverse order (follow BLinks instead of FLinks) 
Addr = start address of the list 
MaxCount = max # elements to dump 
Size = Size of each element 

Use !list to execute some command for each element in the list.


!address -? 
!address Addr 
!address -summary 
!address -RegionUsageXXX

Display info about the memory used by the target process 
Brief help 
Dump info for region with Addr 
Dump summary info for process 
Dump specified regions (RegionUsageStack, RegionUsagePageHeap, ..)


!vprot -? 
!vprot Addr

Brief Help 
Dump virtual memory protection info


!mapped_file -? 
!mapped_file Addr

Brief Help 
Dump name of the file containing given Addr

dd 0046c6b0 display dwords at 0046c6b0
dd 0046c6b0 L1 display 1 dword at 0046c6b0
dd 0046c6b0 L3 display 3 dwords at 0046c6b0
du 0046c6b0 display Unicode chars at 0046c6b0
du 0046c6b0 L5 display 5 Unicode chars at 0046c6b0
dds esp == kd display words and symbols on stack
!mapped_file 00400000 Dump name of file containing address 00400000
!address show all memory regions of our process
!address -RegionUsageStack show all stack regions of our process
!address esp show info for committed sub-region for our thread’s stack. 
Note: For stack overflows SubRegionSize (size of committed memory) will be large, i.e.:

   AllocBase : SubRegionBase - SubRegionSize
   001e0000 : 002d6000 - 0000a000

Determine stack usage for a thread

                Stack Identifier           Memory Identifier ^
--------------  <- _TEB.StackBase          SubRegionBase3 + SubRegionSize3

|            |
|            |
|------------|  <- _TEB.StackLimit         SubRegionBase3 ^, SubRegionBase2 + SubRegionSize2
|------------|                             SubRegionBase2 ^, SubRegionBase1 + SubRegionSize1 
|            |
|            |
|------------|  <- _TEB.DeallocationStack  AllocationBase or RegionBase, SubRegionBase1 ^
                DeallocationStack: dt ntdll!_TEB TebAddr DeallocationStack

From MSDN CreateThread > dwStackSize > "Thread Stack Size": 

"Each new thread receives its own stack space, consisting of both committed and reserved memory. By default, each thread uses 1 Mb of reserved memory, and one page of committed memory. The system will commit one page block from the reserved stack memory as needed."

19) Manipulating memory ranges
Cmd Variants / Params Description


c Range DestAddr

Compare memory


m Range DestAddr

Move memory


f Range Pattern

Fill memory. Pattern = a series of bytes (numeric or ASCII chars)


s Range Pattern 

s -[Flags]b Range Pattern 

s -[Flags]w Range ‘Pattern’ 

s -[Flags]d Range ‘Pattern’ 

s -[Flags]q Range ‘Pattern’ 

s -[Flags]a Range "Pattern" 

s -[Flags]u Range "Pattern" 

s -[Flags,l length]sa Range 

s -[Flags,l length]su Range 

s -[Flags]v Range Object

Search memory 

b = byte (default value) 

Pattern = a series of bytes (numeric or ASCII chars) 

w = word (2b) 

d = dword (4b) 

q = qword (8b) 

Pattern = enclosed in single quotation marks (for example, ‘Tag7’) 

a = ascii string (must not be null-terminated) 

u = Unicode string (must not be null-terminated) 

Pattern = enclosed in double quotation marks (for example, "This string") 

Search for any memory containing printable ascii strings 

Search for any memory containing printable Unicode strings 

Length = minimum length of such strings; the default is 3 chars 

Search for objects of the same type. 

Object = Addr of a pointer to the Object or of the Object itself 



w = search only writable memory 

1 = output only addresses of search matches (useful if you are using the .foreach)

Flags must be surrounded by a single set of brackets without spaces. 

Example: s -[swl 10]Type Range Pattern


.holdmem -a Range 

.holdmem -o 

.holdmem -c Range 

.holdmem -D 

.holdmem -d { Range | Address }

Hold and compare memory. The comparison is made byte-for-byte 

Memory range to safe 

Display all saved memory ranges 

Compares Range to all saved memory ranges 

Delete all saved memory ranges 

Delete specified memory ranges (any saved range containing Addr or overlapping with Range)

c Addr (Addr+100) DestAddr compare 100 bytes at Addr with DestAddr
c Addr L100 DestAddr -||-
m Addr L20 DestAddr move 20 bytes from Addr to DestAddr
f Addr L20 ‘A’ ‘B’ ‘C’ fill specified memory location with the pattern "ABC", repeated several times
f Addr L20 41 42 43 -||-
s 0012ff40 L20 ‘H’ ‘e’ ‘l’ ‘l’ ‘o’ search memory locations 0012FF40 through 0012FF5F for the pattern "Hello"
s 0012ff40 L20 48 65 6c 6c 6f -||-
s -a 0012ff40 L20 "Hello" -||-
s -[w]a 0012ff40 L20 "Hello" search only writable memory

20) Memory: Heap
Cmd Variants / Params Description


!heap -? 


!heap -h 

!heap -h [HeapAddr | Idx | 0] 

!heap -v [HeapAddr | Idx | 0] 

!heap -s [HeapAddr | 0] 

!heap -i [HeapAddr] 

!heap -x [-v] Address 

!heap -l

Brief help 

List heaps with index and HeapAddr 

List heaps with index and range (= startAddr(=HeapAddr), endAddr) 

Detailed heap info [Idx = heap Idx, 0 = all heaps] 

Validate heap [Idx = heap Idx, 0 = all heaps] 

Summary info, i.e. reserved and committed memory [Idx = heap Idx, 0 = all heaps] 

Detailed info for a block at given address 

Search heap block containing the address (v = search the whole process virtual space) 

Search for potentially leaked heap blocks

!heap -b, -B

!heap Heap -b [alloc | realloc | free] [Tag] 

!heap Heap -B [alloc | realloc | free]

Set conditional breakpoint in the heap manager [Heap = HeapAddr | Idx | 0] 

Remove a conditional breakpoint

!heap -flt

!heap -flt s Size 

!heap -flt r SizeMin SizeMax

Dump info for allocations matching the specified size 

Filter by range

!heap -stat

!heap -stat 

!heap -stat -h [HeapHandle | 0]

Dump heap handle list 

Dump usage statistic for every AllocSize [HeapHandle = given heap | 0 = all heaps]. 

The statistic includes AllocSize, #blocks, TotalMem for each AllocSize.

!heap -p

!heap -p -? 

!heap -p 

!heap -p -h HeapHandle 

!heap -p -a UserAddr 

!heap -p -all 

Extended page heap help 

Summary for NtGlobalFlag, HeapHandle + NormalHeap list ** 

Detailed info about a page heap with Handle 

Details of heap allocation containing UserAddr. Prints backtraces when available. 

Details of all allocations in all heaps in the process. 

The output includes UserAddr and AllocSize for every HeapAlloc call.

It seems that the following applies for windows XP SP2:

a) Normal heap

CreateHeap -> creates a _HEAP

AllocHeap -> creates a _HEAP_ENTRY

b) Page heap enabled (gflags.exe /i +hpa)

CreateHeap -> creates a _DPH_HEAP_ROOT (+ _HEAP + 2x _HEAP_ENTRY)**

AllocHeap -> creates a _DPH_HEAP_BLOCK

** With page heap enabled there will still be a _HEAP with two constant _HEAP_ENTRY’s for every CreateHeap call. 

Term Description Heap type
HeapHandle = value returned by HeapCreate or GetProcessHeap 

For normal heap: HeapHandle == HeapStartAddr

Normal & page
HeapAddr = startAddr = NormalHeap Normal & page
UserAddr, UserPtr = value in the range [HeapAlloc…HeapAlloc+AllocSize] 

For normal heap this range is further within Heap[startAddr-endAddr]

Normal & page
UserSize = AllocSize (value passed to HeapAlloc) Normal & page
_HEAP = HeapHandle = HeapStartAddr 

For every HeapCreate a _HEAP struct is created. 

You can use "!heap -p -all" to get these addresses.

Normal heap
_HEAP_ENTRY For every HeapAlloc a _HEAP_ENTRY is created. 

You can use "!heap -p -all" to get these addresses.

Normal heap
_DPH_HEAP_ROOT = usually HeapHandle + 0x1000 

For every HeapCreate a _DPH_HEAP_ROOT is created. 

You can use "!heap -p -all" to get these addresses.

Page heap
_DPH_HEAP_BLOCK For every HeapAlloc a _DPH_HEAP_BLOCK is created. 

You can use "!heap -p -all" to get these addresses.

Page heap

dt ntdll!_HEAP dump _HEAP struct
dt ntdll!_DPH_HEAP_ROOT dump _DPH_HEAP_ROOT struct. 

Enable page heap. Then you can use "!heap -p -all" to get addresses of actual _DPH_HEAP_ROOT structs in your process.

dt ntdll!_DPH_HEAP_BLOCK dump _DPH_HEAP_BLOCK struct. 

Enable page heap. Then you can use "!heap -p -all" to get addresses of actual _DPH_HEAP_BLOCK structs in your process.

!heap list all heaps with index and HeapAddr
!heap -h list all heaps with range information (startAddr, endAddr)
!heap -h 1 detailed heap info for heap with index 1
!heap -s 0 Summary for all heaps (reserved and committed memory, ..)
!heap -flt s 20 Dump heap allocations of size 20 bytes
!heap -stat Dump HeapHandle list. HeapHandle = value returned by HeapCreate or GetProcessHeap
!heap -stat -h 00150000 Dump usage statistic for HeapHandle = 00150000
!heap 2 -b alloc mtag Breakpoint on HeapAlloc calls with TAG=mtag in heap with index 2
!heap -p Dump heap handle list
!heap -p -a 014c6fb0 Details of heap allocation containing address 014c6fb0 + call-stack if available
!heap -p -all Dump details of all allocations in all heaps in the process

Who allocated memory – who called HeapAlloc?

Select "Create user mode stack trace database" for your image in GFlags (gflags.exe /i +ust)

From WinDbg’s command line do a !heap -p -a , where is the address of your allocation ***.

While !heap -p -a will dump a call-stack, no source information will be included. 

To get source information you must additionally enable page heap in step 1 (gflags.exe /i +ust +hpa)

Do a dt ntdll!_DPH_HEAP_BLOCK StackTrace , where is the DPH_HEAP_BLOCK address retrieved in step 3.

Do a dds ", where is the value retrieved in step 5. 

Note that dds will dump the stack with source information included.

Who created a heap – who called HeapCreate?

Select "Create user mode stack trace database" and "Enable page heap" for your image in GFlags (gflags.exe /i +ust +hpa)

a) From WinDbg’s command line do a !heap -p -h , where is the value returned by HeapCreate. You can do a !heap -stat or !heap -p to get all heap handles of your process. 

b) Alternatively you can use !heap -p -all to get addresses of all _DPH_HEAP_ROOT’s of your process directly.

Do a dt ntdll!_DPH_HEAP_ROOT CreateStackTrace , where is the address of a _DPH_HEAP_ROOT retrieved in step 2

Do a dds , where is the value retrieved in step 3.

Finding memory leaks

From WinDbg’s command line do a !address –summary. 

If RegionUsageHeap or RegionUsagePageHeap are growing, then you might have a memory leak on the heap. Proceed with the following steps.

Enable "Create user mode stack trace database" for your image in GFlags (gflags.exe /i +ust)

From WinDbg’s command line do a !heap -stat, to get all active heap blocks and their handles.

Do a !heap -stat -h 0. This will list down handle specific allocation statistics for every AllocSize. 

For every AllocSize the following is listed: AllocSize, #blocks, and TotalMem. Take the AllocSize with maximum TotalMem.

Do a !heap -flt s . =AllocSize that we determined in the previous step. This command will list down all blocks with that particular size.

Do a !heap -p -a to get the stack trace from where you have allocated that much bytes. Use the that you got in step 4. 

To get source information you must additionally enable page heap in step 1 (gflags.exe /i +ust +hpa)

Do a dt ntdll!_DPH_HEAP_BLOCK StackTrace , where is the DPH_HEAP_BLOCK address retrieved in step 5.

Do a dds ", where is the value retrieved in step 7. 

Note that dds will dump the stack with source information included.

*** What is a ?

is usually the address returned by HeapAlloc:

int AllocSyze = 0x100000; 	// == 1 MB
BYTE* pUserAddr = (BYTE*) HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, AllocSyze); 

Often any address in the range [UserAddr….UserAddr+AlloSize] is also a valid parameter:

!heap -p -a [UserAddr....UserAddr+AlloSize]

21) Application Verifier 

Application Verifier profiles and tracks Microsoft Win32 APIs (heap, handles, locks, threads, DLL load/unload, and more), Exceptions, Kernel objects, Registry, File system. With the !avrf extension we get access to this tracking information!

Cmd Variants / Params Description


Displays Application Verifier options. If an Application Verifier Stop has occurred, reveal the nature of the stop and what caused it.



-vs N 

-vs -a ADDR 

-hp N 

-hp -a ADDR 

-cs N 

-cs -a ADDR 

-dlls N 

-ex N 




-trace INDEX 

-brk [INDEX]

Brief help 

Dump last N entries from vspace log (MapViewOfFile, UnmapViewOfFile, ..). 

Searches ADDR in the vspace log. 

HeapAlloc, HeapFree, new, and delete log 

Searches ADDR in the heap log. 

DeleteCriticalSection API log (last #Entries). ~CCriticalSection calls this implicitly. 

Searches ADDR in the critical section delete log. 

LoadLibrary/FreeLibrary log 

exception log 

global counters (WaitForSingleObject, HeapAllocation calls, …) 

thread information + start parameters for child threads 

TerminateThread API log 

dump stack trace with INDEX. 

dump or set/reset break triggers.

22) Logging extension (logexts.dll) 

You must enable the following options for you image in GFlags: 

-> "Create user mode stack trace database" 

-> "Stack Backtrace: (Megs)" -> 10 

-> It seems that you sometimes also need to check and specify the "Debugger" field in GFlags

Cmd Variants / Params Description


displays all Logexts.dll extension commands


!loge [dir]

Enable logging + possibly initialize it if not yet done. Output directory optional.


Initialize (=inject Logger into the target application) but don’t enable logging.


Disable logging



!logo [e|d] [d|t|v]

List output settings 

Enable/disable [d – Debugger, t – Text file, v – Verbose log] output. Use logviewer.exe to examine Verbose logs.



!logc p # 

!logc [e|d] * 

!logc [e|d] # [#] [#]

List all categories 

List APIs in category # 

Enable/disable all categories 

Enable/disable category #


!logb p 

!logb f

Print buffer contents to debugger 

Flush buffer to log files



!logm [i|x] [DLL] [DLL]

Display module inclusion/exclusion list 

Specify module inclusion/exclusion list


Enable 19-ProcessesAndThreads and 22-StringManipulation logging:

!loge Enable logging
!logc d * Disable all categories
!logc p 19 Display APIs of category 19
logc e 19 22 Enable category 19 and 22
!logo d v Disable verbose output
!logo d t Disable text output
!logo e d Enable debugger output

Source: http://windbg.info/doc/1-common-cmds.html