WinDBG

From ReactOS Wiki
Jump to: navigation, search

Introduction

WinDbg (pronounced "Wind bag"), is Microsoft's advanced debugging tool. ReactOS, being very compatible with Windows and able to be compiled with Microsoft's compiler, is able to be debugged in kernel and user mode using WinDbg.

To begin, one needs to compile ReactOS from source using Microsoft's tools. This creates executables and Program DataBase symbols (PDBs), which WinDbg uses to step through the code. One requires the minimal CMake and ninja download (see Build Environment), and a version of Microsoft's compiler (which is available with desktop versions of Visual Studio). See WinDbg Tutorial for details on building.

When booting from the BootCD the first time, you probably won't see anything apart from a black screen, this is usual behaviour. The ReactOS kernel is waiting for WinDbg to attach to it before it starts the boot process proper (since revision 70417 the connection will bail out after approximately 20 seconds, as on a regular debugged Windows). For a virtual machine this can be achieved in several ways, the debugging page has instructions on how to set this up.

ReactOS should connect and start booting for the installation, so install and reboot for the second stage. To use WinDbg, select ReactOS (Debug) from the FreeLoader menu (again it will hang for approximately 20 seconds until connection). Note that when WinDbg is connected, ReactOS boots and performs significantly slower.

To break into the kernel, mash Ctrl + Break in the WinDbg GUI (or Ctrl + C if one is using the command line version). Alternatively, one can press TAB + K in ReactOS to break. From here, one can set breakpoints and step through the source code, but there are a few more things to set up first. The command 'g' is used to restart the machine.

Symbols

Symbols must be used for WinDbg to be really useful, and one can set them for one WinDbg instance or using environment variables. Let's look at environment variables first.

Set _NT_SYMBOL_PATH to C:\path\to\reactos\output-VS-i386\msvc_pdb

Sometimes it's useful to also have Microsoft's symbols available as well, this is fine and doesn't interfere in any untoward way, so one can instead use

C:\path\to\reactos\output-VS-i386\msvc_pdb;SRV*c:\path\to\Documents\symbols\*http://msdl.microsoft.com/download/symbols

Source code

To break into a step through proper C code, go to File -> Open Source File (Ctrl + O) and grab a kernel code file. User space connections are iffy, but covered at the bottom of this page.

Scripted debug

You can use this batch script to set all needed environment variables to start debugging in one click:

@echo off
rem Setting up variables...
set _NT_SOURCE_PATH=C:\path\to\reactos
set _NT_EXECUTABLE_IMAGE_PATH=%_NT_SOURCE_PATH%\output-VS-i386
set _NT_SYMBOL_PATH=%_NT_EXECUTABLE_IMAGE_PATH%\msvc_pdb
rem Starting WinDbg...
start "" "C:\Program Files\Debugging Tools for Windows (x86)\windbg.exe" -k [parameters]

Replace [parameters] with your target parameters.

More details on WinDbg environment variables on MSDN.

On ReactOS

Recent versions of WinDbg don't work in ReactOS due to unimplemented layered windows, and the installers from Microsoft are particularly troublesome. However, a standalone version of WinDbg is available on the net, and the command line versions cdb.exe and ntsd.exe do work very well. This allows debugging user-mode applications in ReactOS and also deferred debugging user-mode code on a host Windows system running WinDbg as the kernel-mode debugger. This can be done with the -d flag when launching the command line versions, for example ntsd.exe -d C:\ReactOS\explorer.exe will start the program explorer.exe and break out to WinDbg at the initial breakpoint. This is particularly useful if one's symbols and source code reside on the host machine.

Research on Windows

ReactOS aims to be compatible with Windows (current target is Server 2003) for essentially all apps, drivers, and even DLLs, so a lot of research is required. WinDbg can assist in this by giving hints about how Windows performs certain operations.

For example: Currently, copy and paste functionality in ReactOS is handled using the default context menu, in Windows, however, the call path goes through the context menu but is quickly pushed into the drag-and-drop implementation. This "dries" the code by avoiding "wet" code (write everything twice). So let's have a look at how to discover this for one's self.

Fire up WinDbg in Windows and ensure you have the Microsoft symbol server configured (see above). Now, attach to the explorer process (F6, then select explorer). Now we know that if we copy and paste a file using the context menu (or Ctrl + C, Ctrl + V) that a file operation is going to be performed as one of the last things done. A quick trip to MSDN shows that SHFileOperation was used prior to Vista, while IFileOperation::CopyItems was used for Vista and later. SHFileOperation lives in shell32 and has two versions: Unicode and ANSI, so set a breakpoint on the Unicode version using bp SHELL32!SHFileOperationW

For Vista it's a little bit more tricky, as IFileOperation is an interface, and we need a class to set the breakpoint on. Using x SHELL32!*::CopyItems tells us that the class is (somewhat logically) called CFileOperation, so we can set a breakpoint on this one with bp SHELL32!CFileOperation::CopyItems

Now restart Explorer with g and copy and paste a file. The process should break and one can use the k command to see the backtrace. By tracking backward, one can find out the calling helper class, see it's run in an async manner, and set additional breakpoints to find out how that was called too.

Useful commands

Command Description
kp generate a backtrace
ta trace to address
x *! full modules list (atm lm is only showing basic modules)
!drvobj <driver_object address> 0x7 dumps driver object details – start/unload + irp handlers
!gflag +soe catch all exceptions (first exception handling) apart from STATUS_PORT_DISCONNECTED or if the exception code is not an error code
!gflag +hpa enable page heap flag turns on page heap verification, which monitors dynamic heap memory operations
!process 0 0 list all process with basic info
!process <addr> 0x1e list detailed info of attached process, with its threads
!process <addr> 0x1f list stack traces for all the threads in the process
.process <addr> attach to the process of a given address
!thread <addr> list info about the thread
.thread <addr> attach to the thread of a given address and set the default thread context in kernel mode
.reload /user reloads user symbols and enables resolving of usermode

User-mode breakpoints

  • With WinDbg attached as a kernel debugger, it is not trivial to set breakpoints on user-mode code. Since every process has its own address space, the debugger does not know which process to set the breakpoint in unless specifically told so.

Since revision 68851, we have .process /i which is capable of doing this by adding a debug worker thread to the target process. Alternatively, as a workaround, you can place a kernel-mode breakpoint (those are always reliable) that is likely to be executed in the context of the right user-mode process.

Using .process /i

  • connect to the target machine as you would when doing kernel debugging
  • break so that you can issue commands
  • list processes using !process 0 0. This should give you something like
PROCESS b0c01020  SessionId: 0  Cid: 01bc    Peb: 7ffda000  ParentCid: 00dc
    DirBase: 148fb000  ObjectTable: e1a9d100  HandleCount:  87.
    Image: svchost.exe

PROCESS b0bfc2a0  SessionId: 0  Cid: 01f4    Peb: 7ffaf000  ParentCid: 01b0
    DirBase: 14d2e000  ObjectTable: e19c24b8  HandleCount:  90.
    Image: explorer.exe

PROCESS b0bb8780  SessionId: 0  Cid: 0204    Peb: 7ffd9000  ParentCid: 00dc
    DirBase: 14e0e000  ObjectTable: e19c4da0  HandleCount:  41.
    Image: svchost.exe
  • attach to the specific process; e.g. for explorer.exe: .process /i b0bfc2a0 . Then continue as told. You will see something like this:
kd> .process /i b0bfc2a0
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
80582948 cc              int     3
  • you can now reload symbols: it might take a while if you're doing this for the first time.
kd> .reload /user
Loading User Symbols
..................

Press ctrl-c (cdb, kd, ntsd) or ctrl-break (windbg) to abort symbol loads that take too long.
Run !sym noisy before .reload to track down problems loading symbols.

.............................



Using kernel mode breakpoints

As an example, bm win32k!NtUserCreateWindowEx will break whenever a window is created*. By executing .reload /user after this breakpoint is hit, you will be able to look at a backtrace (that hopefully includes the right user-mode symbols) and judge whether you are in the right process at the right time. Once that is achieved, you can place user-mode breakpoints in the context of this process for further examination.

Further tricks

* when debugging issues during OS boot, be sure to break late enough so that win32k is loaded, though — e.g. you can be sure of this by breaking right after the display mode change that makes the desktop's background color appear.

  • Another trick: the .process /r /p <addr> command (where <addr> is the process address) allows you to quickly reload the user-mode symbols after the process context has been set (this replaces the manual .reload /user command).

References