Use Windows Debuggers for Non-Debugging Tasks

Many people who has been using Emacs for decades were shocked when they heard that Emacs is actually a text editor instead of an operating system.

- vi advocator

Sharing a similar spirit as Emacs, Windows Debuggers are also super good at non-debugging tasks.

Calculator

The builtin expression evaluator of Windows Debuggers can be used as a handy calculator:

 0:000>  ?? 1+2+3
int 0n6
 0:000>  .formats 0x00905a4d
Evaluate expression:
  Hex:     00000000`00905a4d
  Decimal: 9460301
  Octal:   0000000000000044055115
  Binary:  00000000 00000000 00000000 00000000 00000000 10010000 01011010 01001101
  Chars:   ......ZM
  Time:    Mon Apr 20 19:51:41 1970
  Float:   low 1.32567e-038 high 0
  Double:  4.67401e-317

Process Manager

I try not to use the term Task Manager, since the name is already occupied by taskmgr.exe, and we have nothing to do with tasks (taskmgr also has nothing to do with tasks).

 0:000>  .tlist -v windbg* 

Shell

 0:000>  .shell tlist -t
 0:000>  .shell dir
 0:000>  !!dir

Examine Export Table

 cdb.exe -y . -z ntdll.dll -c "x ntdll!*; q"

Binary Editor

The basic idea is to load a portion of file data into the address space of a debuggee, perform some inspection or modification, then write back to the file. WinDBG even has a Memory Window, which makes it a perfect GUI Hex Editor!

To examine the file information such like name, permission and size, use the .shell command.

 0:000>  .shell dir *.netmodule 

10/23/2011  12:12 PM             2,048 bar.netmodule
10/23/2011  12:12 PM             2,048 foo.netmodule

.shell: Process exited
Press ENTER to continue

We can load the file into the debugee's user mode virtual address space, given that the pages are commited.

 0:000>  .dvalloc 0n2048
Allocated 1000 bytes starting at 00020000
 0:000>  .readmem foo.netmodule 20000 L0n2048
Reading 800 bytes.
 0:000> dt ntdll!_IMAGE_DOS_HEADER 20000
   +0x000 e_magic          : 0x5a4d
   +0x002 e_cblp           : 0x90
   +0x004 e_cp             : 3
   +0x006 e_crlc           : 0
   +0x008 e_cparhdr        : 4
   +0x00a e_minalloc       : 0
   +0x00c e_maxalloc       : 0xffff
   +0x00e e_ss             : 0
   +0x010 e_sp             : 0xb8
   +0x012 e_csum           : 0
   +0x014 e_ip             : 0
   +0x016 e_cs             : 0
   +0x018 e_lfarlc         : 0x40
   +0x01a e_ovno           : 0
   +0x01c e_res            : [4] 0
   +0x024 e_oemid          : 0
   +0x026 e_oeminfo        : 0
   +0x028 e_res2           : [10] 0
   +0x03c e_lfanew         : 0n128
 0:000> dt -r ntdll!_IMAGE_NT_HEADERS 20000+0n128
   +0x000 Signature        : 0x4550
   +0x004 FileHeader       : _IMAGE_FILE_HEADER
      +0x000 Machine          : 0x14c
      +0x002 NumberOfSections : 2
      +0x004 TimeDateStamp    : 0x4ea39447
      +0x008 PointerToSymbolTable : 0
      +0x00c NumberOfSymbols  : 0
      +0x010 SizeOfOptionalHeader : 0xe0
      +0x012 Characteristics  : 0x2102
   +0x018 OptionalHeader   : _IMAGE_OPTIONAL_HEADER
      +0x000 Magic            : 0x10b
      +0x002 MajorLinkerVersion : 0x8 ''
      +0x003 MinorLinkerVersion : 0 ''
      +0x004 SizeOfCode       : 0x400
      +0x008 SizeOfInitializedData : 0x200
      +0x00c SizeOfUninitializedData : 0
      +0x010 AddressOfEntryPoint : 0x22de
      +0x014 BaseOfCode       : 0x2000
      +0x018 BaseOfData       : 0x4000
      +0x01c ImageBase        : 0x400000
      +0x020 SectionAlignment : 0x2000
      +0x024 FileAlignment    : 0x200
      +0x028 MajorOperatingSystemVersion : 4
      +0x02a MinorOperatingSystemVersion : 0
      +0x02c MajorImageVersion : 0
      +0x02e MinorImageVersion : 0
      +0x030 MajorSubsystemVersion : 4
      +0x032 MinorSubsystemVersion : 0
      +0x034 Win32VersionValue : 0
      +0x038 SizeOfImage      : 0x6000
      +0x03c SizeOfHeaders    : 0x200
      +0x040 CheckSum         : 0
      +0x044 Subsystem        : 3
      +0x046 DllCharacteristics : 0x8540
      +0x048 SizeOfStackReserve : 0x100000
      +0x04c SizeOfStackCommit : 0x1000
      +0x050 SizeOfHeapReserve : 0x100000
      +0x054 SizeOfHeapCommit : 0x1000
      +0x058 LoaderFlags      : 0
      +0x05c NumberOfRvaAndSizes : 0x10
      +0x060 DataDirectory    : [16] _IMAGE_DATA_DIRECTORY
         +0x000 VirtualAddress   : 0
         +0x004 Size             : 0
 0:000>  !dh 20000
 0:000>  .writemem foo.netmodule 20000 L0n2048
 0:000>  .dvfree 20000 0

Be cautious while using .dvfree, the debugger simply backed this by VirtualFreeEx:

 kernel32!VirtualFreeEx
dbgeng!LiveUserDebugServices::FreeVirtual+0x1e
dbgeng!LiveUserTargetInfo::FreeVirtual+0x47
dbgeng!DotDvFree+0xcb
dbgeng!DotCommand+0x3f
dbgeng!ProcessCommands+0x4e1
dbgeng!ProcessCommandsAndCatch+0x49
dbgeng!Execute+0x2b9
dbgeng!DebugClient::ExecuteWide+0x6a
windbg!ProcessCommand+0x156
windbg!ProcessEngineCommands+0xb2
windbg!EngineLoop+0x366
kernel32!BaseThreadStart+0x37

This means  .dvfree can be used to free any block of virtual memory owned by the target process.

When .dvfree is used without the /d option, it would use MEM_RELEASE instead of MEM_DECOMMIT (this can be verified with .dbgdbg), and you have to make sure BaseAddress is the value returned by  !address and Size is always zero.