// This post will be updated regularily. Don’t rely on it, i’m learning.
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:
If you do u
on it’s own the debugger will assume you want to unassemble from EIP. If you have a memory address in mind u {address}
. If that’s not what you want, and you have a function in mind use:
u Kernel32!GetCurrentThread
The display commands:
If it’s bytes you want:
db esp
db Kernel32!WriteFile
80 bytes is the default amount you’ll get if you don’t specify.
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(ebp+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 a psuedo register like:
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 uses a DWORD -d
as the type we want to search; it starts at address 0 and goes up 8000000
- we use the L?
modifer for the top of the range.
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 has just written is: %p\", poi(esp + 0x0C);.echo;g"
or, with a conditional (like, only tell me if it’s an 8 byte write):
bp kernel32!WriteFile ".if (poi(esp + 0x0C) != 8) {gc} .else {.printf \"WriteFile just did the 8 byte write\";.echo;}"
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
// now if it's modified or accessed we will break.
Hardware breakpoints don’t need to software breakpoint (INT 3
) inserted: ba e 1 Kernel32!WriteFile
where e is execute access (could have been read or write) and 1 is the number of bytes.
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
“hide debugger” to remove the gflags resposible for this.
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