1. 程式人生 > >windbg除錯和斷點學習總結2

windbg除錯和斷點學習總結2

WinDbg 設定斷點



在windbg中,斷點設定的地址形式有好多種,可以是以下幾種:
1.虛擬地址:即給出直接地址,如 12345678
2.函式偏移量:如DriverEntry+5c.
3.原始碼+行數 :`[[Module!]Filename][:LineNumber]`
4.對C++可以對模組中的某個類的方法設定斷點:
 
設定斷點語法:
         
1:無條件設定斷點:   bp  Address
  例如:
    kd> bp 0040108c
    kd> bp main+5c
    kd> bp `source.c:31` 
    kd> bp MyClass::MyMethod 
    kd> bp MyClass__MyMethod 
    kd> bp @@( MyClass::MyMethod )
 
2:設定有條件斷點
  
kd> bp Address "j (Condition) 'OptionalCommands'; 'gc' "kd> bp Address ".if (Condition) 


{OptionalCommands} .else {gc}"
這兩種設斷點語法是等價的。
 
bp,bu,bm 的區別:
bu:Set Unresolved Breakpoint


bp 和bu的區別:
 1:bp是立即生效,且馬上被轉化為記憶體中的某個地址,如果調式模組被改變,bp指向的地址不會變,


而bu只是和symbol檔案相關聯,模組改變的時候,指向的symbol的offset或者plus是不變的
 2: bp指定的斷點在模組unload之後從bl列表中刪除,而bu的斷點是永遠存在的。
 3:在windbg的可視原始碼或者可視的反彙編程式碼中所設的斷點都是bu模組的斷點
bm:Set Symbol Breakpoint
 使用bm設定斷點支援正則表示式的模式匹配,所以可以使用他來設定多個斷點
 如果正則表示式被匹配的話,他的效果將和bu設定的是一樣的
 例如:
    0:000> bm dbgtest!*main*
 1: 00413530 @!"dbgtest!wmain"
 2: 00411810 @!"dbgtest!__tmainCRTStartup"
 3: 004117f0 @!"dbgtest!wmainCRTStartup"
 使用bp和bm /a的風險:
  當wndbg在設定軟體斷點在程式碼段的時候,windbg將程式指令替換為斷點指令,但是當斷點設定在資料


段的時候,將會將程式資料替換為斷點指令,從而導致資料被修改,因此在設定斷點在資料段的時候,


推薦使用 ba(ba (Break on Access).)指令
 
控制斷點的方法
使用下面一些方法來控制或顯示斷點:
bl (Breakpoint List)命令列出當前存在的斷點和他們的狀態。
bp (Set Breakpoint) 命令設定新斷點。
bu (Set Unresolved Breakpoint) 命令設定新斷點。使用bu設定的斷點和bp設定的斷點特點不同,詳細


資訊檢視後面的內容。
bm (Set Symbol Breakpoint) 在匹配指定格式的符號上設定斷點。
ba (Break on Access)命令設定資料斷點。這種斷點在指定記憶體被訪問時觸發。(可以在寫入、讀取、執


行或發生核心I/O時觸發,但不是所有處理器都支援所有的記憶體訪問斷點。更多資訊,檢視ba (Break on 


Access)。)
bc (Breakpoint Clear) 命令移除一個或多個斷點。
bd (Breakpoint Disable) 命令暫時禁用一個或多個斷點。
be (Breakpoint Enable) 命令重新啟用一個或多個斷點。
br (Breakpoint Renumber)命令修改一個已存在的斷點的ID。
(僅WinDbg) 反彙編視窗(Disassembly window) 和 原始碼視窗(Source windows) 會將設定了斷點的行高


亮。已啟用的斷點為紅色,禁用的斷點為黃色,如果當前程式計數器(EIP)位置是斷點位置則顯示為紫色



(僅WinDbg) Edit | Breakpoints 命令或ALT+F9快捷鍵開啟Breakpoints對話方塊。該對話方塊會列出所有斷


點,所以可以用它來禁用、啟用、刪除已存在的斷點或設定新斷點。
(僅WinDbg) 如果游標在反彙編視窗或原始碼視窗中,可以按下F9或點選工具欄上的Insert or remove 按


鈕 () 來在游標所在行上設定斷點。如果在當前視窗不是反彙編視窗或原始碼視窗時按下快捷鍵或點選上


述按鈕,則和使用Edit | Breakpoints具有相同效果。
每個斷點都有一個關聯的10進位制數字稱為斷點ID 。該數字在各種命令中用於指定斷點。
未定斷點:BU vs. BP
如果一個斷點是設定在某個還未載入的函式名上,則稱為延遲、虛擬或未定斷點。 (這些術語可交替使


用。) 未定斷點沒有被關聯到任何具體被載入的模組上。每當一個新的模組被載入時,會檢查該函式名


。如果這個函數出現,偵錯程式計算虛擬斷點的實際位置並啟用它。
使用bu設定的斷點自動被認為是未定斷點。如果斷點在一個已載入模組中,則會啟用並正常生效。但是


,如果模組之後被解除安裝並重新載入,這個斷點不會消失。而使用bp設定的斷點會立即繫結到某個地址。
bp和bu斷點有以下三個主要的不同點:
bp 斷點的位置總是被轉換成地址。如果某個模組改變了,並且bp設定的地址位置改變,斷點還是在原來


的位置。而bu斷點仍然和使用的符號值關聯(一般是符號加上偏移),它會一直跟蹤符號的地址,即使這


個地址已經改變。
如果bp的斷點地址在某個已載入模組中找到,並且該模組之後被解除安裝,則該斷點會從斷點列表中移除。


而bu斷點經過反覆的解除安裝和載入仍然存在。
用bp設定的斷點不會儲存到WinDbg 工作空間(workspaces)中,而使用bu設定的斷點會儲存。
當在WinDbg 反彙編視窗或原始碼視窗中使用滑鼠設定斷點時,偵錯程式建立的是bu斷點。
初始斷點
當偵錯程式啟動一個新的目標程式時,初始斷點在主映像和所有靜態載入的DLL被載入、DLL初始化例程被


呼叫之前自動觸發。
偵錯程式附加到一個已存在的使用者模式程式時,初始斷點立即觸發。
-g 命令列選項使得WinDbg或CDB跳過初始斷點。在這時可以自動執行命令。更多資訊,檢視控制異常和


事件。
如果想啟動新除錯目標並在實際的程式即將開始執行的時候中斷下來,就不要使用-g選項。應該讓初始


斷點被觸發。當偵錯程式啟用之後,在main或winmai函式上設定斷點並使用g (Go) 命令。之後所有初始化


過程都會執行並且程式在main函式即將執行時停止。
關於核心模式的自動斷點的更多資訊,檢視崩潰和重起目標機。
斷點中的地址
斷點支援幾種地址語法,包括虛擬地址、函式偏移和原始碼行號。例如,可以使用下面的方法之一來設定


斷點:
0:000> bp 0040108c
0:000> bp main+5c
0:000> bp `source.c:31`
關於這些語法的更多資訊,檢視數值表示式語法, 原始碼行語法,以及各個命令的主題。
斷點的數量
在核心模式下,最多可以使用32個斷點。在使用者模式下,可以使用任意數量的斷點。
資料斷點的數量由目標處理器架構決定。
方法的斷點
如果要在MyClass類的MyMethod方法上設定斷點,可以使用兩種不同語法:
用MASM表示式語法,可以用雙冒號或者雙下劃線來指定一個方法。
0:000> bp MyClass::MyMethod 
0:000> bp MyClass__MyMethod 
用C++表示式語法,必須用雙冒號指定方法。
0:000> bp @@( MyClass::MyMethod ) 
如果要使用更復雜一些的斷點命令,應該使用MASM表示式語法。表示式語法的更多資訊,查看錶達式求


值。
使用者空間和系統空間
每個使用者模式應用程式在虛擬記憶體0x00000000 到0x7FFFFFFF 的地址稱為使用者空間。
當WinDbg或CDB在小於0x80000000的地址上下斷時,斷點是在單個程序的指定的使用者空間的地址設定。用


戶模式除錯時,當前程序決定了虛擬地址的意義。更多資訊,檢視控制程序和執行緒。
在核心模式,可以使用bp、 bu、 ba 命令和Breakpoints 對話方塊在使用者空間設定斷點。必須首先使


用.process /i (或在一些核心空間的函式上的指定程序的斷點)來將目標切換成當前程序上下文,並使


用該程序上下文來指定擁有目標地址空間的使用者模式程序。
使用者模式的斷點總是和設定該斷點時程序上下文為啟用狀態的程序關聯起來。如果有使用者模式偵錯程式在


除錯該程序,而還有一個核心模式偵錯程式在除錯程序執行的機器,即使斷點由核心偵錯程式設定,它中斷


時也是進入使用者模式偵錯程式。這時可以從核心模式偵錯程式中斷系統,或使用.breakin (Break to the 


Kernel Debugger) 命令來將控制權交給核心偵錯程式。
注意  如果目標機執行在Microsoft Windows NT 4.0上,則不能使用核心偵錯程式在使用者空間中設定斷點



斷點偽暫存器(Pseudo-Registers)
如果在某個表示式中想引用某個斷點的地址,可以使用一個$bpNumber 語法的偽暫存器,Number是斷點


ID。關於該語法的更多資訊,檢視偽暫存器語法。
設定斷點時的風險
當使用記憶體地址或符號加偏移的方式設定斷點時,一定不能將斷點設定到一條指令的中間。
例如,有下面一段彙編程式碼。
770000f1 5e               pop     esi
770000f2 5b               pop     ebx
770000f3 c9               leave
770000f4 c21000           ret     0x10
770000f7 837ddc00         cmp     dword ptr [ebp-0x24],0x0
前三條指令只有1個位元組長。但是第四條指令有3位元組長。(包含在0x770000F4,0x770000F5, 和


0x770000F6三個地址的位元組)如果要在該指令上使用bp、bu或 ba設定斷點,則必須將地址指定為


0x770000F4 。
如果使用ba命令在0x770000F5 地址設定了斷點,處理器將在該位置設定斷點。但是 該斷點永遠不會被


觸發,因為處理器認為0x770000F4 才是這條指令的實際地址。
如果使用bp 或bu命令在 0x770000F5 設定斷點,偵錯程式在這個位置會寫入斷點。但是由於偵錯程式使用如


下方法設定斷點,它可能造成目標執行錯誤:
偵錯程式儲存0x770000F5 地址的內容,並用一條斷點指令寫入該地址。
如果用偵錯程式顯示這段記憶體的內容,並不會顯示被寫入的斷點指令,而是顯示那裡原來"應該是"的內容


。即偵錯程式顯示原來的記憶體,或者斷點設定之後該記憶體被修改的內容。
如果使用BC命令刪除斷點,偵錯程式將斷點位置的記憶體恢復成原始值。
當在0x770000F5設定斷點時,偵錯程式儲存它的值並寫入斷點指令。但是當程式執行時到達0x770000F4 時


,會將它視為一條多位元組指令的第一個位元組。處理器將0x770000F4、0x770000F5可能還有後面的一些字


節當作一條指令。這會產生各種非正常的行為。
因此,當使用bp、bu或ba 命令設定斷點時,要確定斷點在合適的地址上。如果使用WinDbg圖形介面來添


加斷點就不用在意這樣的情況,因為它會自動選擇正確的地址。
斷點命令
可以在斷點中包含一條命令用於在斷點觸發時自動執行。
也可以包含一條用於執行的命令字串。但是,其中任何恢復程式執行的命令(例如g和t)都會終止命令


列表的執行。
例如下面的命令在MyFunction+0x47中斷,寫入一個dump檔案並恢復執行。
0:000> bu MyFunction+0x47 ".dump c:\mydump.dmp; g" 
注意  如果正在從核心偵錯程式控制使用者模式偵錯程式,不要在命令字串中使用g (Go) 。串列埠的速度可能


跟不上該命令,並且不能再中斷到CDB中。關於這種情況的更多資訊,檢視從核心偵錯程式控制使用者模式調


試器。
@!""
@!"" 語法用於在MASM求值器中進行轉義,使得符號解析支援任意文字。必須以@!"開始並以引號(")結束


。如果不使用該語法,則在MASM表示式的符號名中不能使用空格、大於小於號(<, >)和其他特殊字元。


模板和過載是符號中需要這種引號的主要原因。也可以使用如下的@!"" 語法來設定bu 命令。
0:000> bu @!"ExecutableName!std::pair,std::allocator > >::operator="
這個例子中,ExecutableName 是一個可執行檔案的名字。
這種轉義語法在C++中比C中更加有用(例如過載的操作符),因為C函式名中不會存在空格(或特殊字元)。


但是,該語法在託管程式碼中也同樣重要,因為.NET Framwork中非常多的使用過載。
條件斷點
可以設定僅在特定條件下被觸發的斷點。關於這類斷點的更多資訊,檢視設定條件斷點。
 
斷點命令:
1.ba。break on acess. The ba command sets a data breakpoint.
2.bc.(Breakpoint Clear)
The bc command permanently removes previously set breakpoints from the system.
3.bd (Breakpoint Disable)
The bd command disables, but does not delete, previously set breakpoints.
Syntax
bd Breakpoints 
4.be (Breakpoint Enable)
The be command restores one or more breakpoints that were previously disabled.
Syntax
be Breakpoints 
5.bl (Breakpoint List)
The bl command lists information about existing breakpoints.
Syntax
bl [/L] [Breakpoints] 
========

在windbg中用bm加斷點遇到的故事  



想在沒有原始碼和pdb的情況下在可執行檔案中增加斷點,因為知道會有某個系統呼叫
而且這個系統呼叫就是我想加斷點的地方,所以想利用微軟提供的系統pdb來實現。


先下載微軟的pdb,都沒有問題。


attach上以後用bm加斷點


0:003> bm *SysAllocStringLen*
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\fix.tmp\XML 


Interface Tester\XML Interface Tester.exe - 
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\fix.tmp\XML 


Interface Tester\WizLogger.dll - 
*** ERROR: Module load completed but symbols could not be loaded for C:\Windows


\system32\odbcint.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows


\system32\PGPhk.dll - 
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows


\SYSTEM32\SYSFER.DLL - 
*** ERROR: Module load completed but symbols could not be loaded for C:\Windows\WinSxS


\x86_microsoft.vc80.mfcloc_1fc8b3b9a1e18e3b_8.0.50727.6195_none_03ce2c72205943d3\MFC80ENU.D


LL
  1: 73255efa @!"MFC80!SysAllocStringLen"
*** ERROR: Module load completed but symbols could not be loaded for C:\Windows


\system32\PGPmapih.dll
*** ERROR: Module load completed but symbols could not be loaded for C:\Windows


\system32\AMINIT32.DLL
0:003> bl
 1 e 73255efa     0001 (0001)  0:**** MFC80!FtpRemoveDirectoryA
但是奇怪的事情發生了
搜尋時找到的斷點似乎正確,紅色部分。
但是用bl再次檢視斷點的時候,老母雞變鴨子了。
函式名字根本不對,綠色的部分。


用lm檢視發現大量的pdb沒有載入,而這些pdb就包括我想加斷點的那個函式所在的pdb -- oleaut32
0:003> lm
start    end        module name
00400000 00431000   XML_Interface_Tester   (deferred)             
10000000 10012000   WizLogger   (deferred)             
50180000 5020c000   ODBC32     (deferred)             
504b0000 504e8000   odbcint    (deferred)             
65f50000 65f5d000   PGPhk      (deferred)             
6ef90000 6efff000   SYSFER     (deferred)             
724c0000 72544000   COMCTL32   (deferred)             
72ef0000 72efe000   MFC80ENU   (deferred)             
73190000 7329f000   MFC80      (private pdb symbols)  c:\symcache\MFC80.i386.pdb


\922F97184FA4489BBA5DF664B9B3CB07e\MFC80.i386.pdb
732a0000 7333b000   MSVCR80    (deferred)             
740a0000 740b3000   dwmapi     (deferred)             
74170000 741b0000   uxtheme    (deferred)             
741b0000 7434e000   comctl32_741b0000   (deferred)             
75030000 75038000   Secur32    (deferred)             
75060000 7507a000   SSPICLI    (deferred)             
750d0000 750dc000   CRYPTBASE   (deferred)             
75120000 7512e000   PGPmapih   (deferred)             
75190000 751a5000   AMINIT32   (deferred)             
75220000 7522c000   MSASN1     (deferred)             
752b0000 752fa000   KERNELBASE   (deferred)             
75390000 754ae000   CRYPT32    (deferred)             
754b0000 754cf000   IMM32      (deferred)             
754d0000 75505000   WS2_32     (deferred)             
75510000 76159000   SHELL32    (deferred)             
76190000 76230000   ADVAPI32   (deferred)             
763c0000 7646c000   msvcrt     (deferred)             
76660000 767bc000   ole32      (deferred)             
767c0000 76894000   kernel32   (deferred)             
76c10000 76cd9000   USER32     (deferred)             
76ce0000 76d81000   RPCRT4     (deferred)             
76d90000 76e2d000   USP10      (deferred)             
76ec0000 76f8c000   MSCTF      (deferred)             
76f90000 7701f000   OLEAUT32   (deferred)             
77020000 7715c000   ntdll      (pdb symbols)          c:\symcache\ntdll.pdb


\EC61266D53004BD6826A130DADFC5C732\ntdll.pdb
77160000 7716a000   LPK        (deferred)             
77170000 771be000   GDI32      (deferred)             
771c0000 771c6000   NSI        (deferred)             
771d0000 771e9000   sechost    (deferred)             
771f0000 77247000   SHLWAPI    (deferred)             


用reload命令強制載入所有的斷點 f=全部
0:003> .reload /f
Reloading current modules
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\fix.tmp\XML 


Interface Tester\XML Interface Tester.exe - 
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\fix.tmp\XML 


Interface Tester\WizLogger.dll - 
..*** ERROR: Module load completed but symbols could not be loaded for C:\Windows


\system32\odbcint.dll
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows


\system32\PGPhk.dll - 
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows


\SYSTEM32\SYSFER.DLL - 
..*** ERROR: Module load completed but symbols could not be loaded for C:\Windows\WinSxS


\x86_microsoft.vc80.mfcloc_1fc8b3b9a1e18e3b_8.0.50727.6195_none_03ce2c72205943d3\MFC80ENU.D


LL
.........*** ERROR: Module load completed but symbols could not be loaded for C:\Windows


\system32\PGPmapih.dll
.*** ERROR: Module load completed but symbols could not be loaded for C:\Windows


\system32\AMINIT32.DLL
.....................
0:003> lm
start    end        module name
00400000 00431000   XML_Interface_Tester   (export symbols)       C:\fix.tmp\XML Interface 


Tester\XML Interface Tester.exe
10000000 10012000   WizLogger   (export symbols)       C:\fix.tmp\XML Interface Tester


\WizLogger.dll
50180000 5020c000   ODBC32     (pdb symbols)          c:\symcache\ODBC32.pdb


\0B85774251EE4F9D9A5054C74CA31E9D2\ODBC32.pdb
504b0000 504e8000   odbcint    (no symbols)           
65f50000 65f5d000   PGPhk      (export symbols)       C:\Windows\system32\PGPhk.dll
6ef90000 6efff000   SYSFER     (export symbols)       C:\Windows\SYSTEM32\SYSFER.DLL
724c0000 72544000   COMCTL32   (pdb symbols)          c:\symcache\comctl32v582.pdb


\57AB323CAC1E4056AFD759B4858103362\comctl32v582.pdb
72ef0000 72efe000   MFC80ENU   (no symbols)           
73190000 7329f000   MFC80      (private pdb symbols)  c:\symcache\MFC80.i386.pdb


\922F97184FA4489BBA5DF664B9B3CB07e\MFC80.i386.pdb
732a0000 7333b000   MSVCR80    (private pdb symbols)  c:\symcache\msvcr80.i386.pdb


\54C9E2F351544D1CB39517DC4B299EA81\msvcr80.i386.pdb
740a0000 740b3000   dwmapi     (pdb symbols)          c:\symcache\dwmapi.pdb


\D8D91B3F339A4FDC960FC7121D146DF42\dwmapi.pdb
74170000 741b0000   uxtheme    (pdb symbols)          c:\symcache\UxTheme.pdb


\5BECAB35E7714835A6BF3DADD891BB3A2\UxTheme.pdb
741b0000 7434e000   comctl32_741b0000   (pdb symbols)          c:\symcache\comctl32.pdb


\720C79A887E44232A1DC9ADAD3FC92DA2\comctl32.pdb
75030000 75038000   Secur32    (pdb symbols)          c:\symcache\secur32.pdb


\3249C80E3DDE4A7AB930F649D75CBAE32\secur32.pdb
75060000 7507a000   SSPICLI    (pdb symbols)          c:\symcache\sspicli.pdb


\4523AF509FEC4ECC99E6EB36CB692B382\sspicli.pdb
750d0000 750dc000   CRYPTBASE   (pdb symbols)          c:\symcache\cryptbase.pdb


\E62FEAE559EE4CD995614215B01AC2102\cryptbase.pdb
75120000 7512e000   PGPmapih   (no symbols)           
75190000 751a5000   AMINIT32   (no symbols)           
75220000 7522c000   MSASN1     (pdb symbols)          c:\symcache\msasn1.pdb


\7CC4D3288D43465B9217D924C0B79BDB2\msasn1.pdb
752b0000 752fa000   KERNELBASE   (pdb symbols)          c:\symcache\kernelbase.pdb


\BD568BB3D59243EEB8D8E8D5FC7366182\kernelbase.pdb
75390000 754ae000   CRYPT32    (pdb symbols)          c:\symcache\crypt32.pdb


\977EBFA0F06D40889C37967F478D25B22\crypt32.pdb
754b0000 754cf000   IMM32      (pdb symbols)          c:\symcache\imm32.pdb


\105C90B10D924E02AE71D8ECCE77CDC62\imm32.pdb
754d0000 75505000   WS2_32     (pdb symbols)          c:\symcache\ws2_32.pdb


\05B47564705B4BB0BFD23EEDD39091922\ws2_32.pdb
75510000 76159000   SHELL32    (pdb symbols)          c:\symcache\shell32.pdb


\FEFB494EDBAF46739E3D8D995410E95A2\shell32.pdb
76190000 76230000   ADVAPI32   (pdb symbols)          c:\symcache\advapi32.pdb


\8215E3385BE64C70AD230B20F032B9402\advapi32.pdb
763c0000 7646c000   msvcrt     (pdb symbols)          c:\symcache\msvcrt.pdb


\F107690F432145FAA54510BF68F4DB732\msvcrt.pdb
76660000 767bc000   ole32      (pdb symbols)          c:\symcache\ole32.pdb


\71F977C67D9A486DACD23F0BE30AD8802\ole32.pdb
767c0000 76894000   kernel32   (pdb symbols)          c:\symcache\kernel32.pdb


\76F7267453AB403CB21544B4D990DD1D2\kernel32.pdb
76c10000 76cd9000   USER32     (pdb symbols)          c:\symcache\user32.pdb


\C1D1D6EB9354465389912A697CCB2D502\user32.pdb
76ce0000 76d81000   RPCRT4     (pdb symbols)          c:\symcache\rpcrt4.pdb


\20EE55884BAB426589CA1892F8B9003C2\rpcrt4.pdb
76d90000 76e2d000   USP10      (pdb symbols)          c:\symcache\usp10.pdb


\0F136332ED524622ACD511B27629058A1\usp10.pdb
76ec0000 76f8c000   MSCTF      (pdb symbols)          c:\symcache\msctf.pdb


\173DAEF86B2548DBA6134EB74C4D2F232\msctf.pdb
76f90000 7701f000   OLEAUT32   (pdb symbols)          c:\symcache\oleaut32.pdb


\9D6BC892D2F344E68615FFA93E2B18122\oleaut32.pdb
77020000 7715c000   ntdll      (pdb symbols)          c:\symcache\ntdll.pdb


\EC61266D53004BD6826A130DADFC5C732\ntdll.pdb
77160000 7716a000   LPK        (pdb symbols)          c:\symcache\lpk.pdb


\B99319FE4427418F9EB5432B9F6A13412\lpk.pdb
77170000 771be000   GDI32      (pdb symbols)          c:\symcache\gdi32.pdb


\AF16A95F2D39406DA1912EE039CBF0652\gdi32.pdb
771c0000 771c6000   NSI        (pdb symbols)          c:\symcache\nsi.pdb


\D15A81679FAE4A7392344B6FD26867942\nsi.pdb
771d0000 771e9000   sechost    (pdb symbols)          c:\symcache\sechost.pdb


\7AF14D02D41E4CD6942745FE0E6372B11\sechost.pdb
771f0000 77247000   SHLWAPI    (pdb symbols)          c:\symcache\shlwapi.pdb


\372BB4590B784DBDA605154B826C29EB2\shlwapi.pdb


再用lm檢視pdb已經全部載入了
再用bm加斷點,這次用了/a=全部斷點
0:003> bm  /a *SysAllocStringLen*
breakpoint 1 redefined
  1: 73255efa @!"MFC80!SysAllocStringLen"
  2: 7327c348 @!"MFC80!_imp__SysAllocStringLen"
  3: 73256ba1 @!"MFC80!_imp_load__SysAllocStringLen"
  4: 742fc180 @!"comctl32_741b0000!_imp__SysAllocStringLen"
  5: 741f206e @!"comctl32_741b0000!_imp_load__SysAllocStringLen"
  6: 758d9318 @!"SHELL32!_imp__SysAllocStringLen"
  7: 758bfe20 @!"SHELL32!_imp_load__SysAllocStringLen"
  8: 767a60a4 @!"ole32!_imp__SysAllocStringLen"
  9: 7678bffa @!"ole32!_imp_load__SysAllocStringLen"
 10: 76f24d5a @!"MSCTF!SysAllocStringLen"
 11: 76fb060a @!"OLEAUT32!ErrSysAllocStringLen"
 12: 76f945d2 @!"OLEAUT32!SysAllocStringLen"
 13: 77242214 @!"SHLWAPI!_imp__SysAllocStringLen"
 14: 7723ee50 @!"SHLWAPI!_imp_load__SysAllocStringLen"
0:003> bl
 1 e 73255efa     0001 (0001)  0:**** MFC80!FtpRemoveDirectoryA
 2 e 7327c348     0001 (0001)  0:**** MFC80!_imp__SysAllocStringLen
 3 e 73256ba1     0001 (0001)  0:**** MFC80!_imp_load_SysAllocStringLen
 4 e 742fc180     0001 (0001)  0:**** comctl32_741b0000!_imp__SysAllocStringLen
 5 e 741f206e     0001 (0001)  0:**** comctl32_741b0000!_imp_load__SysAllocStringLen
 6 e 758d9318     0001 (0001)  0:**** SHELL32!_imp__SysAllocStringLen
 7 e 758bfe20     0001 (0001)  0:**** SHELL32!_imp_load__SysAllocStringLen
 8 e 767a60a4     0001 (0001)  0:**** ole32!_imp__SysAllocStringLen
 9 e 7678bffa     0001 (0001)  0:**** ole32!_imp_load__SysAllocStringLen
10 e 76f24d5a     0001 (0001)  0:**** MSCTF!SysAllocStringLen
11 e 76fb060a     0001 (0001)  0:**** OLEAUT32!ErrSysAllocStringLen
12 e 76f945d2     0001 (0001)  0:**** OLEAUT32!SysAllocStringLen
13 e 77242214     0001 (0001)  0:**** SHLWAPI!_imp__SysAllocStringLen
14 e 7723ee50     0001 (0001)  0:**** SHLWAPI!_imp_load__SysAllocStringLen
注意到這次會有很多,也包括第一的那個,但是也終於有我想要的了


然後我又查了查,發現斷點是否加上和reload 那個操作無關只要bm的時候帶/a就能加上斷點了
而且斷點加上以後再查lm,那些pdb也都載入了
所以不需要自己載入pdb,這是可以自動完成的。
========

Windbg除錯命令詳解

http://www.yiiyee.cn/Blog/windbg/


1. 概述


使用者成功安裝微軟Windows除錯工具集後,能夠在安裝目錄下發現四個偵錯程式程式,分別是:cdb.exe、


ntsd.exe、kd.exe和Windbg.exe。其中cdb.exe和ntsd.exe只能除錯使用者程式,Kd.exe主要用於核心除錯


,有時候也用於使用者態除錯,上述三者的一個共同特點是,都只有控制檯介面,以命令列形式工作。


Windbg.exe在使用者態、核心態下都能夠發揮除錯功能,尤其重要的是,它不再是命令列格式而是採用了


視覺化的使用者介面。所以絕大部分情況下,我們在談及Windows除錯工具的時候,都直接指向Windbg,而


不大談及前三者。


Windbg在使用者態和核心態下,都支援兩種除錯模式,即“實時除錯模式(Living)”和“事後除錯模式


(Postmortem)”。所謂實時模式,是被除錯的目標物件(Target)當前正在運行當中,偵錯程式可以實


時分析、修改被除錯目標的狀態,如暫存器、記憶體、變數,除錯exe可執行程式或雙擊雙機實時除錯都屬


於這種模式;所謂事後模式,是被除錯的目標物件(Target)已經結束了,現在只是事後對它保留的快


照進行分析,這個快照稱為轉儲檔案(Dump檔案)。


Windbg另一個重大優點,還在於它支援原始碼級的除錯,就像VC自帶的偵錯程式一樣。


雖然提供了使用者介面,但Windbg歸根結底還是需要使用者一個個地輸入命令來指揮其行動。這就是他的


Command視窗。


每個除錯命令都各有使用範圍,有些命令只能用於核心除錯,有些命令只能用於使用者除錯,有些命令只


能用於活動除錯。但使用者也不必記得這許多,一旦在某個環境下,使用了不被支援的命令,都會顯


示“No export XXX found”的字樣。就拿!process命令來說吧,它顯示程序資訊,但只能用於核心除錯


中,如果在使用者除錯中使用,就是下面的情景:


0:001> !process
No export process found
1.1 尋求幫助


我們首先來看如何在使用過程中獲取有用的幫助。Windbg中的除錯命令,分為三種:基本命令,元命令


和擴充套件命令。基本命令和元命令是偵錯程式自帶的,元命令總是以“.”開頭,而擴充套件命令是外部加入的,


總是以感嘆號“!”開頭。各種除錯命令成千上萬,我們首先要想辦法把它們都列舉出來,並取得使用方


法。


基本命令最少了,大概40個左右。列舉所有的基本命令,使用如下命令:


?
元命令有一百多個,使用下面命令列舉所有元命令:


.help  [/D]
如使用“/D”引數,命令列表將以DML格式顯示。DML是一種類似於HTML的標識語言,下面會講到。下圖


以DML格式顯示所以有字母a開頭的元命令:


dotcommand


最後講擴充套件命令。所謂擴充套件命令,顧名思義是可以“擴充套件”的。擴充套件命令從動態連線庫中暴露出來,一


般以DLL檔名來代表一類擴充套件命令集,首先我們要搜尋出系統中有多少個這樣的DLL檔案,使用下面命


令:


.chain  [/D]
此命令能夠給出一個擴充套件命令集的連結串列。和.help命令一樣,也可以使用/D引數以DML格式顯示。如下所


示:


0:001> .chain
Extension DLL search Path:
    C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64\WINXP;
Extension DLL chain:
    dbghelp: image 6.2.9200.20512, API 6.2.6, built Fri Sep 07 13:45:49 2012
        [path: C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64\dbghelp.dll]
    ext: image 6.2.9200.16384, API 1.0.0, built Thu Jul 26 10:11:33 2012
        [path: C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64\winext\ext.dll]
    exts: image 6.2.9200.16384, API 1.0.0, built Thu Jul 26 10:15:20 2012
        [path: C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64\WINXP\exts.dll]
    uext: image 6.2.9200.16384, API 1.0.0, built Thu Jul 26 10:15:09 2012
        [path: C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64\winext\uext.dll]
    ntsdexts: image 6.2.9200.16384, API 1.0.0, built Thu Jul 26 10:16:01 2012
        [path: C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64\WINXP\ntsdexts.dll]
            最上面兩行顯示了擴充套件模組的搜尋路徑。接下來共列出了六個Windbg自帶的擴充套件模組:


wdfkd、dbghellp、ext、exts、uext和ntsdexts。可以檢視到這些擴充套件模組的版本資訊、映象檔案路徑


。如何列出某個擴充套件庫中所包含的擴充套件命令列表呢?絕大部分擴充套件模組可使用如下命令:


!模組名.help
此外,擴充套件命令模組是可“擴充套件”的。如果讀者從第三方處獲取,或自己編寫了一個擴充套件除錯模組,則


可通過.load/.unload命令動態載入/解除安裝。


1.2 DML語言


DML(Debugger Markup Language偵錯程式標記語言)像HTML一樣,可從一處連結到另一處。不同處在於,


DML的連結內容需要使用者點選後才會動態生成。一般用來以精簡方式顯示大量資訊和擴充套件功能。


DML有很多實用的功能,如果使用者一時不知道從何下手,最好就是輸入.dml_start命令,開始DML之旅





dml


DML連結以更加視覺化的方式,引導使用者檢視除錯資訊,使得除錯工具的使用相比純指令格式而言,更為


友好。DML如同是對原指令的一層輕微的包裝一樣,讓生硬的指令更加溫和了。所以建議讀者總是把DML


預設開啟。


.prefer_dml  1
開始DML。


.prefer_dml  0
關閉DML。


一旦開啟DML後,像k等支援DML的除錯命令,將預設以DML格式顯示輸出內容。


DML還能以一種很特殊的方式為函式畫流程圖。它主要的原理是使用反彙編,類似於uf,但在邏輯分支處


,它會停止反彙編並顯示分支讓使用者選擇。另外,它能顯示彙編程式碼對應的行號,這一點真的非常好。


如果稍加精進,他就能畫出非常漂亮的流程圖了。他的一個特點是反彙編的順序是從後往前推。只要細


想一想,就會覺得很有道理。如果正推的話,分支太多;而反推則分支順序在使用者的參與下(即使用者進


行分支選擇),是固定了的。


.dml_flow  FindAllInfFilesA  FindAllInfFilesA+30
這是一個非常簡單、實用的例子,對Kernel32庫中的FindAllInfFilesA介面函式進行反彙編,效果類似


uf命令卻更強大。


1.3 基本資訊


本節講解和偵錯程式軟體本身相關的命令,比如:檢視軟體版本、啟動引數,以及最基本的軟體設定命令


。首先看版本命令:


version
此命令顯示作業系統的版本資訊以及Windbg本身的版本資訊,Windbg的配置和作業系統密切相關,所以


將作業系統的版本資訊一併顯示出來是很有必要的。在核心環境與使用者環境下執行此命令,會得到不同


的輸出。下圖為核心環境下輸出結果:


0:001> version
Windows 7 Version 7601 (Service Pack 1) MP (8 procs) Free x64
Product: WinNt, suite: SingleUserTS
kernel32.dll version: 6.1.7601.18015 (win7sp1_gdr.121129-1432)
Machine Name:
Debug session time: Thu Aug 22 10:11:04.000 2013 (UTC + 8:00)
System Uptime: 14 days 17:26:44.613
Process Uptime: 14 days 17:14:25.000
  Kernel time: 0 days 0:09:02.000
  User time: 0 days 0:42:36.000
Full memory user mini dump: C:\Users\mozhang\AppData\Local\Temp\dwm.DMP


Microsoft (R) Windows Debugger Version 6.2.9200.16384 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.
除了Windbg版本資訊,上面的輸出中還包括目標系統資訊。如果純粹是為了檢視目標系統的版本資訊,


可使用下面的vertarget命令:


vertarget
Windbg支援對多個除錯系統中的多個除錯目標同時進行除錯。上面我們通過version或vertarget命令列


出了當前除錯系統的版本資訊,還可以檢視當前目標系統的狀態:


||
如Windbg中同時開啟多個除錯物件,“||”命令將列出物件列表。筆者為了演示此種情況,先在Windbg


中開啟Local Debug環境,然後兩次呼叫.opendump命令開啟兩個DUMP檔案,這樣就同時擁有了三個被調


試的目標物件。下圖顯示了這個情況:


multi-target


            上圖中的活動物件是0號物件(可從數字0前面的小數點看出)。偵錯程式需要在多個除錯目


標之間進行切換的話,使用“s”引數。如要切換到1號目標可使用下面的命令:


||  1  s
最後一個命令用來檢視系統時間。這包括系統當前時間,以及系統正常執行持續時間;使用者模式下還會


顯示當前程序的持續時間。命令格式如下:


.time
0:001> .time
Debug session time: Thu Aug 22 10:11:04.000 2013 (UTC + 8:00)
System Uptime: 14 days 17:26:44.613 // 系統執行時間
Process Uptime: 14 days 17:14:25.000// 當前程序執行時間
  Kernel time: 0 days 0:09:02.000
  User time: 0 days 0:42:36.000
1.4 基本設定


首先看一個清屏命令:


.cls
當命令視窗中的內容太亂的時候,這個命令幫你快刀斬亂麻。


下面看一個設定預設數字進位制的命令:


n  [8|10|16]
軟體預設是16進位制,但有時候我們也需要把預設進位制改成八進位制或十進位制的。下面嘗試在八進位制下面求


數字11的值,如下:


0:001> n 8
base is 8
0:001> ? 11
Evaluate expression: 9 = 00000000`00000009
最後再來說一個處理器模式指令。關於處理器模式很值得一說,很重要。處理器模式的設定,反映了


Windbg軟體的強大。舉例來說,主機為32位的系統,卻可以同時除錯X86、IA64、X64的目標系統——前


提是先將主機的處理器模式設定正確了。可用處理器模式值有:x86、adm64、ia64、ebc。


.effmach  x86
命令.effmach表示Effective Machine Type,即有效的機器型別。此命令將當前的處理器模式設定為x86


模式。


1.5 格式化顯示


將一個整數以各種格式顯示,包括:16進位制、10進位制、8進位制、二進位制、字串、日期、浮點數等。是不


是很方便?這個命令是:


.formats  整數
下面以0x123abc為例:


0:001> .formats 0x123abc
Evaluate expression:
  Hex:     00000000`00123abc
  Decimal: 1194684
  Octal:   0000000000000004435274
  Binary:  00000000 00000000 00000000 00000000 00000000 00010010 00111010 10111100
  Chars:   ......:.
  Time:    Thu Jan 15 03:51:24 1970
  Float:   low 1.67411e-039 high 0
  Double:  5.90252e-318
1.6 開始除錯


現在領大家進入除錯階段。首先看看如何讓偵錯程式附載到一個已執行的程序中去?比如IE軟體在執行過


程中發生了崩潰,開啟Windbg後如何除錯呢?第一步就是把Windbg附載到發生崩潰的IE程序上。使用如


下命令格式:


.attach  PID
或者通過Windbg的啟動引數進行掛載:


Windbg –p PID
上面兩個命令中,PID指定了程序ID。如果覺得指定PID不方便,也可以通過程序名進行掛載:


Windbg -pn 程序名
比如掛載到記事本就可以這樣:


windbg –pn notepad.exe


上面的命令是把偵錯程式掛載到已經存在的程序上,另外偵錯程式可以建立新程序並對它進行除錯,這二者


使用了不同的侵入方法。使用下面的命令:


.create 程式啟動命令列
或者Windbg啟動引數


Windbg 程式啟動命令列
比如建立並除錯一個記事本子程序,可用.create notepad或者windbg notepad命令。也可以開啟Windbg


後,在File選單中選擇“Open Executable…”啟動Notepad子程序,但這個選項只能被執行一次(之後


會灰掉)。


使用上述命令可將偵錯程式連續附載到多個程序,也就是說,能夠同時除錯多個程序,這一點看上去很神


奇哦。下例中,偵錯程式先建立子程式IOCTL.exe,然後又呼叫.attach命令附加到記事本程序,使用命令


“|”列出所有被除錯程序。


讀者可能會奇怪,多個程序同時除錯怎麼兼顧呢?只要有一個切換指令就可以了,這樣就能夠切換到任


意的程序(令其為當前程序)並對之進行除錯。比如上圖顯示1號程序為當前程序(注意1前面的小點)


,如何將當前程序切換到0號程序呢?可以使用程序列表命令“|”輕鬆切換,比如:


|  0  s
此命令把當前除錯環境切換到0號IOCTL.exe程序。另外需注意的是,多個使用者程序除錯目標都處於同一


個除錯會話中,使用“||”命令會看到,它們屬於同一個 “Live user mode”除錯會話。


下面看dump檔案除錯,使用命令:


.opendump  檔名
此命令開啟一個dump檔案,並建立一個DUMP除錯會話。如何手動建立一個dump檔案呢?比如在除錯過程


中,遇到了無法解決的問題,希望獲得異地幫助,則把當前除錯環境儲存到Dump檔案中傳送給能提供幫


助的人,不失為一種好辦法。


.dump  檔名
Dump檔案一般以.dmp為字尾,系統生成的Dump檔案都預設以.dmp為字尾的,但使用.dump命令時,使用者


可以設定任意字尾,甚至無後綴。下例中,首先為當前程序生成一個dump檔案儲存到a.txt中(即字尾名


為.txt),然後將之開啟並分析:


0:001> .dump a.txt
Creating a.txt - mini user dump
Dump successfully written
0:001> .opendump a.txt
Loading Dump File [C:\Program Files (x86)\Windows Kits\8.0\Debuggers\a.txt]
User Mini Dump File: Only registers, stack and portions of memory are available


Opened 'a.txt' // 開啟成功
上文講到程序掛載命令,當需要解除掛載時,可使用解掛命令,如下:


.detach
此命令結束當前除錯會話, Windbg解除和被除錯程序之間的除錯關係(不管是通過掛載,還是通過建立


方式建立的除錯關係),解掛後,被除錯程序能夠獨立執行;如果當前的除錯會話是一個Dump檔案,此


命令直接結束對dump檔案的除錯,即結束除錯會話。


如果需要徹底結束除錯,下面的命令更有用:


q | qq | qd
q是Quit的縮寫。結束當前除錯會話,並返回到最簡單的工作空間,甚至把命令列介面也關閉掉。q和qq


兩個命令將結束(close)被除錯的程序,qd不會關閉除錯程序,而是進行解掛操作。


雙機除錯的時候,如果你感覺除錯已經陷入僵局,比如目標機Hang住了動都動不了,此時通過主機讓目


標機強制宕機或重啟,不失為一個好主意。


.crash
.reboot
crash命令能引發一個系統藍屏,並生成dump檔案;而.reboot使系統重啟,不產生dump檔案。


2. 符號與原始碼


符號與原始碼是除錯過程中的重要因素,它們使得枯燥生硬的除錯內容更容易地除錯人員讀懂。在可能的


情況下,應該儘量地為模組載入符號和原始碼。大部分情況下原始碼難以得到,但符號卻總能以符號檔案的


形式易於得到。


什麼是符號檔案呢?編譯器和連結器在建立二進位制映象檔案(諸如exe、dll、sys)時,伴生的字尾名為


.dbg、.sym或.pdb的包含映象檔案編譯、連結過程中生成的符號資訊的檔案稱為符號檔案。具體來說,


符號資訊包括如下內容:


全域性變數(型別、名稱、地址);
區域性變數(型別、名稱、地址);
函式(名稱、原型、地址);
變數、結構體型別定義;
原始檔路徑以及每個符號對應於原始檔中的行號,這是進行原始碼級別除錯的基礎。


有這麼多的資訊包含在符號檔案中,使得符號檔案通常要比二進位制檔案(PE格式檔案)本身要大很多。


除錯過程中,符號之重要性不言而喻。只有正確設定了符號路徑,使得偵錯程式能夠將除錯目標、符號文


件以及原始碼檔案一一對應起來,才能夠最好地發揮偵錯程式的強大功用。


symbols


符號資訊隸屬於指定的模組,所以只有偵錯程式需要用到某個模組時,他的符號資訊才有被載入和分析的


必要。所以我們在講符號內容之前,先講和模組相關的命令。


2.1 模組列表


每個可執行程式都是由若干個模組構成,有些模組靜態載入,有些模組以動態方式進行載入。所以對於


有些模組,可能在A時刻執行時被載入,而在B時刻執行時,自始至終都未被載入。除錯過程中,偵錯程式


根據模組的載入情況載入符號。有幾個命令可以用來列舉模組列表,分別是:lm、!dlls、.reload /l、


!imgreloc。下面分別來看。


lm [選項] [a Address] [m Pattern | M Pattern]
lm是list loaded modules的縮寫,他還有一個DML版本:


lmD [選項] [a Address] [m Pattern | M Pattern]
使用/v選項能列出模組的詳細資訊,包括:模組名、模組地址、模組大小、映象名、時間戳、以及對應


的符號檔案資訊(包括型別、路徑、型別、編譯器、符號載入狀態)。


如使用引數a,後面跟地址(address),則只有指定地址所在的模組能夠被列出;


如使用引數m,後面跟一個表示模組名的字串萬用字元,如lm  m  *o*將顯示所有名稱中包含字母o的模


塊,下圖所示:


||0:0:001> lm m *o*
start             end                 module name
f3380000 f3512000   dwmcore    (private pdb symbols) 
f92d0000 f9327000   d3d10_1core   (deferred)             
fa890000 fa9f1000   WindowsCodecs   (deferred)             
faa50000 fac44000   comctl32   (deferred)             
fbf70000 fbf7c000   version    (deferred)             
fce20000 fce2f000   profapi    (deferred)             
fd970000 fdb73000   ole32      (deferred)             
fee60000 fee7f000   sechost    (deferred)
下面介紹另一個命令:


!dlls [選項] [LoaderEntryAddress]
            首先看他的可選引數:


            -i/-l/-m:排序方式,分別按照初始化順序、載入順序、記憶體起始地址順序排列。


-a:列出映象檔案PE結構的檔案頭、Section頭等詳細資訊,是分析PE結構的好幫手(更好的幫手是利用


自如PEView或Stud_PE等UI工具)。


            -c:指定函式所在的模組。這個選項非常實用,比如我想知道NtCreateFile函式是哪個模


塊暴露出來的介面,如下:


0:000> !dlls -c ntcreatefile
Dump dll containing 0x7c92d0ae:
0x00251f48: C:\WINDOWS\system32\ntdll.dll
      Base   0x7c920000  EntryPoint  0x7c932c48  Size        0x00096000
      Flags  0x00085004  LoadCount   0x0000ffff  TlsIndex    0x00000000
             LDRP_IMAGE_DLL
             LDRP_LOAD_IN_PROGRESS
             LDRP_ENTRY_PROCESSED
             LDRP_PROCESS_ATTACH_CALLED
除了lm和!dlls外,下文將講到的.reload命令在加入 /l選項後,也能列舉模組,其命令格式如下:


.reload /l
最後再來看一個!imgreloc命令,它也能夠列出模組列表並顯示各模組地址。但其主要作用尚不在此,它


用來判斷各個模組是否處於preferred地址範圍。所謂Preferred地址是這麼一回事:二進位制檔案在編譯


的時候,編譯器都會為其設定一個理想地址(Preferred Address),這樣二進位制檔案被載入時,系統會


儘可能將他對映到這個理想地址。當然,所謂“理想”往往是會受到“現實”的挑戰的,當存在地址競


爭的時候,需要適當調整二進位制檔案的載入地址,選擇另一個合適的地方載入之。!imgreloc命令就是用


來檢視這種情況的,命令如下:


!imgreloc [模組地址]
命令!imgReloc是Image Relocate的縮寫,字面已能夠反映其含義:映象檔案重定位資訊。下面是一個例


子。


 imgreloc


      上例中,大部分系統模組(上圖下部方框所示)其地址由於事先經過統籌分配,所以一般都能被


載入到preferred地址處。只有少數模組(如最上面的Normaliz模組)由於地址衝突而受到了調整。


2.2 模組資訊


      上一節我們瞭解瞭如何列舉模組列表,這一節我們研究針對單個模組,如何獲取詳細資訊。有多


個命令可以檢視指定模組的詳細模組資訊,這包括:lm、!dh、lmi等,下面來一一介紹。


首先看lm,這個命令上面我們已經介紹過,現在利用它來獲取指定模組資訊。其命令格式如下:


lm v a 模組地址
這裡使用了v選項,以顯示詳細(verbose)資訊;並使用a引數以指定模組地址。通過此命令顯示的資訊


,和我們在explorer資源管理器中通過滑鼠右鍵檢視一個檔案的屬性所看到的資訊差不多。請看下面的


清單:


0:000> lm v a 00400000
start    end        module name
00400000 00752000   UsbKitApp C (private pdb symbols)  C:\Trunk\CY001\UsbKitApp\Debug


\UsbKitApp.pdb
    Loaded symbol image file: UsbKitApp.exe
    Image path: UsbKitApp.exe
    Image name: UsbKitApp.exe
    Timestamp:        Tue Mar 16 22:07:02 2010 (4B9F9086)
    CheckSum:         00000000
    ImageSize:        00352000
    File version:     1.0.0.1
    Product version:  1.0.0.1
    File flags:       1 (Mask 3F) Debug
    File OS:          4 Unknown Win32
    File type:        1.0 App
    File date:        00000000.00000000
    Translations:     0804.03a8
    CompanyName:      TODO: <公司名>
    ProductName:      TODO: <產品名>
    InternalName:     UsbKitApp.exe
    OriginalFilename: UsbKitApp.exe
    ProductVersion:   1.0.0.1
    FileVersion:      1.0.0.1
    FileDescription:  TODO: <檔案說明>
    LegalCopyright:   TODO: (C) <公司名>。保留所有權利。
    下面看!lmi命令,此命令通過指定模組地址查詢模組並獲取其資訊,其命令格式如下


!lmi 模組地址
此命令側重獲取對偵錯程式有用的資訊,請看下面的列表:


0:000> !lmi 0x400000
Loaded Module Info: [0x400000]
         Module: UsbKitApp
   Base Address: 00400000
     Image Name: UsbKitApp.exe
   Machine Type: 332 (I386)
     Time Stamp: 4b9f9086 Tue Mar 16 22:07:02 2010
           Size: 352000
       CheckSum: 0
Characteristics: 103
Debug Data Dirs: Type  Size     VA  Pointer
CODEVIEW  - GUID: {5DB12DF1-71CA-43F7-AD85-0977FB3629A4}
               Age: 3, Pdb: C:\Trunk\CY001\UsbKitApp\Debug\UsbKitApp.pdb
     Image Type: FILE     - Image read successfully from debugger.
                 UsbKitApp.exe
    Symbol Type: PDB      - Symbols loaded successfully from image header.
                 C:\Trunk\CY001\UsbKitApp\Debug\UsbKitApp.pdb
       Compiler: Resource - front end [0.0 bld 0] - back end [9.0 bld 21022]
    Load Report: private symbols & lines, not source indexed
                 C:\Trunk\CY001\UsbKitApp\Debug\UsbKitApp.pdb
 如果還要檢視更詳細、豐富的模組資訊,可以使用!dh命令,命令格式如下:


!dh [標誌] 模組地址
dh是display header的縮寫,直譯就是“顯示檔案頭”的意思,它能夠顯示非常詳細的PE頭資訊。下圖


截取了輸出資訊中的開頭部分,其它詳細內容,需要讀者熟悉微軟的PE結構才能看懂:


!dh


模組相關的知識點講完了,下面講符號有關命令。和符號相關的知識點包括:符號路徑、符號伺服器、


符號快取、符號載入以及符號的使用等。


2.3 符號路徑


什麼是符號路徑呢?就是偵錯程式尋找符號檔案的方向,它可以是本地資料夾路徑、可訪問的UNC路徑、或


者是符號伺服器路徑。什麼是符號伺服器呢?如果除錯過程中,需要涉及到成千上萬個符號檔案,以及


同一個符號檔案存在不同平臺下的不同符號檔案版本的時候,那麼一一手動設定符號路徑肯定是不現實


的,於是引入符號伺服器的概念。符號伺服器有一套命名規則,使得除錯軟體能夠正確找到需要的符號


檔案。一般來說,符號伺服器比較大,都是共用的,放在遠端主機上。為了降低網路訪問的成本,又引


入了符號快取的概念,即將從伺服器上下載到的符號檔案,儲存在本地快取中,以後偵錯程式需要符號文


件的時候,先從快取中尋找,找不到的時候再到伺服器上下載。下面分幾部分一一來看。


設定符號路徑:


設定符號路徑的語法如下:


.sympath  [+]  [路徑]
如果不加入任何引數執行.sympath命令,將顯示當前的路徑設定:


.sympath
如要覆蓋原來的路徑設定,使用新路徑即可:


.sympath  <新路徑>
要在原有路徑的基礎上新增一個新路徑,可使用:


.sympath+  <新增路徑>
要注意的是,使用.sympath改變或新增符號路徑後,符號檔案並不會自動更新,應再執行.reload命令以


更新之。


這裡要談一談延遲載入的知識點。延遲載入使得模組的符號表,只在第一次真正使用的時候才被載入。


這加快了程式啟動,不用在一開始耗費大量時間載入全部的符號檔案。


使用.symopt +4和.symopt -4來開啟或關閉延遲載入設定。


在已經啟動了延遲載入的情況下,如想臨時改變策略,立刻將指定模組的符號載入到偵錯程式中,可以使


用ld或者.reload /f命令。


符號伺服器與符號快取:


設定符號伺服器的基本語法是:


SRV*[符號快取]*伺服器地址
語法由SRV引導,符號快取和伺服器地址的前面各有一個星號引導。符號快取一般也叫做下游符號庫。如


某公司有一臺專門的符號伺服器,地址為\\symsrv\\symbols,則他們公司的所有開發人員都應該在他們


的偵錯程式中使用類似下面的命令:


.sympath+ srv*c:\symbols*\\symsrv\symbols
此外,我們總是應該把微軟的公用符號庫加入到我們的符號路徑中:


.sympath+ srv*<快取地址>*http://msdl.microsoft.com/download/symbols
這是一臺微軟對外公開的伺服器,使用http地址訪問,不是所有人都能牢記這個網址,所以最好的辦法


就是使用.symfix命令,語法如下:


.symfix [+] [符號快取地址]
這個命令等價於上面的.sympath命令,而不用輸入長長的http地址。


0:000> .symfix c:\windows\symbols


0:000> .sympath
Symbol search path is: SRV*c:\windows\symbols*http://msdl.microsoft.com/download/symbols
符號選項:


命令格式如下:


顯示當前設定:.symopt
增加選項:.symopt+  Flags
刪除選項:.symopt-  Flags
第一個命令沒有任何引數,顯示當前設定。後面兩個,第二個命令含有“+”代表新增一個選項,第三個


命令含有“-”代表去除一個選項。


001> .symopt
Symbol options are 0x30337:
  0x00000001 - SYMOPT_CASE_INSENSITIVE
  0x00000002 - SYMOPT_UNDNAME
  0x00000004 - SYMOPT_DEFERRED_LOADS
  0x00000010 - SYMOPT_LOAD_LINES
  0x00000020 - SYMOPT_OMAP_FIND_NEAREST
  0x00000100 - SYMOPT_NO_UNQUALIFIED_LOADS
  0x00000200 - SYMOPT_FAIL_CRITICAL_ERRORS
  0x00010000 - SYMOPT_AUTO_PUBLICS
  0x00020000 - SYMOPT_NO_IMAGE_SEARCH
可用的符號選項請見下表:





可讀名稱


描述


0×1


SYMOPT_CASE_INSENSITIVE


符號名稱不區分大小寫


0×2


SYMOPT_UNDNAME


符號名稱未修飾


0×4


SYMOPT_DEFERRED_LOADS


延遲載入


0×8


SYMOPT_NO_CPP


關閉C++轉換,C++中的::符號將以__顯示


0×10


SYMOPT_LOAD_LINES


從原始檔中載入行號


0×20


SYMOPT_OMAP_FIND_


NEAREST


如果由於編譯器優化導致找不到對應的符號,就以最近的一個符號代替之


0×40


SYMOPT_LOAD_ANYTHING


使得符號匹配的時候,匹配原則較鬆散,不那麼嚴格。


0×80


SYMOPT_IGNORE_CVREC


忽略映象檔案頭中的CV記錄


0×100


SYMOPT_NO_UNQUALIFIED_


LOADS


只在已載入模組中搜索符號,如果搜尋符號失敗,不會自動載入新模組。


0×200


SYMOPT_FAIL_CRITICAL_


ERRORS


不顯示檔案訪問錯誤對話方塊。


0×400


SYMOPT_EXACT_SYMBOLS


進行最嚴格的符號檔案檢查,只要有微小的差異,符號檔案都不會被載入。


0×800


SYMOPT_ALLOW_ABSOLUTE_


SYMBOLS


允許從記憶體的一個絕對地址處讀取符號資訊。


0×1000


SYMOPT_IGNORE_NT_


SYMPATH


忽視在環境變數中設定的符號路徑,也忽視被除錯程序的執行路徑。也就是說,當搜尋符號檔案的時候


,不會從這些路徑中搜索。


0×2000


SYMOPT_INCLUDE_32BIT_MODULES


讓執行在安騰系統上的偵錯程式,也列舉32位模組。


0×4000


SYMOPT_PUBLICS_ONLY


僅搜尋符號檔案的公共(PUBLIC)符號表,忽略私有符號表。


0×8000


SYMOPT_NO_PUBLICS


不搜尋符號檔案的公共(PUBLIC)符號表


0×10000


SYMOPT_AUTO_PUBLICS


先搜尋pdb檔案的私有符號表,如果在其中找到對應的符號,就不再搜尋公共(PUBLIC)符號表,這可以


加快搜索速度。


0×20000


SYMOPT_NO_IMAGE_SEARCH


不搜尋映象拷貝


0×40000


SYMOPT_SECURE


安全模式,讓偵錯程式儘量不影響到主機。


0×80000


SYMOPT_NO_PROMPTS


不顯示符號代理伺服器的認證對話方塊,將導致某些時候無法訪問符號伺服器


0×80000000


SYMOPT_DEBUG


顯示符號搜尋的詳細過程和資訊


表8-1 符號選項


2.4 符號載入


本節分下面幾個子題目分別講解。


立刻載入:


其命令格式如下:


ld 模組名 [/f 符號檔名]
載入指定模組的符號。偵錯程式預設採用延遲模式載入符號,也就是直到符號被使用的時候,才將符號文


件載入到偵錯程式中並進行解析。ld使得延遲模式被打破,讓指定模組的符號檔案立刻載入到偵錯程式中。


此指令可為模組的符號檔案設定自定義的匹配名稱,比如