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.
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:
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
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:
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.
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.
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.
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 analyze different execution paths took due to our modification.
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.