All Articles

Trying the WinDbg User-Mode Debugging Tutorial

This page has been machine-translated from the original page.

My goal is to become proficient with WinDbg for Windows debugging and dump-based troubleshooting.

This time, I started by reproducing the steps in the official tutorial so I could try user-mode process debugging with WinDbg.

For a full list of articles on Windows debugging and dump analysis with WinDbg, see the page below.

Reference: Debugging and Troubleshooting Techniques with WinDbg

This article covers the following topics.

Table of Contents

What Is WinDbg?

WinDbg is a tool used for debugging and troubleshooting in Windows environments.

Reference: Getting Started with Windows Debugging - Windows drivers | Microsoft Docs

WinDbg is mainly used for the following purposes.

  • Analyzing Windows memory dumps and process dumps
  • Live debugging in kernel mode
  • Live debugging in user mode

It is Microsoft’s official debugger, and it is also used by the Windows development team.

One difference from the Visual Studio debugger is that WinDbg can also handle kernel-mode debugging and thread stack analysis.

WinDbg Tutorial

As a first step with WinDbg, I followed the official tutorial.

In this tutorial, we attach WinDbg to a user-mode process and debug it.

Environment Used This Time

The environment used this time is as follows.

  • Windows 10 20H2
  • WinDbg 10.0.22000.1 AMD64 (launched with administrator privileges)

Launching Notepad.exe

After starting WinDbg in Windows, press [Ctrl+E] to open [Open Executable File], then select C:\Windows\System32\notepad.exe.

When I opened the executable file, the Command window appeared.

Setting and Loading the Symbol Path

Enter the following command in the console at the bottom of the Command window.

.sympath srv*

The .sympath command sets the symbol path. The symbol path is the search location that the debugger uses when looking for symbol files.

Symbol files are required for the debugger to obtain information about code modules (such as function names and variable names).

Reference: .sympath (Set Symbol Path) - Windows drivers | Microsoft Docs

Now that the symbol path has been configured, run the following command.

.reload

The .reload command clears symbol information and then reloads it.

Reference: .reload (Reload Module) - Windows drivers | Microsoft Docs

Note that even when you run .reload, there may be no visible response. Proceed once you see output like the following.

0:000> .reload
Reloading current modules
................

Listing Symbols

After confirming the output above, run the x notepad!* command to display the symbols in the Notepad.exe module. If no output appears here, run .reload again.

Reference: x notepad!*

The output was close to 1,500 lines. That’s quite a lot.

Setting a Breakpoint

Next, using the symbol information confirmed above, set a breakpoint on the wWinMain function.

bu notepad!wWinMain

There is no particular output, but you can check the list of breakpoints with the bl command. Reference: bl (Breakpoint List) - Windows drivers | Microsoft Docs

This confirms that a breakpoint has been set on notepad!wWinMain.

0:000> bu notepad!wWinMain
0:000> bl
     0 e Disable Clear  00007ff6`8402c0f8     0001 (0001)  0:**** notepad!wWinMain

Running Notepad.exe

Now that the breakpoint has been set, run the application with the g command. Reference: g (Go) - Windows drivers | Microsoft Docs

Execution stopped when the wWinMain function specified by the breakpoint was called.

0:000> g
ModLoad: 00007ff9`9cd70000 00007ff9`9cda0000   C:\WINDOWS\System32\IMM32.DLL
Breakpoint 0 hit
notepad!wWinMain:
00007ff6`8402c0f8 488bc4          mov     rax,rsp

Displaying the List of Code Modules Loaded in the Process

With the process stopped at the breakpoint, run the lm command to check the code modules currently loaded in the process. Reference: lm (List Loaded Modules) - Windows drivers | Microsoft Docs

0:000> lm
start             end                 module name
00007ff6`84020000 00007ff6`8405a000   notepad    (pdb symbols)          C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\sym\notepad.pdb\6539CE998C7CAFD73A8E13A54542E1121\notepad.pdb
00007ff9`8ef30000 00007ff9`8f1ca000   COMCTL32   (deferred)             
00007ff9`982c0000 00007ff9`98350000   apphelp    (deferred)             
00007ff9`9aa40000 00007ff9`9aadd000   msvcp_win   (deferred)             
00007ff9`9ab90000 00007ff9`9ae59000   KERNELBASE   (deferred)             
00007ff9`9aec0000 00007ff9`9afc0000   ucrtbase   (deferred)             
00007ff9`9b010000 00007ff9`9b11b000   gdi32full   (deferred)             
00007ff9`9b340000 00007ff9`9b362000   win32u     (deferred)             
00007ff9`9bbc0000 00007ff9`9bcea000   RPCRT4     (deferred)             
00007ff9`9bf20000 00007ff9`9c275000   combase    (deferred)             
00007ff9`9c3c0000 00007ff9`9c46e000   shcore     (deferred)             
00007ff9`9cc50000 00007ff9`9cd0e000   KERNEL32   (deferred)             
00007ff9`9cd70000 00007ff9`9cda0000   IMM32      (deferred)             
00007ff9`9cfd0000 00007ff9`9cffb000   GDI32      (deferred)             
00007ff9`9d000000 00007ff9`9d1a1000   USER32     (deferred)             
00007ff9`9d1b0000 00007ff9`9d24e000   msvcrt     (deferred)             
00007ff9`9d290000 00007ff9`9d485000   ntdll      (pdb symbols)          C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\sym\ntdll.pdb\E2BF5EA3ECAA1D5310F1E166306A0BCC1\ntdll.pdb

Displaying a Stack Trace

With the process stopped, run the k command to display a stack trace.

This time, I was able to capture the stack trace at the moment execution stopped in notepad!wWinMain.

0:000> k
 # Child-SP          RetAddr               Call Site
00 00000055`5f11f7b8 00007ff6`840459b6     notepad!wWinMain
01 00000055`5f11f7c0 00007ff9`9cc67034     notepad!__scrt_common_main_seh+0x106
02 00000055`5f11f800 00007ff9`9d2e2651     KERNEL32!BaseThreadInitThunk+0x14
03 00000055`5f11f830 00000000`00000000     ntdll!RtlUserThreadStart+0x21

Resuming Notepad.exe

If you run the g command once more, the paused process resumes and the Notepad application starts.

In this state, the debugger becomes Busy and stops accepting additional command input.

To stop the Notepad process again and continue using the debugger, click the Break button or press [Ctrl+Break].

This stops the Notepad process again, and debugger operations become available once more.

Stopping the Process When a File Is Written

Next, run bu ntdll!ZwWriteFile to set a breakpoint that interrupts the process when a file is written.

0:002> bu ntdll!ZwWriteFile
0:002> bl
     0 e Disable Clear  00007ff6`8402c0f8     0001 (0001)  0:**** notepad!wWinMain
     1 e Disable Clear  00007ff9`9d32ce60     0001 (0001)  0:**** ntdll!NtWriteFile

After entering the g command again to resume the process, try writing and saving content in Notepad, and the process will stop.

If you display a stack trace at this point, you can capture the stack trace at the time of the write.

0:011> k
 # Child-SP          RetAddr               Call Site
00 00000055`5f8fdb78 00007ff9`9bc1f6f4     ntdll!NtWriteFile
01 00000055`5f8fdb80 00007ff9`9bc0c641     RPCRT4!UTIL_WriteFile+0x5c
02 00000055`5f8fdbe0 00007ff9`9bbf5863     RPCRT4!NMP_SyncSend+0x81
03 00000055`5f8fdc60 00007ff9`9bbf2a56     RPCRT4!OSF_CCONNECTION::TransSendReceive+0xf7
04 00000055`5f8fdcd0 00007ff9`9bbf239b     RPCRT4!OSF_CCONNECTION::SendBindPacket+0x2ee
05 00000055`5f8fdf20 00007ff9`9bbf3ed1     RPCRT4!OSF_CCONNECTION::ActuallyDoBinding+0xeb
06 00000055`5f8fdfd0 00007ff9`9bbf3c0e     RPCRT4!OSF_CCONNECTION::OpenConnectionAndBind+0x225
07 00000055`5f8fe080 00007ff9`9bbf7736     RPCRT4!OSF_CCALL::BindToServer+0xce
08 00000055`5f8fe120 00007ff9`9bbf84d6     RPCRT4!OSF_BINDING_HANDLE::InitCCallWithAssociation+0x8a
09 00000055`5f8fe180 00007ff9`9bbf75e7     RPCRT4!OSF_BINDING_HANDLE::AllocateCCall+0x256
0a 00000055`5f8fe2e0 00007ff9`9bca00f5     RPCRT4!OSF_BINDING_HANDLE::NegotiateTransferSyntax+0x37
0b 00000055`5f8fe330 00007ff9`9bca3840     RPCRT4!NdrpClientCall3+0x715
0c 00000055`5f8fe6a0 00007ff9`99bc139e     RPCRT4!NdrClientCall3+0xf0
0d 00000055`5f8fea30 00007ff9`775c1e00     wkscli!NetWkstaGetInfo+0x5e
0e 00000055`5f8feae0 00007ff9`83c62df6     ntlanman!NPOpenEnum+0x50
0f 00000055`5f8fec40 00007ff9`83c61b7f     MPR!MprOpenEnumConnect+0x176

Displaying the List of Threads in the Process

You can use the ~ command to get the list of threads in the process. Reference: ~ (Thread Status) - Windows drivers | Microsoft Docs

It looks like the current Notepad process has the following 14 threads.

0:011> ~
   0  Id: de4.7ac Suspend: 1 Teb: 00000055`5f35c000 Unfrozen
   1  Id: de4.22f0 Suspend: 1 Teb: 00000055`5f36a000 Unfrozen
   2  Id: de4.1500 Suspend: 1 Teb: 00000055`5f36e000 Unfrozen
   3  Id: de4.198c Suspend: 1 Teb: 00000055`5f370000 Unfrozen
   4  Id: de4.2094 Suspend: 1 Teb: 00000055`5f364000 Unfrozen
   5  Id: de4.1d6c Suspend: 1 Teb: 00000055`5f372000 Unfrozen
   6  Id: de4.1048 Suspend: 1 Teb: 00000055`5f368000 Unfrozen
   7  Id: de4.1408 Suspend: 1 Teb: 00000055`5f374000 Unfrozen
   8  Id: de4.30c Suspend: 1 Teb: 00000055`5f376000 Unfrozen
   9  Id: de4.1b18 Suspend: 1 Teb: 00000055`5f378000 Unfrozen
  10  Id: de4.af8 Suspend: 1 Teb: 00000055`5f37a000 Unfrozen
. 11  Id: de4.898 Suspend: 1 Teb: 00000055`5f37e000 Unfrozen
  12  Id: de4.1720 Suspend: 1 Teb: 00000055`5f380000 Unfrozen
  13  Id: de4.37c Suspend: 1 Teb: 00000055`5f382000 Unfrozen

Getting the Stack Trace of a Specific Thread

To get the stack trace of a specific thread, use the following commands in sequence.

~0s
k

The ~0s command switches to thread number 0. Reference: ~s (Set Current Thread) - Windows drivers | Microsoft Docs

The output looked as follows.

0:011> ~0s
win32u!NtGdiGetCharABCWidthsW+0x14:
00007ff9`9b3465e4 c3              ret
0:000> k
 # Child-SP          RetAddr               Call Site
00 00000055`5f117108 00007ff9`9b01a2ae     win32u!NtGdiGetCharABCWidthsW+0x14
01 00000055`5f117110 00007ff9`9b01a211     gdi32full!LoadGlyphMetricsWithGetCharABCWidthsI+0x5e
02 00000055`5f1174b0 00007ff9`9b019d60     gdi32full!LoadGlyphMetrics+0x99
03 00000055`5f1174f0 00007ff9`8aa29016     gdi32full!CUspShapingFont::GetGlyphDefaultAdvanceWidths+0x150
04 00000055`5f117550 00007ff9`9b020b65     TextShaping!ShapingGetGlyphPositions+0x516
05 00000055`5f117750 00007ff9`9b0266e3     gdi32full!ShlPlaceOT+0x255
06 00000055`5f117970 00007ff9`9b025d9b     gdi32full!RenderItemNoFallback+0x573
07 00000055`5f117aa0 00007ff9`9b025c6b     gdi32full!RenderItemWithFallback+0xeb
08 00000055`5f117af0 00007ff9`9b025a3f     gdi32full!RenderItem+0x3b
09 00000055`5f117b40 00007ff9`9b027ac6     gdi32full!ScriptStringAnalyzeGlyphs+0x20f
0a 00000055`5f117bf0 00007ff9`9b024ca2     gdi32full!ScriptStringAnalyse+0x626
0b 00000055`5f117dc0 00007ff9`9b0246be     gdi32full!LpkCharsetDraw+0x5c2
0c 00000055`5f117ff0 00007ff9`9d01f5f2     gdi32full!LpkDrawTextEx+0x5e
0d 00000055`5f118060 00007ff9`9d01e9bf     USER32!DT_DrawStr+0xb6
0e 00000055`5f118110 00007ff9`9d01eede     USER32!DT_GetLineBreak+0xf3
0f 00000055`5f1181b0 00007ff9`9d01eb50     USER32!DrawTextExWorker+0x36e
10 00000055`5f118300 00007ff9`6b8a6123     USER32!DrawTextW+0x40
11 00000055`5f118370 00007ff9`6b89ac60     DUI70!DirectUI::Element::GetContentSize+0x463
12 00000055`5f118450 00007ff9`6b8a7196     DUI70!DirectUI::Element::_UpdateDesiredSize+0x6a0
{{ Omitted below }}

Ending Debugging and Detaching from the Process

Finally, use the qd command to end debugging and detach from the process.

When debugging ended, the Command window closed and the paused write operation in Notepad resumed.

Wrap-up

For now, I worked through the user-mode debugging steps from the official tutorial.

For other published information on Windows debugging and dump analysis with WinDbg, see the list on the following page.

Reference: Debugging and Troubleshooting Techniques with WinDbg