[Windows翻译]Windows调试与开发第二部分 – WinDBG 101

原文地址:www.trustwave.com/en-us/resou…

原文作者:

发布时间:2019年11月18日

简介

又见面了! 在我们上一篇关于环境设置的文章之后,现在是时候介绍这个项目的主要工具,WinDBG。WinDBG是一个调试工具,所以将允许我们通过处理汇编指令和内存数据来发现应用程序和内核下的秘密。2017年,微软发布了WinDBG预览版,并通过漂亮的界面(包括黑暗模式!)和TTD(Time-Travel Debugging)等非常酷的功能提高了可怕的水平。你可以在这里下载该工具,所以让我们开始吧!

在这篇博文中,我将写下一些有用的命令供初学者使用,并希望能对这个工具进行一些解释。

调试一个应用程序

对于在WinDBG上调试一个应用程序,你可以选择附加到一个现有的进程上,或者作为一个新的进程运行。

image.png

图1 – 开始调试

选择之后,我们会让进程停止,等待命令的进行。你将有一个与此类似的屏幕。

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
复制代码

启动应用程序

从这里开始,我们可以继续执行。你可以使用菜单上的Go符号或命令。

指令 用途
g 开始或继续执行

当你在调试一个应用程序时,你在进程中加入了一个新的线程,即调试线程。因此,当你停止(Break符号)应用程序时,你将在这个线程的堆栈下,正如上面所确定的那样。为了调试应用程序的线程,你必须换到主线程,为了处理线程,我们有另一个命令。

指令 用途
~ 列出所有线程
~<id>s 设置活动线程
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
复制代码

堆栈框架

用于检查堆栈框架。

指令 用途
k* 显示堆栈框架
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
复制代码

命令行还披露了你正在调试的线程。”0:000>”表示线程0,”0:003>”表示线程3,在这种情况下。

我在这篇文章中使用了上次H2HC CTF挑战赛的二进制数据,我们可能会在未来重新使用,所以请随时下载它。

利用这些数据的信息,让我们来玩一玩,从第0x8行的地址解开数据。

显示数据

指令 用途
u* 从一个特定的地址解开字节的组合
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
复制代码

在第二条命令中,我使用了参数 “l0x15″,这表示我想显示多少个指令行,在本例中是0x15或21。

对于显示来自特定地址的字节,我们应该使用另一条命令。

指令 用途
d* 显示来自一个特定地址的数据
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
复制代码

在这种情况下,”l “将表示特定字节的多少组将被显示。

断点

一个断点将表明在哪里应该停止执行以进行分析,所以让我们重新启动应用程序并对入口函数进行断点。

指令 用途
b* 设置一个断点
.restart 重新启动应用程序

有不同类型的断点,有条件的等等。你应该阅读文档,并根据你的需要进行申请。

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
复制代码

继续执行。

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

断点被击中,现在我们可以继续执行了。

操作步骤

在步入代码时有几个选项,这非常有用,因为我们可以步入下一个调用、下一个返回、下一个分支指令和一个特定的地址,也可以是这些选项的组合。

指令 用途
p* 步入下一条指令
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)
复制代码

此时,如果你想 “跨入 “这个函数h2hc+0x4530,你必须使用 “t “命令,否则,你将跳到当前分支的下一条指令。

指令 用途
t* 执行一条指令
r 打印/修改寄存器的值
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
复制代码

修改数据

当你在调试和分析一个应用程序时,有时你需要修改一个数据来识别应用程序的行为,而没有数据输入的知识会给你带来关于该应用程序的预先信息。

我在有CMP指令的地方设置了一个断点,我们就可以修改内存中的数据,然后分析由于我们的修改而产生的不同执行路径。

指令 用途
e* 在内存中输入数据
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)
复制代码

从应用程序的指令来看,如果来自[rsp+0x28]的值等于0x43483248,它将跳到h2hc+0x14f0。如果不是,它将在这个分支上继续执行。

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)]
复制代码

由于我们能够修改数据,我们采取了跳转(JE)的方式,在执行上达到了不同的执行路径。

如果你需要修改寄存器中的数据,你将需要一个不同的命令。

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
复制代码

结语

在这篇博文之后,我觉得我们可以进一步研究一些应用,因为现在我们知道了如何使用WinDBG(至少有一点)。现在,我希望你已经掌握了WinDBG的基本原理,并且足够你自己做一些基本的分析。这真的不是一个明确的指南,但希望足以激起你对这个工具的好奇心。


www.deepl.com 翻译

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享