Windbg调试命令详解(3)
3 進程與線程
既可以顯示進程和線程列表,又可以顯示指定進程或線程的詳細信息。調試命令可以提供比taskmgr更詳盡的進程資料,在調試過程中不可或缺。
3.1 進程命令
進程命令包括這些內容:顯示進程列表、進程環境塊、設置進程環境。
進程列表
多個命令可顯示進程列表,但一般只能在特定情況下使用,它們是:|、.tlist、!process和!dml_proc。
豎線命令顯示當前被調試進程列表的狀態信息,這個命令在本章開頭已作過介紹,命令格式如下:
- |? [進程號]
請注意這里的定語:被調試進程列表。大多數情況下調試器中只有一個被調試進程,但可以通過.attach或者.create命令同時掛載或創建多個調試對象。當同時對多個進程調試時,進程號是從0開始的整數。下圖中顯示了兩個被調試的進程。
如何在多個進程間進行切換呢?使用s參數即可,這一點前文已然講過。
- .tlist [選項] [模塊名]
? ? ? ? ? ??.tlist命令顯示當前系統中的進程列表,他是目前唯一可在用戶模式下顯示系統當前進程列表的命令。它有兩個可選項:-v顯示進程詳細信息,-c只顯示當前進程信息。
??????????? 內核模式下同樣可以使用.tlist,但更好的命令是!process。!process在內核模式下顯示進程列表,和指定進程的詳細信息,也能顯示進程中的線程和調用棧內容。典型格式如下:
- !process: 顯示調試器當前運行進程信息
- !process 0 0: 顯示進程列表
- !process?PID: PID是進程ID,根據進程ID顯示此進程詳細信息。
- !dml_proc [進程號|進程地址]
此命令可以看成“|”和“!process”命令的DML合并版本,可在用戶與內核模式下使用。顯示的進程信息偏重于線程和調用棧。用戶模式下此命令和“|”一樣,只能顯示被調試進程的信息。右圖是內核模式下使用此命令的效果:
?
進程信息
進程環境塊(Process Enviroment Block)是內核結構體,使用!peb命令參看其信息,但也可以用dt命令查看完整的結構體定義。格式如下:
- !peb ?[地址]
如果未設置PEB地址,則默認為當前進程。內核模式下可通過!process命令獲取PEB結構體地址;用戶模式下只能顯示當前進程的PEB信息,故而一般不帶參數。
- dt ?nt!_peb ?地址
此命令顯示系統nt模塊中所定義的內核結構體PEB詳細內容。使用之前必須先熟悉結構體定義。
進程切換
進程環境的切換,將伴隨著與進程相關的寄存器、堆棧的切換。在不同進程環境中進行的調試結果有天壤之別。上文在講“|”命令的時候,講過用戶環境下多進程間如何互相切換,使用命令:
- |? [進程號]? s
那么內核模式下,情況又不同了。內核模式下的進程切換,不同于用戶模式下的被調試進程間切換,而是系統存在的多進程間切換。內核環境下,以進程地址作為參數,調用如下命令以進行進程環境切換:
- .process [進程地址]
如果不使用任何參數,.process命令將顯示當前進程地址。所謂進程地址,即ERPCESS結構體地址。
或以頁目錄地址為參數,調用下面命令切換用戶地址空間:
- .context [頁目錄地址]
如果不使用任何參數,.context命令將顯示當前頁目錄地址。頁目錄地址就是!process命令中顯示的DirBase值。
進程切換后,為了檢測是否正確切換,可再用!peb命令檢查當前進程的環境信息。
3.2 線程命令
命令“~”能夠進行線程相關的操作。不帶任何參數的情況下,它列出當前調試進程的線程。下圖是計算器進程某時刻的線程列表:
||0:0:001> ~ # 0 Id: f78.374 Suspend: 0 Teb: 000007ff`fffdc000 Unfrozen . 1 Id: f78.fb0 Suspend: 0 Teb: 000007ff`fffda000 Unfrozen2 Id: f78.a4c Suspend: 0 Teb: 000007ff`fffd8000 Unfrozen3 Id: f78.22c8 Suspend: 0 Teb: 000007ff`fff9a000 Unfrozen4 Id: f78.2658 Suspend: 0 Teb: 000007ff`fffd4000 Unfrozen5 Id: f78.cbc Suspend: 0 Teb: 000007ff`fff96000 Unfrozen6 Id: f78.21ec Suspend: 0 Teb: 000007ff`fffd6000 Unfrozen使用此命令可進行的線程操作包括:線程切換、線程環境、線程時間等。
線程冰凍
參數f與u分別代表freeze和unfress,前者是指凍住指定線程,后者將被冰凍線程解凍。
- ~2f
表示把2號線程凍住,在解凍之前,不再分發CPU時間給它。
若要讓指定線程重新運行,需使用參數u:
- ~2u
針對這兩個命令,下面有一個小實驗:
運行Windbg,并選擇調試記事本程序(Notepad.exe)。程序起來后,CTL+BREAK中斷程序運行,輸入命令: ~0f 再輸入g命令讓記事本繼續運行。 此時嘗試用鼠標定位到notepad軟件,發現軟件界面無法被定位、移動、最大小化,甚至“清空桌面”操作也無濟于事。這是因為0號線程為notepad的主線程,被凍住后整個軟件都失去響應。 更嚴重的是,“清空桌面”操作(Win + D)也會失效,應是Notepad拒絕響應的緣故。線程掛起 參數n和m分別代表increase和resume,前者增加一個線程掛起計數,后者減少一個線程掛起計數。如果兩次增加線程掛起計數(即達到2),則必須兩次resume才能讓線程恢復到運行狀態。把上面實驗中的~0f命令改變成~0n,也能達到相似的效果。
線程切換
查看指定線程的信息,用下面的命令:
- ~?線程號
線程號是由調試器軟件內部維護的線程ID值,是一個從0開始的整數,和線程ID不是一回事。
線程信息中包括有線程環境塊地址,可通過!teb命令查看環境塊信息:
- !teb? [teb地址]
如要在多線程間作切換,需使用~命令的s參數:
- ~? 線程號 s
由于線程號在外部是沒有太大意義的,所以另一個線程切換命令是以線程ID來標識一個線程的。這個命令比較奇怪,以雙波浪線打頭,格式如下:
- ~~[線程ID] ?s
注意這個命令中的[]并非可選符,而是命令的一部分。例如命令:~~[11a0] s,它將當前線程切換到線程ID為0x11a0的線程。線程ID是系統維護的系統唯一的ID值。
下圖是關于線程切換的:
0:014> ~0 Id: 4f0.4f4 Suspend: 1 Teb: 00000000`7efdb000 Unfrozen1 Id: 4f0.5e0 Suspend: 1 Teb: 00000000`7ef9d000 Unfrozen2 Id: 4f0.604 Suspend: 1 Teb: 00000000`7ef9a000 Unfrozen3 Id: 4f0.61c Suspend: 1 Teb: 00000000`7ef97000 Unfrozen4 Id: 4f0.600 Suspend: 1 Teb: 00000000`7ef94000 Unfrozen5 Id: 4f0.610 Suspend: 1 Teb: 00000000`7ef91000 Unfrozen6 Id: 4f0.608 Suspend: 1 Teb: 00000000`7ef8e000 Unfrozen7 Id: 4f0.60c Suspend: 1 Teb: 00000000`7ef8b000 Unfrozen8 Id: 4f0.614 Suspend: 1 Teb: 00000000`7ef88000 Unfrozen9 Id: 4f0.5fc Suspend: 1 Teb: 00000000`7ef85000 Unfrozen10 Id: 4f0.5f4 Suspend: 1 Teb: 00000000`7ef82000 Unfrozen11 Id: 4f0.5f8 Suspend: 1 Teb: 00000000`7ef7f000 Unfrozen12 Id: 4f0.f58 Suspend: 1 Teb: 00000000`7ef76000 Unfrozen13 Id: 4f0.28e8 Suspend: 1 Teb: 00000000`7efd5000 Unfrozen . 14 Id: 4f0.1b80 Suspend: 1 Teb: 00000000`7ef79000 Unfrozen # 15 Id: 4f0.1f80 Suspend: 1 Teb: 00000000`7efd8000 Unfrozen0:016> ~~[5fc]s wow64cpu!CpupSyscallStub+0x9: 00000000`74412e09 c3 ret0:009> ~0 Id: 4f0.4f4 Suspend: 1 Teb: 00000000`7efdb000 Unfrozen1 Id: 4f0.5e0 Suspend: 1 Teb: 00000000`7ef9d000 Unfrozen2 Id: 4f0.604 Suspend: 1 Teb: 00000000`7ef9a000 Unfrozen3 Id: 4f0.61c Suspend: 1 Teb: 00000000`7ef97000 Unfrozen4 Id: 4f0.600 Suspend: 1 Teb: 00000000`7ef94000 Unfrozen5 Id: 4f0.610 Suspend: 1 Teb: 00000000`7ef91000 Unfrozen6 Id: 4f0.608 Suspend: 1 Teb: 00000000`7ef8e000 Unfrozen7 Id: 4f0.60c Suspend: 1 Teb: 00000000`7ef8b000 Unfrozen8 Id: 4f0.614 Suspend: 1 Teb: 00000000`7ef88000 Unfrozen . 9 Id: 4f0.5fc Suspend: 1 Teb: 00000000`7ef85000 Unfrozen10 Id: 4f0.5f4 Suspend: 1 Teb: 00000000`7ef82000 Unfrozen11 Id: 4f0.5f8 Suspend: 1 Teb: 00000000`7ef7f000 Unfrozen12 Id: 4f0.f58 Suspend: 1 Teb: 00000000`7ef76000 Unfrozen13 Id: 4f0.28e8 Suspend: 1 Teb: 00000000`7efd5000 Unfrozen14 Id: 4f0.1b80 Suspend: 1 Teb: 00000000`7ef79000 Unfrozen15 Id: 4f0.17cc Suspend: 1 Teb: 00000000`7efd8000 Unfrozen # 16 Id: 4f0.1538 Suspend: 1 Teb: 00000000`7ef7c000 Unfrozen第一個命令“~”運行時,當前線程是14號線程,請注意3號線程前面有一小點;運行第二個命令,將當前線程切換為9號線程;為了檢驗結果,再次運行“~”命令,此時注意到小點移到9號線程前,表明9號線程為當前線程。?
線程遍歷
仍然是~命令。它除了能夠作為線程列表命令外,還可用來對線程進行遍歷,并執行指定命令。只需借助通配符“*”即可。如:
- ~*k
顯示所有線程棧信息(此命令意指:對所有線程執行k指令)。下圖中,當前進程共包含兩個線程,顯示了這兩個線程各自的棧信息:
0:009> ~*k0 Id: 4f0.4f4 Suspend: 1 Teb: 00000000`7efdb000 Unfrozen Child-SP RetAddr Call Site 0019e648 74412bf1 wow64cpu!CpupSyscallStub+0x9 0019e650 7448d07e wow64cpu!Thunk0ArgReloadState+0x23 0019e710 7448c549 wow64!RunCpuSimulation+0xa 0019e760 77084956 wow64!Wow64LdrpInitialize+0x429 0019ecb0 77081a17 ntdll!LdrpInitializeProcess+0x17e4 0019f1a0 7706c32e ntdll! ?? ::FNODOBFM::`string'+0x29220 0019f210 00000000 ntdll!LdrInitializeThunk+0xe1 Id: 4f0.5e0 Suspend: 1 Teb: 00000000`7ef9d000 Unfrozen Child-SP RetAddr Call Site 00abebc8 74412bf1 wow64cpu!CpupSyscallStub+0x9 00abebd0 7448d07e wow64cpu!Thunk0ArgReloadState+0x23 00abec90 7448c549 wow64!RunCpuSimulation+0xa 00abece0 770be707 wow64!Wow64LdrpInitialize+0x429 00abf230 7706c32e ntdll! ?? ::FNODOBFM::`string'+0x29364 00abf2a0 00000000 ntdll!LdrInitializeThunk+0xe其他有用的遍歷指令包括:
- ~*r
顯示線程寄存器信息。
- ~*e
上面的e是execute(執行)的縮寫,后可跟一個或多個Windbg命令。它遍歷線程并對每個線程執行指定命令,如:
- ~*e? k;r
此命令意為:在所用線程環境中(~*),分別執行(e)棧指令(k)和寄存器指令(r)。
線程時間
在軟件調試的時候,若發現某線程占用執行時間過多,就需要當心是否有問題。線程執行時間的多少,其實就是占用CPU執行工作的時間多少。某線程占用越多,此長彼消,則系統中其它線程占用CPU的時間就越少。
線程的時間信息包括三個方面:自創建之初到現在的總消耗時間、用戶模式執行時間、內核模式執行時間。需注意的是,消耗時間一定會遠遠大于用戶時間+內核時間,多出來的是大量空閑時間(為Idle進程占用)。使用下面的命令查看線程時間:
- .ttime
- !runaway 7
在!runaway命令中加入標志值7,將顯示線程的全部三種時間值。
這兩個命令的區別之處是,.ttime只能顯示當前線程的時間信息,!runaway能顯示當前進程的所有線程時間。下圖是這兩個命令的使用情況:
0:009> .ttime Created: Wed Aug 7 16:47:29.011 2013 (UTC + 8:00) Kernel: 0 days 0:00:00.031 User: 0 days 0:00:00.046 0:009> !runaway 7User Mode TimeThread Time2:604 0 days 0:00:51.7294:600 0 days 0:00:47.1590:4f4 ? ? ? 0 days 0:00:00.0313:61c 0 days 0:00:00.0001:5e0 0 days 0:00:00.000Kernel Mode TimeThread Time4:600 0 days 0:13:45.0732:604 0 days 0:08:44.1003:61c 0 days 0:00:00.0001:5e0 0 days 0:00:00.0000:4f4 0 days 0:00:00.000Elapsed TimeThread Time0:4f4 16 days 0:14:02.2544:600 16 days 0:14:02.2073:61c 16 days 0:14:02.2072:604 16 days 0:14:02.2071:5e0 16 days 0:14:02.207??????????? 對上二圖進行對比,能看出兩個命令之間的功能差異。
3.3 異常與事件
在調試器語境中,事件是一個基本概念,Windbg是事件驅動的。Windows操作系統的調試子系統,是“事件”的發生源。調試器的所有操作,都是因事件而動,因事件被處理而中繼。Windows定義了9類調試事件,異常是其中一類(ID為1)。所以異常和事件,這二者是前者包含于后者的關系。
系統對各種異常和調試事件進行了分類,執行sx命令可以列出針對當前調試目標的異常或非異常事件的處理。下面是一個片段:
0:009> sxct - Create thread - ignoreet - Exit thread - ignorecpr - Create process - ignoreepr - Exit process - breakld - Load module - break(only break for livekd.exe)ud - Unload module - ignoreser - System error - ignoreibp - Initial breakpoint - breakiml - Initial module load - ignoreout - Debuggee output - outputav - Access violation - break - not handled asrt - Assertion failure - break - not handledaph - Application hang - break - not handledbpe - Break instruction exception - break bpec - Break instruction exception continue - handledeh - C++ EH exception - second-chance break - not handledclr - CLR exception - second-chance break - not handled clrn - CLR notification exception - second-chance break - handledcce - Control-Break exception - breakcc - Control-Break exception continue - handledcce - Control-C exception - breakcc - Control-C exception continue - handleddm - Data misaligned - break - not handled dbce - Debugger command exception - ignore - handledgp - Guard page violation - break - not handledii - Illegal instruction - second-chance break - not handledip - In-page I/O error - break - not handleddz - Integer divide-by-zero - break - not handlediov - Integer overflow - break - not handledch - Invalid handle - breakhc - Invalid handle continue - not handledlsq - Invalid lock sequence - break - not handledisc - Invalid system call - break - not handled3c - Port disconnected - second-chance break - not handledsvh - Service hang - break - not handledsse - Single step exception - break ssec - Single step exception continue - handledsbo - Security check failure or stack buffer overrun - break - not handledsov - Stack overflow - break - not handledvs - Verifier stop - break - not handled vcpp - Visual C++ exception - ignore - handledwkd - Wake debugger - break - not handledrto - Windows Runtime Originate Error - second-chance break - not handledrtt - Windows Runtime Transform Error - second-chance break - not handledwob - WOW64 breakpoint - break - handledwos - WOW64 single step exception - break - handled* - Other exception - second-chance break - not handled可以看到這幾個調試事件,當發生進程退出(Exit Process)和初始化斷點(Initial breakpoint)事件的時候,調試器應當被中斷(Break)。模塊加載(Load Modual)以及有調試輸出(Debuggen Output)時,需要輸出相關信息;其他的都被忽略掉,不做處理(Ignore)。???? 我們分析一下前兩個事件。使用調試器調試記事本進程時,不管是用.attach掛載方式還是.create創建方式,在調試器正式侵入記事本進程前,都會有一個中斷(Initial breakpoint異常);調試開始后運行一段時間,在外面將記事本關閉,又會發生一個中斷(Exit Process異常)。
可以通過Debug|Event Filters…打開事件設置對話框。這個對話框中列出了全部調試事件,用戶可分別對它們進行設置。
??????????? 這個對話框列出了對于當前調試會話可用的全部調試事件。針對每個調試事件,可設置其屬性。右列Execution和Continue兩組單選鍵,分別表示事件的中斷屬性與中繼屬性。右列Argument按鈕可設置調試事件執行參數(上圖中Load Module事件有一個Kernel32.dll參數,即當Kernel32.dll模塊被加載時,調試器將被中斷),Commands按鈕可設置事件兩輪機會發生時的執行命令。
更細致的內容,本章無力鋪陳,請讀者參閱《Windows 高級調試》(Mario & Daniel 2009 機械工業出版社)第三章,及《軟件調試》(張銀奎 2008 電子工業出版社)第9、30章相關內容。?
sxr:
??????此命令將當前所有對調試事件的設置,恢復到調試器的默認設置。最后一個字母r表示Reset。
sx{e|d|n|i}:
????? 這4個命令分別代表了圖8-38中Execution組(中斷屬性)中的四個按鈕,即Enable、Disable、Output、Ignore。Enable是開啟中斷,Disable是禁止事件中斷(但對于異常,只禁止第一輪機會,第二輪機會到來時仍會中斷到調試器),Output是禁止中斷但會輸出相關信息,Ignore表示完全忽略這個事件(對于異常,Output和Ignore兩選項使得兩輪機會都不會中斷到調試器)。
sx{e|d|n|i} -h:
????? 上述命令如果帶上-h選項,就不是設置中斷屬性,而是設置中繼屬性了。對應了圖8-38中的Continue組。其中sxe –h表示Handled,se{d|n|i} –h都表示Not Handled。
????? 下面繼續介紹異常、事件相關的其他調試命令:
.lastevent:
顯示最近發生的一個調試事件,往往是導致中斷發生的那個。下圖顯示的是一個很典型的初始化斷點引發的中斷事件。
.exr:
????? 此命令顯示一個異常記錄的詳細內容,傳入一個異常記錄地址:
- .exr?記錄地址
如果僅僅為了顯示最近的一條異常記錄,可以用-1代替異常記錄地址:
- .exr -1
由于異常是事件的一種,所以使用.exr -1命令得到的異常,可能和使用.lastevent命令獲取的事件(其實是異常),是同一個。但二者顯示的信息各有側重點。請對照圖8-39看下面,同樣的初始化斷點異常,使用.exr命令時所顯示的信息:
0:009> .lastevent Last event: 4f0.1538: Break instruction exception - code 80000003 (first chance)debugger time: Fri Aug 23 16:58:02.995 2013 (UTC + 8:00)還有一個類似的命令:!cppexr,他分析并顯示一個C++異常信息。
.bugcheck:
此命令不帶參數。在內核環境下,顯示當前bug check的詳細信息;可用于活動調試或者crash dump調試環境中。用戶環境不可用。見下圖:
0:009> .exr -1 ExceptionAddress: 0000000077090530 (ntdll!DbgBreakPoint)ExceptionCode: 80000003 (Break instruction exception)ExceptionFlags: 00000000 NumberParameters: 1Parameter[0]: 0000000000000000?!analyze:??
此命令分析當前最近的異常事件(如果在進行dump分析,則是bug check),并顯示分析結果。這個異常事件,就是上面.lastevent命令對應的事件。
- -v:顯示異常的詳細信息,這個選項在調試錯誤的時候,最有用。
- -f:f是force的縮寫。強制將任何事件都當作異常來分析,即使僅僅是普通的斷點事件。將因此多輸出一些內容。
- -hang:這個選項很有用,對于遇到死鎖的情況,它會分析原因。在內核環境中,它分析內核鎖和DPC棧;在用戶環境中,它分析線程的調用棧。用戶環境中,調試器只會對當前線程進行分析,所以一定要將線程環境切換到最可能引起問題的那個線程中去,才有幫助。這個參數非常有用,當真的遇到死鎖時,它可以救命(另一個分析死鎖的有效命令是!locks)。
- ?-show?bug-check-代碼 [參數]:在內核環境下,顯示指定的bug check的詳細信息。
!error:
此命令和VC里面內置的errlook工具類似(請有興趣的讀者使用作者編寫的免費軟件e-look,它比errlook功能更好且易于使用)。用來根據錯誤碼,查看對應的可讀錯誤信息。微軟系統中常用的全局錯誤碼有兩套,一套是Win32錯誤碼,通過函數GetLastError()獲得的值;另一套是NTSTATUS值。!error命令對這二者都能支持。區別的方法,若錯誤碼后面無參數1,則為win32錯誤碼;否則就是NTSTATUS錯誤碼。
??????????? 比如,獲取錯誤碼為2的Win32錯誤信息,可用:!error 2
??????????? 獲取錯誤碼為2的NTSTATUS錯誤信息,可用:!error 2 1?
!gle:
此命令是Get Last Error的縮寫。它調用Win32接口函數GetLastError()取得線程的錯誤值,并打印分析結果。如果帶有-all選項,則針對當前進程的所有線程(內核環境下為所有用戶線程)執行GetLastError()操作;否則僅針對當前線程。
gh/gn:這兩個命令是g命令的擴展。
? ? ? ? ? ??gh是go with Exception handled的縮寫,意思是:把異常標識為已處理而并繼續執行程序;注意這里面的措辭,僅僅把異常“標識為”已處理,而并非真的被處理了。gh的作用在于,當遇到某個可以忽略的非致命異常時,將它先放過一邊,而繼續執行程序。
? ? ? ? ? ??而gn是go with Exception not handled的縮寫,意思是,對異常不進行任何處理,而繼續執行程序。這時候,程序自己的異常處理模塊將有機會處理異常。
3.4 局部變量
有兩個命令可以打印當前的局部變量列表:x 和dv。x命令前文已經講過。dv是Display local Variable的縮寫。下面是對一段簡單的Win32控制臺代碼獲取其局部變量的情況:
??????????? 上圖無法體現dv比x命令在顯示局部變量上的高明之處。dv命令有幾個開關選項,介紹如下:
- ? ? ? ? ? ? /v:顯示虛擬地址(virtual);
- ? ? ? ? ? ? /i:顯示變量詳細信息(information),包括局部變量、全局變量、形參、函數變量等。
- ? ? ? ? ? ? /t:顯示變量類型(type),如int、char等等。
- ? ? ? ? ? ? /f:可指定進行分析的函數,需指定函數名。
? ? ? ? ?命令中選項/f wmain是指針對wmain函數(即_tmain)分析其局部變量。看第一個變量argc,“prv param”對應/i開關選項;“@ebp+0×08”對應/v開關選項;“int”對應/t開關選項。
3.5 顯示類型
??????????? 利用dt命令可以查看結構體的類型定義。命令dt是Display Type的縮寫。當我們要查看一些內核結構體的定義時,dt命令是最直接有效的手段。
4. 斷點
4.1 軟件斷點
軟件斷點的本質是代碼改寫,即:將INT 3(代碼為0xCC)指令替換到斷點所在指令處(第一個字節),并保存被替換掉的代碼(即一個字節內容)。等執行到斷點處時,調試器將因斷點而中斷,并將被替換的一字節內容恢復到原內存中。其原理和代碼補丁是一樣的。
源碼或匯編模式下,最簡單的斷點設置方式,是定位到正確的代碼處,并按下F9鍵。此外還有三種設置軟件斷點的指令,分別講解如下:
bp:
命令bp是BreakPoint的縮寫。其指令格式如下:
bp[ID] [Options] [Address [Passes]] ["CommandString"]
參數Address表示需設置斷點的地址,缺省情況下使用當前指令指針(EIP)的地址。ID是斷點號,一般不手動設置,由調試器內部編號。Passes是一個整數值,即第幾次經過斷點所在代碼時,調試器才需要中斷執行,默認為1,即每次都中斷。CommandString用來設置一組命令,當斷點發生的時候,就執行這一組命令,比如可以把它設置為“k”,這樣斷點處就會輸出當前的調用棧。
Options是一組可選開關項,有下面幾種:
/1:即阿拉伯數字1。這個選項表明這個被設置的斷點只會在第一次有效,此后斷點信息即被刪除。
/p:這個開關項后跟一個EPCOESS結構體指針,只能用在內核調試環境下。內核調試環境下,如果要把斷點設置到用戶程序地址(即用戶空間地址),需要使用這個開關,因為用戶地址是進程相關的。
/t:這個開關項后跟一個ETHREAD結構體指針,只能用在內核調試環境下。此開關項與/p起到類似的作用,只不過前者定位到進程,后者更進一步定位到線程。
/c與/C:c或者C代表CallStack(調用棧)。這兩個開關項和調用棧深度有關,都后跟一個整數值。前者表示調用棧深度如果小于這個整數值,斷點才會引發中斷,后者表示調用棧深度如果大于這個整數值,斷點才會引發中斷。
bu:
此命令格式與bp類似,u代表了Unresolved。使用此命令設置的斷點雖登記到調試器,但它具體對應到哪處代碼或指令,尚未確定。
比如某EXE程序使用動態加載的方式加載DLL(使用函數LoadLibrary()),那么當DLL尚未加載時,就可用bu指令設置DLL中的代碼斷點,等到DLL加載時,調試器再正式落實此斷點。
bm:
此命令用來批量設置代碼斷點,它帶有一個通配符字符串,凡是符合通配符格式的地址都將被設置斷點,如:
- bm /a ntdll!NtCreate*File
則諸如NtCreateFile\NtCreateMailslotFile\NtCreateNamedPipeFile等函數都將被設置斷點。
4.2 硬件斷點
硬件斷點的原理和軟件斷點完全不同,硬件斷點是通過CPU的斷點寄存器來實現的,亦即依靠硬件方式實現。由于CPU的調試寄存器數量是有限的,所以能設置的硬件斷點數量也是有限的。設置硬件斷點的命令是ba,a代表了Address。指令格式如下:
ba[ID] Access Size [Options] [Address [Passes]] ["CommandString"]
參數ID、Options、Passes及CommandString,含義與前文bp指令相同,此處不述。
參數Address是內存地址,有別于前文的指令地址,內存地址既可以是指令地址,也可以是數據地址。缺省為當前指令寄存器地址(EIP)。參數Size表示地址長度,x86系統可選值為1、2、4,X64系統可選值為1、2、4、8。需要注意的是,Address地址必須對齊到Size,即Address值必須是Size的整數倍。參數Access是內存訪問類型,有下面幾種:
e:作為指令執行;r:讀,或者寫;w:寫;i:執行IN/OUT操作。???? 比如:
- ba r4 @ebp-0×08
地址@ebp-8一定是一個局部變量地址,所以當CPU對這個局部變量執行讀寫操作時,將引發硬件中斷。
4.3 其他操作
??????????? 其他的斷點操作包括:顯示斷點列表、禁止或恢復斷點、刪除斷點等。
- bl:列出所有斷點
- bd:禁止斷點,d代表Disable。如bd 1,禁止斷點1。斷點被禁止后將不起作用,但亦未刪除。
- be:恢復斷點,e代表Enable。恢復被禁止的斷點。如be 1恢復1號斷點。
- bc:清除斷點,如:bc 1,清除斷點1;bc *,清除全部斷點。
- br:序號管理,r代表ReNumber,即重新排序。如:br 2 0,將2號斷點重設為0號斷點。
總結
以上是生活随笔為你收集整理的Windbg调试命令详解(3)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android开源音乐播放器之播放器基本
- 下一篇: 写给自己的好用的VSCode