windbg
WinDbg notes
Working through a new course that encourages WinDBG over other debuggers. I love WinDBG, but I find I forget even the basics because it is so long between drinks.
The unassemble commands:
The first u without an address starts at the current instruction pointer; repeating it continues after the previous disassembly. Use u @eip to be explicit for these x86 examples (@rip for x64). If you have a memory address in mind, use u {address}. Or name a function:
u Kernel32!GetCurrentThread
The display commands:
If it is bytes you want:
db esp
db Kernel32!WriteFile
db displays 0x80 bytes (128 decimal) if you don’t specify a length. The display-memory reference lists the defaults for the other formats.
Substitute in w to display words.
For ASCII representations use the dW or dc.
For unicode: du
As an example of dealing with pointers - consider that you are interested in a parameter on the stack esp+4 but you know that it is a pointer. It’d be tempting to do:
0:007> dd esp+4
0:007> u 777be222
But if we already know we’re dealing with the pointer, probably because we are looking at the documents for the function, we can tell the debugger to do the steps for us. Use
u poi(esp+4)
Breakpoint:
bp Comdlg32!GetOpenFileNameA
Structures:
We use the display type dt commands.
For example:
dt ntdll!_TEB
You can also pass the r parameter to be recursive since the structure might have child structures inside.
The example command will show the example structure:
0:007> dt ntdll!_teb
+0x000 NtTib : _NT_TIB
+0x038 EnvironmentPointer : Ptr64 Void
+0x040 ClientId : _CLIENT_ID
+0x050 ActiveRpcHandle : Ptr64 Void
+0x058 ThreadLocalStoragePointer : Ptr64 Void
+0x060 ProcessEnvironmentBlock : Ptr64 _PEB
+0x068 LastErrorValue : Uint4B
+0x06c CountOfOwnedCriticalSections : Uint4B
<snip>
What’s more interesting is to look at the populated structure using the $teb pseudo-register:
dt -r ntdll!_TEB @$teb
0:007> dt -r ntdll!_teb @$teb
<snip>
+0x050 DllPath : _UNICODE_STRING ""
+0x060 ImagePathName : _UNICODE_STRING "C:\WINDOWS\system32\notepad.exe"
+0x070 CommandLine : _UNICODE_STRING ""C:\WINDOWS\system32\notepad.exe" "
+0x080 Environment : 0x00000259`1dc60fe0 Void
+0x088 StartingX : 0
+0x08c StartingY : 0
+0x090 CountX : 0
+0x094 CountY : 0
+0x098 CountCharsX : 0
+0x09c CountCharsY : 0
+0x0a0 FillAttribute : 0
+0x0a4 WindowFlags : 0xc01
+0x0a8 ShowWindowFlags : 1
+0x0b0 WindowTitle : _UNICODE_STRING "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Notepad.lnk"
+0x0c0 DesktopInfo : _UNICODE_STRING "Winsta0\Default"
</snip>
Size of Structures:
To inspect the size of a structure (which will often be a parameter passed to a function):
0:000> ?? sizeof(ntdll!_PEB)
unsigned int 0x468
Writing memory:
We use the e (edit?) commands. For example:
0:000> r
eax=00000000 ebx=00000000 ecx=029ff258 edx=77791670 esi=02b5a000 edi=777136cc
eip=777cbb62 esp=029ff274 ebp=029ff2a0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!LdrpDoDebuggerBreak+0x2b:
777cbb62 cc int 3
0:000> dd esp L1
029ff274 f136b9fa
0:000> ed esp 41414141
0:000> dd esp L1
029ff274 41414141
The size modifiers are the same as with the display commands.
We can work with Ascii using the a version of the e command, or the u version for unicode.
0:000> da esp L5
029ff274 "Yikes"
0:000> ea esp "Rainy"
0:000> da esp L5
029ff274 "Rainy"
While writing exploits we can drop the ascii version straight into memory, and then pull it back out with the db command, giving us the bytes we need for shellcode.
0:000> ea esp "c:\\users\\chad\\payload.exe"
0:000> da esp L19
029ff274 "c:\users\chad\payload.exe"
0:000> db esp L19
029ff274 63 3a 5c 75 73 65 72 73-5c 63 68 61 64 5c 70 61 c:\users\chad\pa
029ff284 79 6c 6f 61 64 2e 65 78-65 yload.exe
Searching Memory:
The search commands are s -
As an example, we’ll put a unique pattern in the process address space, then search:
0:000> ed esp 41424344
0:000> s -d 0 L?80000000 41424344
0229f994 41424344 777136cc 024b0000 00000000 DCBA.6qw..K.....
The example searches for a DWORD with -d. L?80000000 is a range length, not an end address; the ? permits a range larger than the debugger’s normal limit. Here it starts at zero and covers 0x80000000 bytes, the lower 2 GiB. That is useful for this x86 example, but isn’t the whole address space of a 64-bit process. See the debugger’s address-range syntax.
To search for ascii we use -a instead:
0:000> s -a 0 L?80000000 "Microsoft Corporation"
0005357c 4d 69 63 72 6f 73 6f 66-74 20 43 6f 72 70 6f 72 Microsoft Corpor
6b75bb78 4d 69 63 72 6f 73 6f 66-74 20 43 6f 72 70 6f 72 Microsoft Corpor
6bd2b6fc 4d 69 63 72 6f 73 6f 66-74 20 43 6f 72 70 6f 72 Microsoft Corpor
7094322c 4d 69 63 72 6f 73 6f 66-74 20 43 6f 72 70 6f 72 Microsoft Corpor
70945412 4d 69 63 72 6f 73 6f 66-74 20 43 6f 72 70 6f 72 Microsoft Corpor
74b4224c 4d 69 63 72 6f 73 6f 66-74 20 43 6f 72 70 6f 72 Microsoft Corpor
754f6ecc 4d 69 63 72 6f 73 6f 66-74 20 43 6f 72 70 6f 72 Microsoft Corpor
76b470ec 4d 69 63 72 6f 73 6f 66-74 20 43 6f 72 70 6f 72 Microsoft Corpor
772611cc 4d 69 63 72 6f 73 6f 66-74 20 43 6f 72 70 6f 72 Microsoft Corpor
776abf9c 4d 69 63 72 6f 73 6f 66-74 20 43 6f 72 70 6f 72 Microsoft Corpor
The example above shows us that many of the binaries loaded by notepad.exe contain the ascii string “Microsoft Corporation”
Or unicode - u
0:000> s -u 0 L?80000000 "Microsoft Corporation"
0006c8d8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
0006ca00 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
689d97e8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
689d9934 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
6b8cb168 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
6b8cb2dc 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
6bd2ba60 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
6bd2bbb8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
6fec11c8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
6fec131c 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
70943738 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
70943888 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
738fd168 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
738fd2a8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
73c88118 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
73c88274 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
73d591c8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
73d5933c 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
7417a7f8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
7417a948 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
741c2118 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
741c2268 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
741ec118 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
741ec26c 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
743a0168 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
743a02d0 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
74455118 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
74455270 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
7456b118 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
7456b278 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
745d4118 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
745d42a0 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
74b42760 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
74b428c0 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
74de8118 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
74de8240 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
74f381b8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
74f382ec 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
74fd6118 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
74fd6278 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
754f73d0 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
754f751c 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
765311c8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
7653131c 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
7665f168 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
7665f2c4 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
76b47878 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
76b479cc 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
76bd9778 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
76bd98e0 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
76d4b168 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
76d4b2a8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
76d4b428 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
76d4b540 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
770921a8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
77092308 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
772616d0 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
77261830 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
772df168 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
772df2cc 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
7736f118 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
7736f24c 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
773fe168 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
773fe290 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
77528118 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
7752825c 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
775eb1c8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
775eb324 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
776eb030 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
776eb174 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
778201a8 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
778202e0 004d 0069 0063 0072 006f 0073 006f 0066 M.i.c.r.o.s.o.f.
Registers:
We can inspect registers with r
We can manipulate them by passing the register we are interested in, with the assignment operator:
0:000> r edi
edi=777136cc
0:000> r edi=00000001
0:000> r edi
edi=00000001
0:000> r
eax=00000000 ebx=00000000 ecx=0229f978 edx=77791670 esi=024b0000 edi=00000001
eip=777cbb62 esp=0229f994 ebp=0229f9c0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!LdrpDoDebuggerBreak+0x2b:
777cbb62 cc int 3
Breakpoints:
b family :)
bp kernel32!WriteFile for example. But you might have an unresolved symbol you want to nab: bu ole32!WriteStringStream.
bl to list, bd to disable one, be to re-enable, bc to clear.
The really powerful stuff is the automated actions:
bp kernel32!WriteFile ".printf \"WriteFile requested bytes: %u\", poi(esp + 0x0C);.echo;g"
or, with a conditional (like, only tell me if it is an 8 byte write):
bp kernel32!WriteFile ".if (poi(esp + 0x0C) != 8) {gc} .else {.printf \"WriteFile requested an 8 byte write\";.echo;}"
These stop at function entry in an x86 process. esp+0x0C holds the requested byte count, not the bytes written or proof of a successful write. The x64 calling convention puts the first arguments in registers, so the same stack offset won’t apply there.
Hardware breakpoint can help monitor access or changes to memory:
//search for the string we've saved in notepad
s -a 0x0 L?80000000 chadchad
093d7718 63 68 61 64 63 68 61 64-00 00 00 00 00 00 00 00 chadchad........
// Set breakpoint
ba w 2 093d7718
// Break on a write to these two bytes, not on a read.
Hardware breakpoints don’t need a software breakpoint (INT 3) inserted: ba e 1 Kernel32!WriteFile breaks on execution. In the earlier example, w means write; r means read or write on x86/x64. The available access types and sizes depend on the architecture. Microsoft’s ba reference lists them.
A very handy example is the winsock receive:
bp wsock32!recv
Stepping:
p steps over a function.
t steps into a function
pt will step to the next return instruction.
ph will continue to the next branch.
Examine Symbols:
x KernelBase!CreateNamed*
Formats:
Handy, it’ll show you the formated values of an input (decimal, hex etc).
.formats 41414141
SEH Work
!exchain is very handy to inspect the SEH chain.
Heap
Overview - get the current Heap addresses for the process:
0:001> !heap
NtGlobalFlag enables following debugging aids for new heaps: tail checking
free checking
validate parameters
Index Address Name Debugging options enabled
1: 00450000 tail checking free checking validate parameters
2: 006b0000 tail checking free checking validate parameters
0:001> !heap stat
Index Address Name Debugging options enabled
1: 00450000
Segment at 00450000 to 00550000 (00100000 bytes committed)
Segment at 00550000 to 00650000 (00100000 bytes committed)
Segment at 006c0000 to 008c0000 (00023000 bytes committed)
2: 006b0000
Segment at 006b0000 to 006c0000 (00005000 bytes committed)
!heap -h 00450000 or ->
0:001> !heap -stat -h 00450000
heap @ 00450000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
5000 64 - 1f4000 (99.12)
1534 1 - 1534 (0.26)
cbc 1 - cbc (0.16)
500 1 - 500 (0.06)
440 1 - 440 (0.05)
400 1 - 400 (0.05)
200 2 - 400 (0.05)
78 6 - 2d0 (0.03)
284 1 - 284 (0.03)
220 1 - 220 (0.03)
208 1 - 208 (0.03)
1f0 1 - 1f0 (0.02)
20 c - 180 (0.02)
ce 1 - ce (0.01)
64 2 - c8 (0.01)
3e 3 - ba (0.01)
10 b - b0 (0.01)
90 1 - 90 (0.01)
80 1 - 80 (0.01)
40 2 - 80 (0.01)
When we have an address to inspect:
0:001> !heap -p -a 0x45e900
address 0045e900 found in
_HEAP @ 450000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0045e8f8 0a03 0000 [00] 0045e900 05000 - (busy)
0:001> !heap -x 0x45e900
Entry User Heap Segment Size PrevSize Unused Flags
-----------------------------------------------------------------------------
0045e8f8 0045e900 00450000 00450000 5018 5018 18 busy extra fill
The header, showing the fill baadf00d after the header values when in the debugger.
0:001> dp 0045e8f8
0045e8f8 517a25a5 1800c607 baadf00d baadf00d
0045e908 baadf00d baadf00d baadf00d baadf00d
0045e918 baadf00d baadf00d baadf00d baadf00d
0045e928 baadf00d baadf00d baadf00d baadf00d
0045e938 baadf00d baadf00d baadf00d baadf00d
0045e948 baadf00d baadf00d baadf00d baadf00d
0045e958 baadf00d baadf00d baadf00d baadf00d
0045e968 baadf00d baadf00d baadf00d baadf00d
0:001> dp 0045e8f8 - 0x10
0045e8e8 abababab abababab 00000000 00000000
0045e8f8 517a25a5 1800c607 baadf00d baadf00d
0045e908 baadf00d baadf00d baadf00d baadf00d
0045e918 baadf00d baadf00d baadf00d baadf00d
0045e928 baadf00d baadf00d baadf00d baadf00d
0045e938 baadf00d baadf00d baadf00d baadf00d
0045e948 baadf00d baadf00d baadf00d baadf00d
0045e958 baadf00d baadf00d baadf00d baadf00d
You can start WinDbg with -hd to avoid enabling the debug heap when launching the process. It doesn’t mean “hide debugger”, and it doesn’t conceal the debugger from the application.
If we attach to the binary after it is already running we get different gflag behaviour.
For example:
0:001> !gflag
Current NtGlobalFlag contents: 0x00000000
dp {chunk} with debugger attached after exectution shows none of the baadf00d fill:
0:001> dp 0x53b1c0
0053b1c0 007b00c4 008a9380 00000000 00000000
0053b1d0 00000000 00000000 00000000 00000000
0053b1e0 00000000 00000000 00000000 00000000
0053b1f0 00000000 00000000 00000000 00000000
0053b200 00000000 00000000 00000000 00000000
0053b210 00000000 00000000 00000000 00000000
0053b220 00000000 00000000 00000000 00000000
0053b230 00000000 00000000 00000000 00000000
Same with the tail -
0:001> dp 0x53b1c0 - 0x10
0053b1b0 00000000 00000000 02db8bc3 080f30c7
0053b1c0 007b00c4 008a9380 00000000 00000000
0053b1d0 00000000 00000000 00000000 00000000
0053b1e0 00000000 00000000 00000000 00000000