Loading...
Blogs & Stories

SpiderLabs Blog

Attracting more than a half-million annual readers, this is the security community's go-to destination for technical breakdowns of the latest threats, critical vulnerability disclosures and cutting-edge research.

Windows Debugging & Exploiting Part 2 - WinDBG 101

Introduction

Hello again! After our previous post about the environment setup, now it is time to cover the main tool of this project, the WinDBG. WinDBG is a debugging tool, so will allow us to discover the secrets under the applications and kernel by working with assembly instructions and memory data. In 2017, Microsoft released the WinDBG Preview and increased the level of awesomeness with a beautiful interface (including dark mode!) and really cool features like TTD (Time-Travel Debugging). You can download the tool here, so let's get to it!

In this blog post, I'll write down some useful commands for starters and hopefully will demystify this tool a little bit.

Debugging an Application

For debugging an application on WinDBG, you can choose to attach to an existing process or you can run as a new process.

Picture1

Figure 1 - Start Debugging

 

After choosing, we will have the process stopped waiting for a command to proceed. You will have a screen similar to this:

Microsoft (R) Windows Debugger Version 10.0.19494.1001 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: C:\Users\mphx2\Desktop\H2HC_CTF_2019\xpl_to_distribute\h2hc.exe

************* Path validation summary **************
Response                         Time (ms) Location
Deferred                                       srv*
Symbol search path is: srv*
Executable search path is: 
ModLoad: 00007ff6`01ed0000 00007ff6`01ee4000   image00007ff6`01ed0000
ModLoad: 00007ffa`2fa60000 00007ffa`2fc50000   ntdll.dll
ModLoad: 00007ffa`2e240000 00007ffa`2e2f2000   C:\WINDOWS\System32\KERNEL32.DLL
ModLoad: 00007ffa`2ccf0000 00007ffa`2cf93000   C:\WINDOWS\System32\KERNELBASE.dll
ModLoad: 00007ffa`2ec10000 00007ffa`2ec7f000   C:\WINDOWS\System32\WS2_32.dll
ModLoad: 00007ffa`2edb0000 00007ffa`2eed0000   C:\WINDOWS\System32\RPCRT4.dll
(fd8.10c): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00007ffa`2fb311dc cc              int 3

 

Starting the Application

From here, we can proceed with the execution. You can use the Go sign on the menu or the command:

 

g

 Start or continue the execution

 

While you are debugging an application, you are adding a new thread to the process, the debugging thread. So when you stop (Break sign) the application you will be under this thread's stack as identified above. For debugging the application’s thread, you must change to the main thread and for dealing with threads we have another command.

 

~

 List all threads

~<id>s

 Set active thread

 

 0:003> ~
    0  Id: fd8.10c Suspend: 1 Teb: 00000000`0053f000 Unfrozen
    1  Id: fd8.130c Suspend: 1 Teb: 00000000`00541000 Unfrozen
    2  Id: fd8.2394 Suspend: 1 Teb: 00000000`00543000 Unfrozen
 .  3 Id: fd8.1e00 Suspend: 1 Teb: 00000000`00545000 Unfrozen
 0:003> ~0s
 ntdll!NtWaitForSingleObject+0x14:
 00007ffa`2fafc144 c3              ret
 0:000> ~
 .  0 Id: fd8.10c Suspend: 1 Teb: 00000000`0053f000 Unfrozen
    1  Id: fd8.130c Suspend: 1 Teb: 00000000`00541000 Unfrozen
    2  Id: fd8.2394 Suspend: 1 Teb: 00000000`00543000 Unfrozen
 #  3 Id: fd8.1e00 Suspend: 1 Teb: 00000000`00545000 Unfrozen

 

Stack Frame

For checking the stack frame:

 

k*

 Display the stack frame

 

 0:003> kb
  # RetAddr           : Args to Child                                                       : Call Site
 00 00007fff`b3eed4db : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!DbgBreakPoint
 01 00007fff`b1ee7bd4 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!DbgUiRemoteBreakin+0x4b
 02 00007fff`b3e8ced1 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
 03 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
 0:003> ~0s
 ntdll!NtWaitForSingleObject+0x14:
 00007fff`b3ebc144 c3              ret
 0:000> kp
  # Child-SP          RetAddr Call Site
 00 00000000`00eff498 00007fff`b05082ed ntdll!NtWaitForSingleObject+0x14
 01 00000000`00eff4a0 00007fff`b050fd1c mswsock!SockWaitForSingleObject+0x1bd
 02 00000000`00eff540 00007fff`b22657f3 mswsock!WSPAccept+0x50c
 03 00000000`00eff950 00007fff`b2265712 WS2_32!WSAAccept+0xd3
 04 00000000`00eff9d0 00007ff7`85011243 WS2_32!accept+0x12
 05 00000000`00effa10 00007ff7`850115bf h2hc+0x1243
 06 00000000`00effc40 00007ff7`8501165d h2hc+0x15bf
 07 00000000`00effcb0 00007ff7`85011a6b h2hc+0x165d
 08 00000000`00effce0 00007fff`b1ee7bd4 h2hc+0x1a6b
 09 00000000`00effd20 00007fff`b3e8ced1 KERNEL32!BaseThreadInitThunk+0x14
 0a 00000000`00effd50 00000000`00000000 ntdll!RtlUserThreadStart+0x21

 

The command-line also discloses what thread you are debugging: "0:000>" for thread 0 and "0:003>" for thread 3 in this case.

I'm using the binary from the last H2HC CTF challenge for this post and we will probably re-use in the future, so feel free to download it.

Taking advantage of the information from this data, let's play around and unassemble data from address at line 0x8.

 

Displaying Data

u*

 Unassemble the bytes from a specific address

 

0:003> uf h2hc+0x1a6b
h2hc+0x1930:
00007ff7`85011930 48895c2410      mov qword ptr [rsp+10h],rbx
00007ff7`85011935 57              push rdi
00007ff7`85011936 4883ec30        sub rsp,30h
00007ff7`8501193a b84d5a0000      mov eax,5A4Dh
00007ff7`8501193f 663905bae6ffff  cmp word ptr [h2hc (00007ff7`85010000)],ax
00007ff7`85011946 7404            je h2hc+0x194c (00007ff7`8501194c)  Branch

h2hc+0x1948:
00007ff7`85011948 33db            xor ebx,ebx
00007ff7`8501194a eb38            jmp h2hc+0x1984 (00007ff7`85011984)  Branch

h2hc+0x194c:
00007ff7`8501194c 486305e9e6ffff  movsxd rax,dword ptr [h2hc+0x3c (00007ff7`8501003c)]
00007ff7`85011953 488d0da6e6ffff  lea rcx,[h2hc (00007ff7`85010000)]
00007ff7`8501195a 4803c1          add rax,rcx
00007ff7`8501195d 813850450000    cmp dword ptr [rax],4550h
00007ff7`85011963 75e3            jne h2hc+0x1948 (00007ff7`85011948)  Branch
[...]
0:003> u h2hc+0x1a6b l0x15
h2hc+0x1a6b:
00007ff7`85011a6b 8bf8            mov edi,eax
00007ff7`85011a6d 89442420        mov dword ptr [rsp+20h],eax
00007ff7`85011a71 85db            test ebx,ebx
00007ff7`85011a73 7507            jne h2hc+0x1a7c (00007ff7`85011a7c)
00007ff7`85011a75 8bc8            mov ecx,eax
00007ff7`85011a77 e8e0190000      call h2hc+0x345c (00007ff7`8501345c)
00007ff7`85011a7c e8f3190000      call h2hc+0x3474 (00007ff7`85013474)
00007ff7`85011a81 eb17            jmp h2hc+0x1a9a (00007ff7`85011a9a)
00007ff7`85011a83 8bf8            mov edi,eax
00007ff7`85011a85 837c244000      cmp dword ptr [rsp+40h],0
00007ff7`85011a8a 7508            jne h2hc+0x1a94 (00007ff7`85011a94)
00007ff7`85011a8c 8bc8            mov ecx,eax
00007ff7`85011a8e e8d5190000      call h2hc+0x3468 (00007ff7`85013468)
00007ff7`85011a93 cc              int 3
00007ff7`85011a94 e8eb190000      call h2hc+0x3484 (00007ff7`85013484)
00007ff7`85011a99 90              nop
00007ff7`85011a9a 8bc7            mov eax,edi
00007ff7`85011a9c 488b5c2448      mov rbx,qword ptr [rsp+48h]
00007ff7`85011aa1 4883c430        add rsp,30h
00007ff7`85011aa5 5f              pop rdi
00007ff7`85011aa6 c3              ret

 

On the second command, I used the argument "l0x15" this indicates how many instruction lines I want to display, in this case 0x15 or 21. 

For displaying the bytes as they are from a specific address, we should use another command:

 

d*

 Display data from a specific address

 

0:003> dq h2hc+0x1a6
00007ff7`850101a6  00000000`00000000 00000000`00000000
00007ff7`850101b6  00000000`00000000 00000000`00000000
00007ff7`850101c6  02600000`a0000000 00000000`00000000
00007ff7`850101d6  00000000`00000000 00000000`00000000
00007ff7`850101e6  00747865`742e0000 10000000`80270000
00007ff7`850101f6  04000000`82000000 00000000`00000000
00007ff7`85010206  00200000`00000000 61746164`722e6000
00007ff7`85010216  a0000000`2b8a0000 86000000`2c000000
0:003> dd h2hc+0x1a6
00007ff7`850101a6  00000000 00000000 00000000 00000000
00007ff7`850101b6  00000000 00000000 00000000 00000000
00007ff7`850101c6  a0000000 02600000 00000000 00000000
00007ff7`850101d6  00000000 00000000 00000000 00000000
00007ff7`850101e6  742e0000 00747865 80270000 10000000
00007ff7`850101f6  82000000 04000000 00000000 00000000
00007ff7`85010206  00000000 00200000 722e6000 61746164
00007ff7`85010216  2b8a0000 a0000000 2c000000 86000000
0:003> dw h2hc+0x1a6 l0x10
00007ff7`850101a6  0000 0000 0000 0000 0000 0000 0000 0000
00007ff7`850101b6  0000 0000 0000 0000 0000 0000 0000 0000

In this case, the "l" will indicate how many groups of the specific bytes will be shown. 

 

Breakpoints

A breakpoint will indicate where the execution should be stopped for the analysis, so let's restart the application and breakpoint the entry function.

b*

 Set a breakpoint

.restart

 Restart the application

There are different types of breakpoint, with conditions etc. You should read the documentation and apply for your needs.

 

0:003> .restart
[...]
Microsoft (R) Windows Debugger Version 10.0.19494.1001 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.

CommandLine: C:\Users\mphx2\Desktop\H2HC_CTF_2019\xpl_to_distribute\h2hc.exe

************* Path validation summary **************
Response                         Time (ms) Location
Deferred                                       srv*
Symbol search path is: srv*
Executable search path is: 
ModLoad: 00007ff7`85010000 00007ff7`85024000   image00007ff7`85010000
ModLoad: 00007fff`b3e20000 00007fff`b4010000   ntdll.dll
ModLoad: 00007fff`b1ed0000 00007fff`b1f82000   C:\WINDOWS\System32\KERNEL32.DLL
ModLoad: 00007fff`b0ed0000 00007fff`b1173000   C:\WINDOWS\System32\KERNELBASE.dll
ModLoad: 00007fff`b2250000 00007fff`b22bf000   C:\WINDOWS\System32\WS2_32.dll
ModLoad: 00007fff`b3490000 00007fff`b35b0000   C:\WINDOWS\System32\RPCRT4.dll
(1aa0.1f80): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00007fff`b3ef11dc cc              int 3
0:000> bp h2hc+0x1aa8
0:000> bl
     0 e Disable Clear  00007ff7`85011aa8   0001 (0001) 0:**** h2hc+0x1aa8

 

Continuing the execution:

 

0:000> g
Breakpoint 0 hit
h2hc+0x1aa8:
00007ff7`85011aa8 4883ec28        sub rsp,28h

 The breakpoint is hit and now we can walk on the execution.

 

Steps

There are a few options while stepping into the code, this is extremely useful as we can step to the next call, to the next return, to the next branching instruction and to a specific address and also a combination of these options.

 

p*

 Step into the next instruction

 

0:000> u
h2hc+0x1aa8:
00007ff7`85011aa8 4883ec28        sub rsp,28h
00007ff7`85011aac e87f2a0000      call h2hc+0x4530 (00007ff7`85014530)
00007ff7`85011ab1 4883c428        add rsp,28h
00007ff7`85011ab5 e976feffff      jmp h2hc+0x1930 (00007ff7`85011930)
00007ff7`85011aba cc              int 3
00007ff7`85011abb cc              int 3
00007ff7`85011abc 4c8bdc          mov r11,rsp
00007ff7`85011abf 49895b08        mov qword ptr [r11+8],rbx
0:000> p
h2hc+0x1aac:
00007ff7`85011aac e87f2a0000      call h2hc+0x4530 (00007ff7`85014530)
0:000> p
h2hc+0x1ab1:
00007ff7`85011ab1 4883c428        add rsp,28h
0:000> p
h2hc+0x1ab5:
00007ff7`85011ab5 e976feffff      jmp h2hc+0x1930 (00007ff7`85011930)

 

At this point, if you want to "Step Into" this function h2hc+0x4530, you must use the command "t", otherwise, you will jump to the next instruction on the current branch.

 

t*

 Executes a single instruction

r

 Print/modify registers value

 

0:000> g
Breakpoint 0 hit
h2hc+0x1aa8:
00007ff7`85011aa8 4883ec28        sub rsp,28h
0:000> p
h2hc+0x1aac:
00007ff7`85011aac e87f2a0000      call    h2hc+0x4530 (00007ff7`85014530)
0:000> t
h2hc+0x4530:
00007ff7`85014530 48895c2418      mov qword ptr [rsp+18h],rbx ss:00000000`0073f9e0=0000000000000000
0:000> t
h2hc+0x4535:
00007ff7`85014535 57              push rdi
0:000> u
h2hc+0x4535:
00007ff7`85014535 57              push rdi
00007ff7`85014536 4883ec20        sub rsp,20h
00007ff7`8501453a 488b059f940000  mov rax,qword ptr [h2hc+0xd9e0 (00007ff7`8501d9e0)]
00007ff7`85014541 488364243000    and qword ptr [rsp+30h],0
00007ff7`85014547 48bf32a2df2d992b0000 mov rdi,2B992DDFA232h
00007ff7`85014551 483bc7          cmp rax,rdi
00007ff7`85014554 740c            je h2hc+0x4562 (00007ff7`85014562)
00007ff7`85014556 48f7d0          not rax
0:000> prt
rax=00002b992ddfa233 rbx=0000000000000000 rcx=0000000000000000
rdx=000000141594f374 rsi=0000000000000000 rdi=0000000000000000
rip=00007ff7850145e2 rsp=000000000073f9c8 rbp=0000000000000000
 r8=00000000004e3083  r9=000000007ffea000 r10=0000000000000001
r11=ffff66a25ca88b54 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b ds=002b  es=002b fs=0053 gs=002b             efl=00000206
h2hc+0x45e2:
00007ff7`850145e2 c3              ret

 

Modifying Data

While you are debugging and analyzing an application, sometimes you will need to modify a data to identify the application's behavior with no data input knowledge that will give you advance information about the application.

I set a breakpoint where there is a CMP instruction, we will able to modify data on memory and then analyse different execution paths took due to our modification.

 

e*

 Enter data in memory

 

Breakpoint 0 hit
h2hc+0x14d8:
00007ff7`850114d8 817c242848324843 cmp     dword ptr [rsp+28h],43483248h ss:00000000`00eff9e8=41414141
0:000> u
h2hc+0x14d8:
00007ff7`850114d8 817c242848324843 cmp     dword ptr [rsp+28h],43483248h
00007ff7`850114e0 740e            je h2hc+0x14f0 (00007ff7`850114f0)
00007ff7`850114e2 488d0dbfbb0000  lea rcx,[h2hc+0xd0a8 (00007ff7`8501d0a8)]
00007ff7`850114e9 e8d2fcffff      call h2hc+0x11c0 (00007ff7`850111c0)
00007ff7`850114ee eb0e            jmp h2hc+0x14fe (00007ff7`850114fe)
00007ff7`850114f0 8b54242c        mov edx,dword ptr [rsp+2Ch]
00007ff7`850114f4 488b4c2440      mov rcx,qword ptr [rsp+40h]
00007ff7`850114f9 e882feffff      call h2hc+0x1380 (00007ff7`85011380)

From the application's instructions, if the value from [rsp + 0x28] is equal 0x43483248, it will jump to h2hc+0x14f0. If not, it will continue the execution on this branch.

 

0:000> dq 00000000`00eff9e8 l0x1
00000000`00eff9e8  00000000`41414141
0:000> t
h2hc+0x14e0:
00007ff7`850114e0 740e            je h2hc+0x14f0 (00007ff7`850114f0) [br=0]
0:000> t
h2hc+0x14e2:
00007ff7`850114e2 488d0dbfbb0000  lea rcx,[h2hc+0xd0a8 (00007ff7`8501d0a8)]

In this case, it was not the value expected for jumping to h2hc+0x14f0.

So let's restart the application, set the breakpoint again and modify this data.

 

Breakpoint 0 hit
h2hc+0x14d8:
00007ff7`850114d8 817c242848324843 cmp     dword ptr [rsp+28h],43483248h ss:00000000`005bfe38=41414141
0:000> u
h2hc+0x14d8:
00007ff7`850114d8 817c242848324843 cmp     dword ptr [rsp+28h],43483248h
00007ff7`850114e0 740e            je h2hc+0x14f0 (00007ff7`850114f0)
00007ff7`850114e2 488d0dbfbb0000  lea rcx,[h2hc+0xd0a8 (00007ff7`8501d0a8)]
00007ff7`850114e9 e8d2fcffff      call h2hc+0x11c0 (00007ff7`850111c0)
00007ff7`850114ee eb0e            jmp h2hc+0x14fe (00007ff7`850114fe)
00007ff7`850114f0 8b54242c        mov edx,dword ptr [rsp+2Ch]
00007ff7`850114f4 488b4c2440      mov rcx,qword ptr [rsp+40h]
00007ff7`850114f9 e882feffff      call h2hc+0x1380 (00007ff7`85011380)
0:000> eq 00000000`005bfe38 0x43483248
0:000> dqs 00000000`005bfe38 l0x1
00000000`005bfe38  00000000`43483248
0:000> t
h2hc+0x14e0:
00007ff7`850114e0 740e            je h2hc+0x14f0 (00007ff7`850114f0) [br=1]
0:000> t
h2hc+0x14f0:
00007ff7`850114f0 8b54242c        mov edx,dword ptr [rsp+2Ch] ss:00000000`005bfe3c=00000000

 

As we were able to modify the data, we took the jump (JE) and reached a different execution path on the execution.

If you need to modify the data from a register, you will need a different command:

0:000> r
rax=0000000000000008 rbx=0000000000000000 rcx=00000000ffffffff
rdx=0000000000000001 rsi=0000000000000000 rdi=0000000000000000
rip=00007ff7850114f0 rsp=00000000005bfe10 rbp=0000000000000000
 r8=00000000005bdef8  r9=00000000005be070 r10=0000000000000000
r11=0000000000000246 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b ds=002b  es=002b fs=0053 gs=002b             efl=00000246
h2hc+0x14f0:
00007ff7`850114f0 8b54242c        mov edx,dword ptr [rsp+2Ch] ss:00000000`005bfe3c=00000000
0:000> r @rax=0x4141414141414141
0:000> r
rax=4141414141414141 rbx=0000000000000000 rcx=00000000ffffffff
rdx=0000000000000001 rsi=0000000000000000 rdi=0000000000000000
rip=00007ff7850114f0 rsp=00000000005bfe10 rbp=0000000000000000
 r8=00000000005bdef8  r9=00000000005be070 r10=0000000000000000
r11=0000000000000246 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b ds=002b  es=002b fs=0053 gs=002b             efl=00000246
h2hc+0x14f0:
00007ff7`850114f0 8b54242c        mov edx,dword ptr [rsp+2Ch] ss:00000000`005bfe3c=00000000

 

Conclusion

After this blog post, I feel that we could go further and investigate some applications since now we know how to work (at least a bit) with WinDBG. For now, I hope you got the fundamentals of WinDBG and enough for doing some basic analysis on your own. This is really not a definitive guide, but hopefully enough to pique your curiosity about the tool. 

Recent SpiderLabs Blog Posts