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:
- On the Registry menu, click Import Registry File
- In the Import Registry File dialog box, select the
windbg.regfile 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.
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
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
cmd.exeand 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\*http://msdl.microsoft.com/download/symbols
- You can replace
c:\windows\system32\kernel32.dllwith 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,
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:
- Then force a reload of the symbols:
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.
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.
!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.
.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.
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.
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.
lm -a 400000 would show the module that is loaded at the address 0x400000.
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.
Examine the symbols for a given module.
x kernel32!* will list all the symbols for
Go! This will run the debugger.
Add a breakpoint at the address. You can also use pseudo-registers, and symbols instead of address.
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.
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
See this Microsoft explanation on Unresolved Breakpoints.
List all breakpoints.
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.
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).
Microsoft full list of commands which is terribly formatted!