WinDbg Malware Analysis Cheat Sheet

A big thanks to our friend Josh for helping with this post. Go follow him over on Twitter for more excellent reverse engineering content!

Workspace Setup

WinDbg workspace customization has been coved in detail by Zach Burlingame over on his site here. To import his custom workspace we have archived the registry key he provides in his post as a gist windbg.reg. Simply download the gist and add it to your registry:

  • Open regedit.exe
  • On the Registry menu, click Import Registry File
  • In the Import Registry File dialog box, select the windbg.reg file you downloaded, and click Open.

Now reopen WinDbg and you should see a nicely formatted workspace. As noted in his Zach's blog post you will need to fix the host specific path information in the workspace after opening it for the first time in WinDbg.

Symbol Setup

In order to take full advantage of WinDbg's features you will need to load the symbols for the DLLs that your malware will load during runtime (or at the very least symbols for Kernel32.dll, and ntdll.dll).

First you will need to download the symbols to your analysis host using the symchk.exe tool. This tool is installed along with WinDbg when you install the Windows SDK.

  • Create a directory to store your symbols in. We use C:\symbols
  • Open cmd.exe and navigate to the path where you have installed the Windows SDK.
  • Use the following command with symchk to download the symbols you need.
    symchk /r c:\windows\system32\kernel32.dll /s SRV*c:\symbols\*
  • You can replace c:\windows\system32\kernel32.dll with the path to the DLL you need to download the symbols for.
    *Remember for 32 bit malware you will need to specify the path to the 32 bit DLLs in the SysWow64 directory. For example, C:\Windows\SysWOW64\kernel32.dll

After you have loaded the symbols you need to your symbols directory you will need to set the symbol path in WinDbg and ask it to load the symbols from your directory. This is a two step process using the WinDbg command line.

  • First tell WinDbg where to find your symbols directory:
    .sympath C:\symbols
  • Then force a reload of the symbols:
    .reload /f

Pseudo-Regisiter Cheat Sheet

Pseudo-Registers are used to quickly reference registers and other important information while debugging. For example, $eax for the eax register,  $scopeip for the in scope instruction pointer, and $exentry for the entrypoint of the executable. They are easily identified by the ($) and (@) symbols.

Microsoft provides a full list of these registers here: Pseudo-Register Syntax.

Exploring Memory

List all memory segments in the process with their permissions and memory type. This is similar to the Memory Map in x64dbg.

!address [virtual address]
List information about the memory segment that contains the virtual address.
For example !address 400000 would show information about the section that contains address 0x40000. This is a quick way to find the start and end of a memory segment if you want to dump it.

.writemem [file name] [start address] [end address]
Dump memory range to file.
For example, .writemem C:\dump.bin 400000 401000 would dump memory starting at 0x40000 and ending at 0x401000 to the dump.bin file.

eb [address] [byte]
Enter one byte into memory at the address.
For example, eb 400000 0xff would change the byte at address 0x400000 to 0xff.
For a full list of enter commands (string, word, etc.) see the Microsoft docs here.

Loaded Modules (DLLs)

List loaded modules (show all the DLLs).

lm -m [pattern]
List loaded modules that match the pattern.
For example, lm -m kernel* would find and list kernel32.dll if it had been loaded into the process space.

lm -a [address]
List the module loaded at that address.
For example, lm -a 400000 would show the module that is loaded at the address 0x400000.

ln [address]
List the nearest symbol to the address. This is a good way to figure out what is near an address if you get lost debugging.

x [module!symbol]
Examine the symbols for a given module.
For example, x kernel32!* will list all the symbols for kernel32.dll.


Go! This will run the debugger.

bp [address]
Add a breakpoint at the address. You can also use pseudo-registers, and symbols instead of address.
For example, bp $exentry will add a breakpoint to the entry point of the PE that spawned the process. And bp Kernel32!GlobalAlloc will add a breakpoint on the GlobalAlloc API.

bu [address]
Adds an unresolved breakpoint. This differs from a standard breakpoint as it is not set on the specific address and can be set on addresses that do not resolve (because a module has not yet been loaded).
If you use bp to set a breakpoint on an address that doesn't resolve it will automatically be converted into a bu.
See this Microsoft explanation on Unresolved Breakpoints.

List all breakpoints.

bc [number]
Clear the breakpoint at that number in the breakpoint list (list them first).
You can also use * to clear all breakpoints.

Data Types and Structures

dt [module!symbol] [address]
List the address a type cast to the symbol specified. This is very useful for examining structures that are returned from API calls.
For example dt -r ntdll!_IMAGE_NT_HEADERS 400100 will display the address at 0x400100 as the struct _IMAGE_NT_HEADERS recursively (also casting all memory pointed to by the struct).

Further Reading...

Check out this more in-depth cheat sheet from Devon Greene, and his nice set of WinDbg debugging tricks.

Talos tutorial for using the JavaScript extension with newer versions of WinDbg.

Microsoft full list of commands which is terribly formatted!