k*命令顯示給定線程的呼叫堆疊,以及其他相關資訊
~0 k表示列印0號線程的呼叫堆疊,直接用k表示列印當前線程的呼叫堆疊
kd> ~0k
- ChildEBP RetAddr
- 0007fddc 77d191be ntdll!KiFastSystemCallRet
- 0007fdfc 010021b0 USER32!NtUserGetMessage+0xc
- 0007ff1c 010125e9 calc!WinMain+0x25f
- 0007ffc0 7c817077 calc!WinMainCRTStartup+0x174
- 0007fff0 00000000 kernel32!BaseProcessStart+0x23
- 0:002> k
- ChildEBP RetAddr
- 00bfffc8 7c972119 ntdll!DbgBreakPoint
- 00bffff4 00000000 ntdll!DbgUiRemoteBreakin+0x2d
- 0:002> ~2k
- ChildEBP RetAddr
- 00bfffc8 7c972119 ntdll!DbgBreakPoint
- 00bffff4 00000000 ntdll!DbgUiRemoteBreakin+0x2d
我們注意到2號線程的堆棧,這是windbg建立一個遠程線程來執行DbgUiRemoteBreakin函數,它內部會調用DbgBreakPoint執行斷點指令,以觸發斷點異常,強制把程式斷了下來,所以windbg列印出來的線程總多了一條,所以不要驚訝為什麼線程多了點。
其實我想弄清楚那個ChildEBP/RetAddr分別具體指什麼:先K看下堆棧:
kd> k
- ChildEBP RetAddr
- 0012fb1c 7c95e612 ntdll!DbgBreakPoint
- 0012fc94 7c94108f ntdll!LdrpInitializeProcess+0xffa
- 0012fd1c 7c92e437 ntdll!_LdrpInitialize+0x183
- 00000000 00000000 ntdll!KiUserApcDispatcher+0x7
再開啟反組譯碼視窗:
ntdll!DbgBreakPoint:
- 7c92120e cc int 3
當前運行到這一行:再用r ebp查看下值:
kd> r ebp
- ebp=0012fc94
這個值是LdrpInitializeProcess前面的ChildEBP,F10單步調試到ret(也就是7c92120f)
ntdll!DbgBreakPoint:
- 7c92120e cc int 3
- 7c92120f c3 ret
再F10調試一步,退回到LdrpInitializeProcess中(7c95e612):
7c95e60d e8fc2bfcff call ntdll!DbgBreakPoint (7c92120e)
- 7c95e612 8b4368 mov eax,dword ptr [ebx+68h] ds:0023:7ffd3068=00000070
我們發現這個7c95e612就是DbgBreakPoint的返回地址,也就是返回地址應該是指函數退出後下個EIP的值,我以前還一直以為是那個ret/leave對應的地方,原來是ret運行後的值.
kb 顯示傳遞給堆棧回溯中的每個函數的前三個參數kp 顯示傳遞給堆棧回溯中的每個函數的所有參數。參數列表包含參數的資料類型、名字和值。p命令是區分大小寫。使用該參數需要完整符號資訊。 (事實上我看到的結果和k一樣)kP
和p參數一樣,顯示傳遞給堆棧回溯中的每個函數的所有參數。但是,使用P ,函數參數在第二行中顯示,而不是作為資料的結尾在行末顯示。 (事實上我看到的結果和k一樣)
kd> kb
- ChildEBP RetAddr Args to Child
- 00bfffc8 7c972119 00000005 00000004 00000001 ntdll!DbgBreakPoint
- 00bffff4 00000000 00000000 00000008 000060c0 ntdll!DbgUiRemoteBreakin+0x2d
- 0:002> kp
- ChildEBP RetAddr
- 00bfffc8 7c972119 ntdll!DbgBreakPoint
- 00bffff4 00000000 ntdll!DbgUiRemoteBreakin+0x2d
- </SPAN>
kp在有符號檔案存在時顯示會大不一行,我這貼一個debug下的kp列印:
kd> kp
- ChildEBP RetAddr
- 0012f78c 7c92daea ntdll!KiFastSystemCallRet
- 0012f790 7c932298 ntdll!ZwRequestWaitReplyPort+0xc
- 0012f7b0 7c872a51 ntdll!CsrClientCallServer+0x8c
- 0012f8ac 7c872b98 kernel32!ReadConsoleInternal+0x1be
- 0012f934 7c8018b7 kernel32!ReadConsoleA+0x3b
- 0012f98c 102c207c kernel32!ReadFile+0x64
- 0012fa20 102c19c9 MSVCR90D!_read_nolock(int fh = 0, void * inputbuf = 0x10316740, unsigned int cnt = 0x1000)+0x62c [f:\dd\vctools\crt_bld\self_x86\crt\src\read.c @ 233]
- 0012fa70 10253e43 MSVCR90D!_read(int fh = 0, void * buf = 0x10316740, unsigned int cnt = 0x1000)+0x219 [f:\dd\vctools\crt_bld\self_x86\crt\src\read.c @ 93]
- 0012fa98 102523e8 MSVCR90D!_filbuf(struct _iobuf * str = 0x103113e8)+0x113 [f:\dd\vctools\crt_bld\self_x86\crt\src\_filbuf.c @ 136]
- 0012faf0 10252440 MSVCR90D!getc(struct _iobuf * stream = 0x103113e8)+0x208 [f:\dd\vctools\crt_bld\self_x86\crt\src\fgetc.c @ 76]
- 0012fafc 1025245a MSVCR90D!_fgetchar(void)+0x10 [f:\dd\vctools\crt_bld\self_x86\crt\src\fgetchar.c @ 37]
- 0012fb04 0041160b MSVCR90D!getchar(void)+0xa [f:\dd\vctools\crt_bld\self_x86\crt\src\fgetchar.c @ 47]
- 0012fbe4 004114b2 test2!MyCls::hold(void)+0x2b [d:\project1\test2\test2\test2.cpp @ 28]
- 0012fcec 0041167a test2!foo1(void)+0xa2 [d:\project1\test2\test2\test2.cpp @ 39]
- 0012fdc0 004116ea test2!foo2(void)+0x3a [d:\project1\test2\test2\test2.cpp @ 45]
- 0012fe94 00411743 test2!foo3(void)+0x3a [d:\project1\test2\test2\test2.cpp @ 51]
- 0012ff68 00411ce8 test2!main(void)+0x23 [d:\project1\test2\test2\test2.cpp @ 56]
- 0012ffb8 00411b2f test2!__tmainCRTStartup(void)+0x1a8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 586]
- 0012ffc0 7c817077 test2!mainCRTStartup(void)+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 403]
- 0012fff0 00000000 kernel32!BaseProcessStart+0x23
可以看到,函數參數列表都被顯示出來了.
u 命令顯示指定的記憶體中的程式碼的反組譯碼。
如果要反組譯碼某一個地址,直接用u 命令加地址
kd> u 77d2929a
- USER32!SendMessageW:
- 77d2929a 8bff mov edi,edi
- 77d2929c 55 push ebp
- 77d2929d 8bec mov ebp,esp
- 77d2929f 56 push esi
- 77d292a0 8b750c mov esi,dword ptr [ebp+0Ch]
- 77d292a3 f7c60000feff test esi,0FFFE0000h
- 77d292a9 0f85be800100 jne USER32!SendMessageW+0x11 (77d4136d)
- 77d292af 8b4d08 mov ecx,dword ptr [ebp+8]
如果存在符號檔案,也可以這樣直接加函數名:kd> u user32!SendMessagew
- USER32!SendMessageW:
- 77d2929a 8bff mov edi,edi
- 77d2929c 55 push ebp
- 77d2929d 8bec mov ebp,esp
- 77d2929f 56 push esi
- 77d292a0 8b750c mov esi,dword ptr [ebp+0Ch]
- 77d292a3 f7c60000feff test esi,0FFFE0000h
- 77d292a9 0f85be800100 jne USER32!SendMessageW+0x11 (77d4136d)
- 77d292af 8b4d08 mov ecx,dword ptr [ebp+8]
注意的是,函數只支援全名,你要是寫成u user32!SendMessage,windbg是認不出來的,當然你可以按TAB來讓windbg自動匹配
ub 指示要反組譯碼的地區是向後計算的。如果使用了ubAddress ,反組譯碼地區是以Address結束的8或9條指令。如果用ubAddressLLength文法指定地區,則反組譯碼以Address結尾的指定長度的內容。
kd> ub USER32!SendMessageW
- USER32!SendMessageWorker+0x4ed:
- 77d29290 5b pop ebx
- 77d29291 c9 leave
- 77d29292 c21400 ret 14h
- 77d29295 90 nop
- 77d29296 90 nop
- 77d29297 90 nop
- 77d29298 90 nop
- 77d29299 90 nop
我們可以發現ub的結束後一條剛好是u的開始
同樣如果存在符號檔案,我們可以用uf來反組譯碼整個函數:
uf 命令顯示記憶體中指定函數的反組譯碼代碼。
x命令顯示所有上下文中匹配指定模板的符號。可用字元萬用字元
kd> x user32!send*
- 77d53948 USER32!SendNotifyMessageA = <no type information>
- 77d2fb6b USER32!SendMessageTimeoutA = <no type information>
- 77d6b88f USER32!SendOpenStatusNotify = <no type information>
- 77d6b49e USER32!SendIMEMessageExA = <no type information>
- 77d2d64f USER32!SendNotifyMessageW = <no type information>
- 77d2cdaa USER32!SendMessageTimeoutW = <no type information>
- 77d65b26 USER32!SendHelpMessage = <no type information>
- 77d6b823 USER32!SendMessageToUI = <no type information>
- 77d6b48d USER32!SendIMEMessageExW = <no type information>
- 77d2cd08 USER32!SendMessageTimeoutWorker = <no type information>
- 77d203fc USER32!SendRegisterMessageToClass = <no type information>
- 77d3c2e7 USER32!SendDlgItemMessageA = <no type information>
- 77d2d6db USER32!SendMessageCallbackW = <no type information>
- 77d6b129 USER32!SendMessageCallbackA = <no type information>
- 77d273cc USER32!SendDlgItemMessageW = <no type information>
- 77d61930 USER32!SendWinHelpMessage = <no type information>
- 77d291b3 USER32!SendMessageWorker = <no type information>
- 77d2929a USER32!SendMessageW = <no type information>
- 77d2f3c2 USER32!SendMessageA = <no type information></SPAN>
所以,這個可以用來定位函數,
這裡介紹下字串萬用字元文法
一個星號(*)表示零個或多個字元。這個前面的例子用到了,
一個問號(?)表示任意單個字元,如下例:
kd> x user32!sendMessage?
- 77d2929a USER32!SendMessageW = <no type information>
- 77d2f3c2 USER32!SendMessageA = <no type information></SPAN>
一個井號(#)匹配零個或多個前一個字元。例如,Lo#p 將匹配 "Lp", "Lop", "Loop", "Looop" 等等
一個加號(+)匹配一個或多個前一個字元
如果你需要使用 #、 ?、 [, ]、*、+ 字元本身,必須在這些字元前面加一個反斜線(\)。