The following provides commands for getting started with WinDbg if you've come from a Linux only background and have only used GDB and LLDB.
Some Windows operating system trials come as virtual hard disk (VHD) images, that state Hyper-V is required to use them. With macOS and VMware Fusion (at the time of writing, VMware Fusion 12.1.2), you can drag the executable files into VMware (the same as with ISO images) and install them normally. No subscriptions are required to obtain access to a DVD or ISO image if you do not have a system that uses Windows as the base operating system.
WinDbg can be configured with Microsoft's public symbols. These are useful when using more advanced commands (such as getting a meaningful backtrace with !heap -p -a
) later on. Create a local directory (such as C:\symbols
), then navigate to File -> Symbol File Path ...
in WinDbg and set the following:
srv*c:\symbols*https://msdl.microsoft.com/download/symbols
Refer to Symbol path for Windows debuggers for further information.
b main
does not work on Windows the way it does on Linux. The following is a reasonably close equivalent for most cases:
bp $exentry
Beginner Reverse Engineering | Part 1: How To Find The Main Function by Marcus Hutchins has detailed information about how to find the main function in a Windows binary.
Use the following to break on an address (in the program's text section):
bp 12345678
As per the Set Breakpoint documentation, such a breakpoint will not be saved in your workspace. If you restart the application with the .restart
command, all of your breakpoints will be gone.
Use bu
instead of bp
if you want your breakpoints to persist across debugging sessions and the .restart
command.
bl
Use the following command, where x
is the number of the breakpoint returned from the bl
command:
bc x
Several can be deleted in one command:
bc 0 1 2 3
g
There are a lot of options for viewing a stacktrace in WinDbg. The following is the most basic:
k
If you are interested in parameters, and do not have full symbols, use the following to display a backtrace with the first three function parameters (for each entry in the backtrace):
kb
Use the tilda:
~
Use the stacktrace command, k
, prefixed with the tilda and the thread's number from the previous command's output, for example:
~2 k
C++ can throw
exceptions. By default, WinDbg breaks when this occurs. Depending on what you are doing, you may cause a lot of exceptions but do not want to stop in the debugger each time.
List exceptions using the following:
sx
For example, by default you would see the following for C++ exceptions (note the break
):
eh - C++ EH exception - break - not handled
If you want to ignore these so you do not have to manually continue, use the following command:
sxi eh
Running sx
again will show the following:
eh - C++ EH exception - ignore - not handled
To enable these exceptions (break when they occur):
sxe eh
WinDbg provides several options for viewing memory. Dumping as double words is a good place to start:
dd 12345
How can I see further ahead? If you start dumping at address 12345, run dd
again, and then keep pressing enter, WinDbg will keep scrolling through the memory:
0:005> dd 12345
00012345 00000000 00000000 00000000 00000000
00012355 00000000 00000000 00000000 00000000
00012365 00000000 00000000 00000000 00000000
00012375 00000000 00000000 00000000 00000000
00012385 00000000 00000000 00000000 00000000
00012395 00000000 00000000 00000000 00000000
000123a5 00000000 00000000 00000000 00000000
000123b5 00000000 00000000 00000000 00000000
0:005> dd
000123c5 00000000 00000000 00000000 00000000
000123d5 00000000 00000000 00000000 00000000
000123e5 00000000 00000000 00000000 00000000
000123f5 00000000 00000000 00000000 00000000
00012405 00000000 00000000 00000000 00000000
00012415 00000000 00000000 00000000 00000000
00012425 00000000 00000000 00000000 00000000
00012435 00000000 00000000 00000000 00000000
0:005>
00012445 00000000 00000000 00000000 00000000
00012455 00000000 00000000 00000000 00000000
00012465 00000000 00000000 00000000 00000000
00012475 00000000 00000000 00000000 00000000
00012485 00000000 00000000 00000000 00000000
00012495 00000000 00000000 00000000 00000000
000124a5 00000000 00000000 00000000 00000000
000124b5 00000000 00000000 00000000 00000000
0:005>
000124c5 00000000 00000000 00000000 00000000
000124d5 00000000 00000000 00000000 00000000
000124e5 00000000 00000000 00000000 00000000
000124f5 00000000 00000000 00000000 00000000
00012505 00000000 00000000 00000000 00000000
00012515 00000000 00000000 00000000 00000000
00012525 00000000 00000000 00000000 00000000
00012535 00000000 00000000 00000000 00000000
If the following is not giving you the expected results:
ds
Use da
instead (the 'a' is for ASCII). Refer to Display String for further information.
Use GFlags to enabled page heap. For example:
"C:\Program Files (x86)\Debugging Tools for Windows (x86)\gflags.exe" -i "C:\path\to\your\binary" +hpa
(gflags.exe
with only the -i
option will display what debugging features are enabled for the specified binary.)
With page heap enabled, the following command can be used to show when a pointer was allocated, or in the case of freed memory, when it was freed. xxxx
represents a memory address, or a register containing the address:
!heap -p -a xxxx
Page heap can be removed by using the previous command, but with -hpa
instead of +hpa
.
If you are using the 64-bit version of WinDbg to debug a 32-bit process, the !heap
command will not work. Install the 32-bit version of WinDbg from the Microsoft Windows Driver Kit (D:\Debuggers\dbg_x86
).
You can use break on access commands. For example, the following sets a read and write break on 12345678. If this were a pointer to some memory of interest, the breakpoint would trigger when the pointer is read or written:
ba r4 12345678
Refer to the Break on Access documentation for further information.
To list all registers:
r
To view a specific register, such as rbx
:
r rbx
.restart
Note that this will remove breakpoints that were not set with bu
.
.detach
!address -f:image
u
To start disassembling at a specific address:
u 12345678
Is my heap executable? Run the following command to check. It accepts a register (if the register contains a memory address) or an address (such as 12345678), for example:
!vprot rcx