乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Windbg調(diào)試命令詳解 | 張佩的倉(cāng)庫(kù)

       win2zhang 2014-09-29

      轉(zhuǎn)載注明>> 作者:張佩】【原文http://www./Blog

      1. 概述

      用戶成功安裝微軟Windows調(diào)試工具集后,能夠在安裝目錄下發(fā)現(xiàn)四個(gè)調(diào)試器程序,分別是:cdb.exe、ntsd.exe、kd.exe和Windbg.exe。其中cdb.exe和ntsd.exe只能調(diào)試用戶程序,Kd.exe主要用于內(nèi)核調(diào)試,有時(shí)候也用于用戶態(tài)調(diào)試,上述三者的一個(gè)共同特點(diǎn)是,都只有控制臺(tái)界面,以命令行形式工作。

      Windbg.exe在用戶態(tài)、內(nèi)核態(tài)下都能夠發(fā)揮調(diào)試功能,尤其重要的是,它不再是命令行格式而是采用了可視化的用戶界面。所以絕大部分情況下,我們?cè)谡劶癢indows調(diào)試工具的時(shí)候,都直接指向Windbg,而不大談及前三者。

      Windbg在用戶態(tài)和內(nèi)核態(tài)下,都支持兩種調(diào)試模式,即“實(shí)時(shí)調(diào)試模式(Living)”和“事后調(diào)試模式(Postmortem)”。所謂實(shí)時(shí)模式,是被調(diào)試的目標(biāo)對(duì)象(Target)當(dāng)前正在運(yùn)行當(dāng)中,調(diào)試器可以實(shí)時(shí)分析、修改被調(diào)試目標(biāo)的狀態(tài),如寄存器、內(nèi)存、變量,調(diào)試exe可執(zhí)行程序或雙擊雙機(jī)實(shí)時(shí)調(diào)試都屬于這種模式;所謂事后模式,是被調(diào)試的目標(biāo)對(duì)象(Target)已經(jīng)結(jié)束了,現(xiàn)在只是事后對(duì)它保留的快照進(jìn)行分析,這個(gè)快照稱為轉(zhuǎn)儲(chǔ)文件(Dump文件)。

      Windbg另一個(gè)重大優(yōu)點(diǎn),還在于它支持源碼級(jí)的調(diào)試,就像VC自帶的調(diào)試器一樣。

      雖然提供了用戶界面,但Windbg歸根結(jié)底還是需要用戶一個(gè)個(gè)地輸入命令來指揮其行動(dòng)。這就是他的Command窗口。

      每個(gè)調(diào)試命令都各有使用范圍,有些命令只能用于內(nèi)核調(diào)試,有些命令只能用于用戶調(diào)試,有些命令只能用于活動(dòng)調(diào)試。但用戶也不必記得這許多,一旦在某個(gè)環(huán)境下,使用了不被支持的命令,都會(huì)顯示“No export XXX found”的字樣。就拿!process命令來說吧,它顯示進(jìn)程信息,但只能用于內(nèi)核調(diào)試中,如果在用戶調(diào)試中使用,就是下面的情景:

      0:001> !process
      No export process found

      1.1 尋求幫助

      我們首先來看如何在使用過程中獲取有用的幫助。Windbg中的調(diào)試命令,分為三種:基本命令,元命令和擴(kuò)展命令?;久詈驮钍钦{(diào)試器自帶的,元命令總是以“.”開頭,而擴(kuò)展命令是外部加入的,總是以感嘆號(hào)“!”開頭。各種調(diào)試命令成千上萬(wàn),我們首先要想辦法把它們都列舉出來,并取得使用方法。

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

      元命令有一百多個(gè),使用下面命令列舉所有元命令:

      • .help  [/D]

      如使用“/D”參數(shù),命令列表將以DML格式顯示。DML是一種類似于HTML的標(biāo)識(shí)語(yǔ)言,下面會(huì)講到。下圖以DML格式顯示所以有字母a開頭的元命令:

      dotcommand

      最后講擴(kuò)展命令。所謂擴(kuò)展命令,顧名思義是可以“擴(kuò)展”的。擴(kuò)展命令從動(dòng)態(tài)連接庫(kù)中暴露出來,一般以DLL文件名來代表一類擴(kuò)展命令集,首先我們要搜索出系統(tǒng)中有多少個(gè)這樣的DLL文件,使用下面命令:

      • .chain  [/D]

      此命令能夠給出一個(gè)擴(kuò)展命令集的鏈表。和.help命令一樣,也可以使用/D參數(shù)以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]

                  最上面兩行顯示了擴(kuò)展模塊的搜索路徑。接下來共列出了六個(gè)Windbg自帶的擴(kuò)展模塊:wdfkd、dbghellp、ext、exts、uext和ntsdexts。可以查看到這些擴(kuò)展模塊的版本信息、鏡像文件路徑。如何列出某個(gè)擴(kuò)展庫(kù)中所包含的擴(kuò)展命令列表呢?絕大部分?jǐn)U展模塊可使用如下命令:

      • !模塊名.help

      此外,擴(kuò)展命令模塊是可“擴(kuò)展”的。如果讀者從第三方處獲取,或自己編寫了一個(gè)擴(kuò)展調(diào)試模塊,則可通過.load/.unload命令動(dòng)態(tài)加載/卸載。

      1.2 DML語(yǔ)言

      DML(Debugger Markup Language調(diào)試器標(biāo)記語(yǔ)言)像HTML一樣,可從一處鏈接到另一處。不同處在于,DML的鏈接內(nèi)容需要用戶點(diǎn)擊后才會(huì)動(dòng)態(tài)生成。一般用來以精簡(jiǎn)方式顯示大量信息和擴(kuò)展功能。

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

      dml

      DML鏈接以更加可視化的方式,引導(dǎo)用戶查看調(diào)試信息,使得調(diào)試工具的使用相比純指令格式而言,更為友好。DML如同是對(duì)原指令的一層輕微的包裝一樣,讓生硬的指令更加溫和了。所以建議讀者總是把DML默認(rèn)開啟。

      • .prefer_dml  1

      開始DML。

      • .prefer_dml  0

      關(guān)閉DML。

      一旦開啟DML后,像k等支持DML的調(diào)試命令,將默認(rèn)以DML格式顯示輸出內(nèi)容。

      DML還能以一種很特殊的方式為函數(shù)畫流程圖。它主要的原理是使用反匯編,類似于uf,但在邏輯分支處,它會(huì)停止反匯編并顯示分支讓用戶選擇。另外,它能顯示匯編代碼對(duì)應(yīng)的行號(hào),這一點(diǎn)真的非常好。如果稍加精進(jìn),他就能畫出非常漂亮的流程圖了。他的一個(gè)特點(diǎn)是反匯編的順序是從后往前推。只要細(xì)想一想,就會(huì)覺得很有道理。如果正推的話,分支太多;而反推則分支順序在用戶的參與下(即用戶進(jìn)行分支選擇),是固定了的。

      • .dml_flow  FindAllInfFilesA  FindAllInfFilesA+30

      這是一個(gè)非常簡(jiǎn)單、實(shí)用的例子,對(duì)Kernel32庫(kù)中的FindAllInfFilesA接口函數(shù)進(jìn)行反匯編,效果類似uf命令卻更強(qiáng)大。

      1.3 基本信息

      本節(jié)講解和調(diào)試器軟件本身相關(guān)的命令,比如:查看軟件版本、啟動(dòng)參數(shù),以及最基本的軟件設(shè)置命令。首先看版本命令:

      • version

      此命令顯示操作系統(tǒng)的版本信息以及Windbg本身的版本信息,Windbg的配置和操作系統(tǒng)密切相關(guān),所以將操作系統(tǒng)的版本信息一并顯示出來是很有必要的。在內(nèi)核環(huán)境與用戶環(huán)境下運(yùn)行此命令,會(huì)得到不同的輸出。下圖為內(nèi)核環(huán)境下輸出結(jié)果:

      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版本信息,上面的輸出中還包括目標(biāo)系統(tǒng)信息。如果純粹是為了查看目標(biāo)系統(tǒng)的版本信息,可使用下面的vertarget命令:

      • vertarget

      Windbg支持對(duì)多個(gè)調(diào)試系統(tǒng)中的多個(gè)調(diào)試目標(biāo)同時(shí)進(jìn)行調(diào)試。上面我們通過version或vertarget命令列出了當(dāng)前調(diào)試系統(tǒng)的版本信息,還可以查看當(dāng)前目標(biāo)系統(tǒng)的狀態(tài):

      • ||

      如Windbg中同時(shí)打開多個(gè)調(diào)試對(duì)象,“||”命令將列出對(duì)象列表。筆者為了演示此種情況,先在Windbg中開啟Local Debug環(huán)境,然后兩次調(diào)用.opendump命令打開兩個(gè)DUMP文件,這樣就同時(shí)擁有了三個(gè)被調(diào)試的目標(biāo)對(duì)象。下圖顯示了這個(gè)情況:

      multi-target

                  上圖中的活動(dòng)對(duì)象是0號(hào)對(duì)象(可從數(shù)字0前面的小數(shù)點(diǎn)看出)。調(diào)試器需要在多個(gè)調(diào)試目標(biāo)之間進(jìn)行切換的話,使用“s”參數(shù)。如要切換到1號(hào)目標(biāo)可使用下面的命令:

      • ||  1  s

      最后一個(gè)命令用來查看系統(tǒng)時(shí)間。這包括系統(tǒng)當(dāng)前時(shí)間,以及系統(tǒng)正常運(yùn)行持續(xù)時(shí)間;用戶模式下還會(huì)顯示當(dāng)前進(jìn)程的持續(xù)時(shí)間。命令格式如下:

      • .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 // 系統(tǒng)運(yùn)行時(shí)間
      Process Uptime: 14 days 17:14:25.000// 當(dāng)前進(jìn)程運(yùn)行時(shí)間
        Kernel time: 0 days 0:09:02.000
        User time: 0 days 0:42:36.000

      1.4 基本設(shè)置

      首先看一個(gè)清屏命令:

      • .cls

      當(dāng)命令窗口中的內(nèi)容太亂的時(shí)候,這個(gè)命令幫你快刀斬亂麻。

      下面看一個(gè)設(shè)置默認(rèn)數(shù)字進(jìn)制的命令:

      • n  [8|10|16]

      軟件默認(rèn)是16進(jìn)制,但有時(shí)候我們也需要把默認(rèn)進(jìn)制改成八進(jìn)制或十進(jìn)制的。下面嘗試在八進(jìn)制下面求數(shù)字11的值,如下:

      0:001> n 8
      base is 8
      0:001> ? 11
      Evaluate expression: 9 = 00000000`00000009

      最后再來說一個(gè)處理器模式指令。關(guān)于處理器模式很值得一說,很重要。處理器模式的設(shè)置,反映了Windbg軟件的強(qiáng)大。舉例來說,主機(jī)為32位的系統(tǒng),卻可以同時(shí)調(diào)試X86、IA64、X64的目標(biāo)系統(tǒng)——前提是先將主機(jī)的處理器模式設(shè)置正確了??捎锰幚砥髂J街涤校簒86、adm64、ia64、ebc。

      • .effmach  x86

      命令.effmach表示Effective Machine Type,即有效的機(jī)器類型。此命令將當(dāng)前的處理器模式設(shè)置為x86模式。

      1.5 格式化顯示

      將一個(gè)整數(shù)以各種格式顯示,包括:16進(jìn)制、10進(jìn)制、8進(jìn)制、二進(jìn)制、字符串、日期、浮點(diǎn)數(shù)等。是不是很方便?這個(gè)命令是:

      • .formats  整數(shù)

      下面以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 開始調(diào)試

      現(xiàn)在領(lǐng)大家進(jìn)入調(diào)試階段。首先看看如何讓調(diào)試器附載到一個(gè)已運(yùn)行的進(jìn)程中去?比如IE軟件在運(yùn)行過程中發(fā)生了崩潰,打開Windbg后如何調(diào)試呢?第一步就是把Windbg附載到發(fā)生崩潰的IE進(jìn)程上。使用如下命令格式:

      • .attach  PID

      或者通過Windbg的啟動(dòng)參數(shù)進(jìn)行掛載:

      • Windbg –p PID

      上面兩個(gè)命令中,PID指定了進(jìn)程ID。如果覺得指定PID不方便,也可以通過進(jìn)程名進(jìn)行掛載:

      • Windbg -pn 進(jìn)程名

      比如掛載到記事本就可以這樣:

      windbg –pn notepad.exe

      上面的命令是把調(diào)試器掛載到已經(jīng)存在的進(jìn)程上,另外調(diào)試器可以創(chuàng)建新進(jìn)程并對(duì)它進(jìn)行調(diào)試,這二者使用了不同的侵入方法。使用下面的命令:

      • .create 程序啟動(dòng)命令行

      或者Windbg啟動(dòng)參數(shù)

      • Windbg 程序啟動(dòng)命令行

      比如創(chuàng)建并調(diào)試一個(gè)記事本子進(jìn)程,可用.create notepad或者windbg notepad命令。也可以打開Windbg后,在File菜單中選擇“Open Executable…”啟動(dòng)Notepad子進(jìn)程,但這個(gè)選項(xiàng)只能被執(zhí)行一次(之后會(huì)灰掉)。

      使用上述命令可將調(diào)試器連續(xù)附載到多個(gè)進(jìn)程,也就是說,能夠同時(shí)調(diào)試多個(gè)進(jìn)程,這一點(diǎn)看上去很神奇哦。下例中,調(diào)試器先創(chuàng)建子程序IOCTL.exe,然后又調(diào)用.attach命令附加到記事本進(jìn)程,使用命令“|”列出所有被調(diào)試進(jìn)程。

      讀者可能會(huì)奇怪,多個(gè)進(jìn)程同時(shí)調(diào)試怎么兼顧呢?只要有一個(gè)切換指令就可以了,這樣就能夠切換到任意的進(jìn)程(令其為當(dāng)前進(jìn)程)并對(duì)之進(jìn)行調(diào)試。比如上圖顯示1號(hào)進(jìn)程為當(dāng)前進(jìn)程(注意1前面的小點(diǎn)),如何將當(dāng)前進(jìn)程切換到0號(hào)進(jìn)程呢?可以使用進(jìn)程列表命令“|”輕松切換,比如:

      • |  0  s

      此命令把當(dāng)前調(diào)試環(huán)境切換到0號(hào)IOCTL.exe進(jìn)程。另外需注意的是,多個(gè)用戶進(jìn)程調(diào)試目標(biāo)都處于同一個(gè)調(diào)試會(huì)話中,使用“||”命令會(huì)看到,它們屬于同一個(gè) “Live user mode”調(diào)試會(huì)話。

      下面看dump文件調(diào)試,使用命令:

      • .opendump  文件名

      此命令打開一個(gè)dump文件,并建立一個(gè)DUMP調(diào)試會(huì)話。如何手動(dòng)創(chuàng)建一個(gè)dump文件呢?比如在調(diào)試過程中,遇到了無(wú)法解決的問題,希望獲得異地幫助,則把當(dāng)前調(diào)試環(huán)境保存到Dump文件中發(fā)送給能提供幫助的人,不失為一種好辦法。

      • .dump  文件名

      Dump文件一般以.dmp為后綴,系統(tǒng)生成的Dump文件都默認(rèn)以.dmp為后綴的,但使用.dump命令時(shí),使用者可以設(shè)置任意后綴,甚至無(wú)后綴。下例中,首先為當(dāng)前進(jìn)程生成一個(gè)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' // 打開成功

      上文講到進(jìn)程掛載命令,當(dāng)需要解除掛載時(shí),可使用解掛命令,如下:

      • .detach

      此命令結(jié)束當(dāng)前調(diào)試會(huì)話, Windbg解除和被調(diào)試進(jìn)程之間的調(diào)試關(guān)系(不管是通過掛載,還是通過創(chuàng)建方式建立的調(diào)試關(guān)系),解掛后,被調(diào)試進(jìn)程能夠獨(dú)立運(yùn)行;如果當(dāng)前的調(diào)試會(huì)話是一個(gè)Dump文件,此命令直接結(jié)束對(duì)dump文件的調(diào)試,即結(jié)束調(diào)試會(huì)話。

      如果需要徹底結(jié)束調(diào)試,下面的命令更有用:

      • q | qq | qd

      q是Quit的縮寫。結(jié)束當(dāng)前調(diào)試會(huì)話,并返回到最簡(jiǎn)單的工作空間,甚至把命令行界面也關(guān)閉掉。q和qq兩個(gè)命令將結(jié)束(close)被調(diào)試的進(jìn)程,qd不會(huì)關(guān)閉調(diào)試進(jìn)程,而是進(jìn)行解掛操作。

      雙機(jī)調(diào)試的時(shí)候,如果你感覺調(diào)試已經(jīng)陷入僵局,比如目標(biāo)機(jī)Hang住了動(dòng)都動(dòng)不了,此時(shí)通過主機(jī)讓目標(biāo)機(jī)強(qiáng)制宕機(jī)或重啟,不失為一個(gè)好主意。

      • .crash
      • .reboot

      crash命令能引發(fā)一個(gè)系統(tǒng)藍(lán)屏,并生成dump文件;而.reboot使系統(tǒng)重啟,不產(chǎn)生dump文件。

      2. 符號(hào)與源碼

      符號(hào)與源碼是調(diào)試過程中的重要因素,它們使得枯燥生硬的調(diào)試內(nèi)容更容易地調(diào)試人員讀懂。在可能的情況下,應(yīng)該盡量地為模塊加載符號(hào)和源碼。大部分情況下源碼難以得到,但符號(hào)卻總能以符號(hào)文件的形式易于得到。

      什么是符號(hào)文件呢?編譯器和鏈接器在創(chuàng)建二進(jìn)制鏡像文件(諸如exe、dll、sys)時(shí),伴生的后綴名為.dbg、.sym或.pdb的包含鏡像文件編譯、鏈接過程中生成的符號(hào)信息的文件稱為符號(hào)文件。具體來說,符號(hào)信息包括如下內(nèi)容:

      • 全局變量(類型、名稱、地址);
      • 局部變量(類型、名稱、地址);
      • 函數(shù)(名稱、原型、地址);
      • 變量、結(jié)構(gòu)體類型定義;

      源文件路徑以及每個(gè)符號(hào)對(duì)應(yīng)于源文件中的行號(hào),這是進(jìn)行源碼級(jí)別調(diào)試的基礎(chǔ)。

      有這么多的信息包含在符號(hào)文件中,使得符號(hào)文件通常要比二進(jìn)制文件(PE格式文件)本身要大很多。調(diào)試過程中,符號(hào)之重要性不言而喻。只有正確設(shè)置了符號(hào)路徑,使得調(diào)試器能夠?qū)⒄{(diào)試目標(biāo)、符號(hào)文件以及源碼文件一一對(duì)應(yīng)起來,才能夠最好地發(fā)揮調(diào)試器的強(qiáng)大功用。

      symbols

      符號(hào)信息隸屬于指定的模塊,所以只有調(diào)試器需要用到某個(gè)模塊時(shí),他的符號(hào)信息才有被加載和分析的必要。所以我們?cè)谥v符號(hào)內(nèi)容之前,先講和模塊相關(guān)的命令。

      2.1 模塊列表

      每個(gè)可執(zhí)行程序都是由若干個(gè)模塊構(gòu)成,有些模塊靜態(tài)加載,有些模塊以動(dòng)態(tài)方式進(jìn)行加載。所以對(duì)于有些模塊,可能在A時(shí)刻運(yùn)行時(shí)被加載,而在B時(shí)刻運(yùn)行時(shí),自始至終都未被加載。調(diào)試過程中,調(diào)試器根據(jù)模塊的加載情況加載符號(hào)。有幾個(gè)命令可以用來列舉模塊列表,分別是:lm、!dlls、.reload /l、!imgreloc。下面分別來看。

      • lm [選項(xiàng)] [a Address] [m Pattern | M Pattern]

      lm是list loaded modules的縮寫,他還有一個(gè)DML版本:

      • lmD [選項(xiàng)] [a Address] [m Pattern | M Pattern]

      使用/v選項(xiàng)能列出模塊的詳細(xì)信息,包括:模塊名、模塊地址、模塊大小、鏡像名、時(shí)間戳、以及對(duì)應(yīng)的符號(hào)文件信息(包括類型、路徑、類型、編譯器、符號(hào)加載狀態(tài))。

      如使用參數(shù)a,后面跟地址(address),則只有指定地址所在的模塊能夠被列出;

      如使用參數(shù)m,后面跟一個(gè)表示模塊名的字符串通配符,如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)

      下面介紹另一個(gè)命令:

      • !dlls [選項(xiàng)] [LoaderEntryAddress]

                  首先看他的可選參數(shù):

                  -i/-l/-m:排序方式,分別按照初始化順序、加載順序、內(nèi)存起始地址順序排列。

      -a:列出鏡像文件PE結(jié)構(gòu)的文件頭、Section頭等詳細(xì)信息,是分析PE結(jié)構(gòu)的好幫手(更好的幫手是利用自如PEView或Stud_PE等UI工具)。

                  -c:指定函數(shù)所在的模塊。這個(gè)選項(xiàng)非常實(shí)用,比如我想知道NtCreateFile函數(shù)是哪個(gè)模塊暴露出來的接口,如下:

      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選項(xiàng)后,也能列舉模塊,其命令格式如下:

      • .reload /l

      最后再來看一個(gè)!imgreloc命令,它也能夠列出模塊列表并顯示各模塊地址。但其主要作用尚不在此,它用來判斷各個(gè)模塊是否處于preferred地址范圍。所謂Preferred地址是這么一回事:二進(jìn)制文件在編譯的時(shí)候,編譯器都會(huì)為其設(shè)置一個(gè)理想地址(Preferred Address),這樣二進(jìn)制文件被加載時(shí),系統(tǒng)會(huì)盡可能將他映射到這個(gè)理想地址。當(dāng)然,所謂“理想”往往是會(huì)受到“現(xiàn)實(shí)”的挑戰(zhàn)的,當(dāng)存在地址競(jìng)爭(zhēng)的時(shí)候,需要適當(dāng)調(diào)整二進(jìn)制文件的加載地址,選擇另一個(gè)合適的地方加載之。!imgreloc命令就是用來查看這種情況的,命令如下:

      • !imgreloc [模塊地址]

      命令!imgReloc是Image Relocate的縮寫,字面已能夠反映其含義:鏡像文件重定位信息。下面是一個(gè)例子。

       imgreloc

            上例中,大部分系統(tǒng)模塊(上圖下部方框所示)其地址由于事先經(jīng)過統(tǒng)籌分配,所以一般都能被加載到preferred地址處。只有少數(shù)模塊(如最上面的Normaliz模塊)由于地址沖突而受到了調(diào)整。

      2.2 模塊信息

            上一節(jié)我們了解了如何枚舉模塊列表,這一節(jié)我們研究針對(duì)單個(gè)模塊,如何獲取詳細(xì)信息。有多個(gè)命令可以查看指定模塊的詳細(xì)模塊信息,這包括:lm、!dh、lmi等,下面來一一介紹。

      首先看lm,這個(gè)命令上面我們已經(jīng)介紹過,現(xiàn)在利用它來獲取指定模塊信息。其命令格式如下:

      • lm v a 模塊地址

      這里使用了v選項(xiàng),以顯示詳細(xì)(verbose)信息;并使用a參數(shù)以指定模塊地址。通過此命令顯示的信息,和我們?cè)趀xplorer資源管理器中通過鼠標(biāo)右鍵查看一個(gè)文件的屬性所看到的信息差不多。請(qǐng)看下面的清單:

      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: <產(chǎn)品名>
          InternalName:     UsbKitApp.exe
          OriginalFilename: UsbKitApp.exe
          ProductVersion:   1.0.0.1
          FileVersion:      1.0.0.1
          FileDescription:  TODO: <文件說明>
          LegalCopyright:   TODO: (C) <公司名>。保留所有權(quán)利。

          下面看!lmi命令,此命令通過指定模塊地址查找模塊并獲取其信息,其命令格式如下

      • !lmi 模塊地址

      此命令側(cè)重獲取對(duì)調(diào)試器有用的信息,請(qǐng)看下面的列表:

      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

       如果還要查看更詳細(xì)、豐富的模塊信息,可以使用!dh命令,命令格式如下:

      • !dh [標(biāo)志] 模塊地址

      dh是display header的縮寫,直譯就是“顯示文件頭”的意思,它能夠顯示非常詳細(xì)的PE頭信息。下圖截取了輸出信息中的開頭部分,其它詳細(xì)內(nèi)容,需要讀者熟悉微軟的PE結(jié)構(gòu)才能看懂:

      !dh

      模塊相關(guān)的知識(shí)點(diǎn)講完了,下面講符號(hào)有關(guān)命令。和符號(hào)相關(guān)的知識(shí)點(diǎn)包括:符號(hào)路徑、符號(hào)服務(wù)器、符號(hào)緩存、符號(hào)加載以及符號(hào)的使用等。

      2.3 符號(hào)路徑

      什么是符號(hào)路徑呢?就是調(diào)試器尋找符號(hào)文件的方向,它可以是本地文件夾路徑、可訪問的UNC路徑、或者是符號(hào)服務(wù)器路徑。什么是符號(hào)服務(wù)器呢?如果調(diào)試過程中,需要涉及到成千上萬(wàn)個(gè)符號(hào)文件,以及同一個(gè)符號(hào)文件存在不同平臺(tái)下的不同符號(hào)文件版本的時(shí)候,那么一一手動(dòng)設(shè)置符號(hào)路徑肯定是不現(xiàn)實(shí)的,于是引入符號(hào)服務(wù)器的概念。符號(hào)服務(wù)器有一套命名規(guī)則,使得調(diào)試軟件能夠正確找到需要的符號(hào)文件。一般來說,符號(hào)服務(wù)器比較大,都是共用的,放在遠(yuǎn)程主機(jī)上。為了降低網(wǎng)絡(luò)訪問的成本,又引入了符號(hào)緩存的概念,即將從服務(wù)器上下載到的符號(hào)文件,保存在本地緩存中,以后調(diào)試器需要符號(hào)文件的時(shí)候,先從緩存中尋找,找不到的時(shí)候再到服務(wù)器上下載。下面分幾部分一一來看。

      設(shè)置符號(hào)路徑:

      設(shè)置符號(hào)路徑的語(yǔ)法如下:

      • .sympath  [+]  [路徑]

      如果不加入任何參數(shù)執(zhí)行.sympath命令,將顯示當(dāng)前的路徑設(shè)置:

      • .sympath

      如要覆蓋原來的路徑設(shè)置,使用新路徑即可:

      • .sympath  <新路徑>

      要在原有路徑的基礎(chǔ)上添加一個(gè)新路徑,可使用:

      • .sympath+  <新增路徑>

      要注意的是,使用.sympath改變或新增符號(hào)路徑后,符號(hào)文件并不會(huì)自動(dòng)更新,應(yīng)再執(zhí)行.reload命令以更新之。

      這里要談一談延遲加載的知識(shí)點(diǎn)。延遲加載使得模塊的符號(hào)表,只在第一次真正使用的時(shí)候才被加載。這加快了程序啟動(dòng),不用在一開始耗費(fèi)大量時(shí)間加載全部的符號(hào)文件。

      使用.symopt +4和.symopt -4來開啟或關(guān)閉延遲加載設(shè)置。

      在已經(jīng)啟動(dòng)了延遲加載的情況下,如想臨時(shí)改變策略,立刻將指定模塊的符號(hào)加載到調(diào)試器中,可以使用ld或者.reload /f命令。

      符號(hào)服務(wù)器與符號(hào)緩存:

      設(shè)置符號(hào)服務(wù)器的基本語(yǔ)法是:

      • SRV*[符號(hào)緩存]*服務(wù)器地址

      語(yǔ)法由SRV引導(dǎo),符號(hào)緩存和服務(wù)器地址的前面各有一個(gè)星號(hào)引導(dǎo)。符號(hào)緩存一般也叫做下游符號(hào)庫(kù)。如某公司有一臺(tái)專門的符號(hào)服務(wù)器,地址為\\symsrv\\symbols,則他們公司的所有開發(fā)人員都應(yīng)該在他們的調(diào)試器中使用類似下面的命令:

      • .sympath+ srv*c:\symbols*\\symsrv\symbols

      此外,我們總是應(yīng)該把微軟的公用符號(hào)庫(kù)加入到我們的符號(hào)路徑中:

      這是一臺(tái)微軟對(duì)外公開的服務(wù)器,使用http地址訪問,不是所有人都能牢記這個(gè)網(wǎng)址,所以最好的辦法就是使用.symfix命令,語(yǔ)法如下:

      • .symfix [+] [符號(hào)緩存地址]

      這個(gè)命令等價(jià)于上面的.sympath命令,而不用輸入長(zhǎng)長(zhǎng)的http地址。

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

      符號(hào)選項(xiàng):

      命令格式如下:

      • 顯示當(dāng)前設(shè)置:.symopt
      • 增加選項(xiàng):.symopt+  Flags
      • 刪除選項(xiàng):.symopt-  Flags

      第一個(gè)命令沒有任何參數(shù),顯示當(dāng)前設(shè)置。后面兩個(gè),第二個(gè)命令含有“+”代表添加一個(gè)選項(xiàng),第三個(gè)命令含有“-”代表去除一個(gè)選項(xiàng)。

      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

      可用的符號(hào)選項(xiàng)請(qǐng)見下表:

      可讀名稱

      描述

      0×1

      SYMOPT_CASE_INSENSITIVE

      符號(hào)名稱不區(qū)分大小寫

      0×2

      SYMOPT_UNDNAME

      符號(hào)名稱未修飾

      0×4

      SYMOPT_DEFERRED_LOADS

      延遲加載

      0×8

      SYMOPT_NO_CPP

      關(guān)閉C++轉(zhuǎn)換,C++中的::符號(hào)將以__顯示

      0×10

      SYMOPT_LOAD_LINES

      從源文件中加載行號(hào)

      0×20

      SYMOPT_OMAP_FIND_

      NEAREST

      如果由于編譯器優(yōu)化導(dǎo)致找不到對(duì)應(yīng)的符號(hào),就以最近的一個(gè)符號(hào)代替之

      0×40

      SYMOPT_LOAD_ANYTHING

      使得符號(hào)匹配的時(shí)候,匹配原則較松散,不那么嚴(yán)格。

      0×80

      SYMOPT_IGNORE_CVREC

      忽略鏡像文件頭中的CV記錄

      0×100

      SYMOPT_NO_UNQUALIFIED_

      LOADS

      只在已加載模塊中搜索符號(hào),如果搜索符號(hào)失敗,不會(huì)自動(dòng)加載新模塊。

      0×200

      SYMOPT_FAIL_CRITICAL_

      ERRORS

      不顯示文件訪問錯(cuò)誤對(duì)話框。

      0×400

      SYMOPT_EXACT_SYMBOLS

      進(jìn)行最嚴(yán)格的符號(hào)文件檢查,只要有微小的差異,符號(hào)文件都不會(huì)被加載。

      0×800

      SYMOPT_ALLOW_ABSOLUTE_

      SYMBOLS

      允許從內(nèi)存的一個(gè)絕對(duì)地址處讀取符號(hào)信息。

      0×1000

      SYMOPT_IGNORE_NT_

      SYMPATH

      忽視在環(huán)境變量中設(shè)置的符號(hào)路徑,也忽視被調(diào)試進(jìn)程的執(zhí)行路徑。也就是說,當(dāng)搜索符號(hào)文件的時(shí)候,不會(huì)從這些路徑中搜索。

      0×2000

      SYMOPT_INCLUDE_32BIT_MODULES

      讓運(yùn)行在安騰系統(tǒng)上的調(diào)試器,也枚舉32位模塊。

      0×4000

      SYMOPT_PUBLICS_ONLY

      僅搜索符號(hào)文件的公共(PUBLIC)符號(hào)表,忽略私有符號(hào)表。

      0×8000

      SYMOPT_NO_PUBLICS

      不搜索符號(hào)文件的公共(PUBLIC)符號(hào)表

      0×10000

      SYMOPT_AUTO_PUBLICS

      先搜索pdb文件的私有符號(hào)表,如果在其中找到對(duì)應(yīng)的符號(hào),就不再搜索公共(PUBLIC)符號(hào)表,這可以加快搜索速度。

      0×20000

      SYMOPT_NO_IMAGE_SEARCH

      不搜索鏡像拷貝

      0×40000

      SYMOPT_SECURE

      安全模式,讓調(diào)試器盡量不影響到主機(jī)。

      0×80000

      SYMOPT_NO_PROMPTS

      不顯示符號(hào)代理服務(wù)器的認(rèn)證對(duì)話框,將導(dǎo)致某些時(shí)候無(wú)法訪問符號(hào)服務(wù)器

      0×80000000

      SYMOPT_DEBUG

      顯示符號(hào)搜索的詳細(xì)過程和信息

      表8-1 符號(hào)選項(xiàng)

      2.4 符號(hào)加載

      本節(jié)分下面幾個(gè)子題目分別講解。

      立刻加載:

      其命令格式如下:

      • ld 模塊名 [/f 符號(hào)文件名]

      加載指定模塊的符號(hào)。調(diào)試器默認(rèn)采用延遲模式加載符號(hào),也就是直到符號(hào)被使用的時(shí)候,才將符號(hào)文件加載到調(diào)試器中并進(jìn)行解析。ld使得延遲模式被打破,讓指定模塊的符號(hào)文件立刻加載到調(diào)試器中。此指令可為模塊的符號(hào)文件設(shè)置自定義的匹配名稱,比如:

      • ld  123  /f  abc

      這樣一來,abc.pdb將成為123.exe的符號(hào)文件。正常情況下,這是不可能的,只能是abc.pdb對(duì)應(yīng)abc.exe。

      重新加載:

      如果對(duì)自己正在使用的符號(hào)文件感到疑惑,比如源代碼和行號(hào)明顯不匹配,最好的做法就是重新加載一下符號(hào)文件。此命令語(yǔ)法如下:

      • .reload /f /v [模塊名]

      .reload命令的作用是刪除指定或所有已加載的符號(hào)文件,默認(rèn)情況下,調(diào)試器不會(huì)立刻根據(jù)符號(hào)路徑重新搜索并加載新的符號(hào)文件,而是推遲到調(diào)試器下一次使用到此文件時(shí)。

      使用/f參數(shù)(force),將迫使調(diào)試器立刻搜索并重新加載新的符號(hào)文件。

      其它參數(shù)解釋如下:

      • /v:將搜索過程中的詳細(xì)信息都顯示出來。
      • /i:不檢查pdb文件的版本信息;
      • /l:只顯示模塊信息,內(nèi)核模式下,和“l(fā)m n t”命令類似,但顯示內(nèi)容比后者更多,因?yàn)榘擞脩裟K信息;
      • /n:僅重載內(nèi)核符號(hào),不重載用戶符號(hào);
      • /o:強(qiáng)制覆蓋符號(hào)庫(kù)中的符號(hào)文件,即使版本相同;
      • /d:用戶層模式下使用Windbg時(shí)的默認(rèn)選項(xiàng),重載調(diào)試器模塊列表中的所有模塊;
      • /s:內(nèi)核模式下使用Windbg時(shí)的默認(rèn)選項(xiàng),重載系統(tǒng)模塊列表中的所有模塊,另外,如果調(diào)試器在用戶模式下運(yùn)行,要加載內(nèi)核模塊,也必須使用/s選項(xiàng),否則調(diào)試器將只會(huì)在調(diào)試器模塊列表中搜索而導(dǎo)致找不到內(nèi)核模塊;
      • /u:卸載指定模塊。如發(fā)現(xiàn)當(dāng)前符號(hào)版本不對(duì),使用/u開關(guān)先卸載之再重新加載。

      符號(hào)驗(yàn)證:

      上面講到.reload的時(shí)候,我們說過,符號(hào)文件會(huì)出現(xiàn)不匹配的情況。這是很有可能的,程序員在后期測(cè)試的時(shí)候可能會(huì)將工程多次編譯,為了維護(hù)多個(gè)版本而使得自己也被搞混??梢允褂孟旅娴拿铗?yàn)證一個(gè)模塊的符號(hào)文件:

      • !chksym <模塊名> [符號(hào)名]

      加載選項(xiàng):!sym

      有兩類符號(hào)加載選項(xiàng)。第一類是Noisy/Quiet,Noisy選項(xiàng)將打印符號(hào)加載的詳細(xì)信息,Quiet選項(xiàng)則忽略這些信息。第二類是Prompts/Prompts off,即是否允許執(zhí)行提示(Prompts)對(duì)話框。

      一般都是在調(diào)用.reload 命令之前,執(zhí)行加載選項(xiàng)命令,以見立竿見影之效。

      所謂Noisy是吵鬧的意思,調(diào)試器在搜索、加載符號(hào)的時(shí)候,會(huì)顯示更多與搜索有關(guān)的信息。而安靜模式下,則不會(huì)顯示這些信息。不管吵鬧與否,都不會(huì)影響到最終的搜索、加載結(jié)果。

      當(dāng)從網(wǎng)絡(luò)上下載符號(hào)文件的時(shí)候,可能會(huì)碰到網(wǎng)絡(luò)服務(wù)器要求客戶進(jìn)行安全認(rèn)證的情況,如果開啟Prompts選項(xiàng),則彈出認(rèn)證對(duì)話框,讓用戶輸入認(rèn)證信息;否則,不彈出對(duì)話框,并且不會(huì)下載符號(hào)文件。

      不加任何參數(shù)的情況下,顯示當(dāng)前加載選項(xiàng)設(shè)置,下面的清單表明當(dāng)前的設(shè)置為Quite及Prompts模式:

      lkd> !sym
      !sym <noisy/quiet - prompts/prompts off> - quiet mode - symbol prompts on

      2.5 符號(hào)搜索

      符號(hào)搜索包括全局搜索和就近搜索兩種。

      全局搜索:

      命令“x”被用來進(jìn)行符號(hào)的全局搜索,你可以把它直接就理解為search。格式如下:

      • x  [參數(shù)]  [模塊!符號(hào)]

      如果什么參數(shù)都沒有的話,它將列出當(dāng)前調(diào)試環(huán)境下的所有局部變量,前提是要在有局部變量存在的情況下,顯示局部變量的另一個(gè)命令是dv,后文也會(huì)講到。

      • x  kernel32!a*,

      上面命令搜索并打印出kernel32模塊中所有a開頭的符號(hào)。x命令支持DML,使用/D選項(xiàng)即以DML格式顯示。

      x-command

      如果你不知道ntcreatefile這個(gè)函數(shù)是在哪個(gè)模塊中定義的,可以試著使用下面的命令:

      • x  *!*NtCreateFile* (注:亦請(qǐng)參照!dlls –c命令)

      同名函數(shù)在多個(gè)系統(tǒng)模塊中并定義,這可能出乎你的意料,但卻給你帶來真正的知識(shí)。

      此外,x命令有多個(gè)可選參數(shù)。建議總是帶上/t和/v,可顯示更多符號(hào)、類型信息。

      • /f:將只顯示函數(shù)符號(hào);并且會(huì)顯示函數(shù)的詳細(xì)定義。
      • /d:顯示更多的變量類型相關(guān)信息。 

      就近搜索

      如果知道了符號(hào)的大概地址,但不能確定確切的符號(hào)名稱,該怎么處理呢?就近查找命令ln能發(fā)揮作用,ln是List Nearest的縮寫。它的作用是:(根據(jù)給定的地址)列出附近一定范圍內(nèi)的所有符號(hào)。下表中,指定地址0x7c8179f0的前后各有一個(gè)符號(hào)被找到:

      0:000> ln 7c8179f0
      (7c8179c3)   kernel32!NlsServerInitialize+0x29   |  (7c8179fe)   kernel32!AllocTables

      2.6 源碼命令

      如果含有源碼信息,可使得調(diào)試過程能夠以源碼模式逐行進(jìn)行。和源碼相關(guān)的命令包括下面幾個(gè): 

      源碼路徑:

      和符號(hào)路徑類似,要設(shè)置源碼路徑,使用如下語(yǔ)法格式:

      • .srcpath[+] [路徑1;路徑2]

      不含任何參數(shù)的情況下,顯示當(dāng)前設(shè)置的源碼路徑。

      下面命令將覆蓋原設(shè)置,設(shè)置新的源碼搜索路徑:

      • .srcpath <路徑信息>

      使用“+”可以將新的路徑添加到原設(shè)置中,而不會(huì)把原設(shè)置覆蓋掉:

      • .srcpath+ <路徑信息> 

      源碼選項(xiàng)

      這里列出的源碼選項(xiàng)有三個(gè),下面分別來講。第一個(gè)是源碼的Noisy選項(xiàng),語(yǔ)法如下:

      • .srcnoisy [1|0]

      此命令乃source noisy縮寫,可以理解成“嘈雜的源碼”,類似于符號(hào)設(shè)置中也存在的noisy選項(xiàng)。他的三種運(yùn)用如下所示:

      • 狀態(tài):.srcnoisy
      • 開啟:.srcnoisy 1
      • 關(guān)閉:.srcnoisy 0

      開始“吵鬧的源碼”選項(xiàng)后,在源碼加載、卸載,甚至單步的時(shí)候,都會(huì)顯示豐富的源碼信息。下圖顯示了一個(gè)含有Noisy信息的單步命令:

       sourcenoisy

      第二個(gè)命令是行號(hào)選項(xiàng),即在符號(hào)文件加載過程中,是否將行號(hào)也一并加載進(jìn)來。因?yàn)閃indbg支持源碼級(jí)調(diào)試,所以它在Windbg中是默認(rèn)開啟(Enable)的,我們一般也不應(yīng)該去禁止他。語(yǔ)法如下:

      • .lines [-d|-e|-t]

      參數(shù)d是disable的意思;e是enable的意思;t表示切換的意思,即自動(dòng)在disable和enable兩者之間切換。

      最后看第三個(gè)命令,是代碼行選項(xiàng),包括行號(hào)和內(nèi)容。語(yǔ)法如下:

      • 打開:l+ [選項(xiàng)]
      • 關(guān)閉:l- [選項(xiàng)]

      命令l是line的縮寫,和上面的.lines命令不同的是,.lines是加載時(shí)選項(xiàng),l是調(diào)試時(shí)選項(xiàng)。我建議讀者總是調(diào)用“l(fā)+*”指令,打開所有的行選項(xiàng),效果會(huì)很不錯(cuò)。這樣在單步調(diào)試的時(shí)候,每一步的代碼和行號(hào)都會(huì)顯示出來。顯得很醒目!

                  值得注意的是,進(jìn)入源碼模式和進(jìn)入?yún)R編模式的命令分別為:

      • 源碼模式:l+t
      • 匯編模式:l-t

      運(yùn)行這兩個(gè)命令和在Windbg的Debug菜單下點(diǎn)擊source mode選項(xiàng)其效果是一樣的。

      3 進(jìn)程與線程

      既可以顯示進(jìn)程和線程列表,又可以顯示指定進(jìn)程或線程的詳細(xì)信息。調(diào)試命令可以提供比taskmgr更詳盡的進(jìn)程資料,在調(diào)試過程中不可或缺。

      3.1 進(jìn)程命令

      進(jìn)程命令包括這些內(nèi)容:顯示進(jìn)程列表、進(jìn)程環(huán)境塊、設(shè)置進(jìn)程環(huán)境。

      進(jìn)程列表

      多個(gè)命令可顯示進(jìn)程列表,但一般只能在特定情況下使用,它們是:|、.tlist、!process和!dml_proc。

      豎線命令顯示當(dāng)前被調(diào)試進(jìn)程列表的狀態(tài)信息,這個(gè)命令在本章開頭已作過介紹,命令格式如下:

      • |  [進(jìn)程號(hào)]

      請(qǐng)注意這里的定語(yǔ):被調(diào)試進(jìn)程列表。大多數(shù)情況下調(diào)試器中只有一個(gè)被調(diào)試進(jìn)程,但可以通過.attach或者.create命令同時(shí)掛載或創(chuàng)建多個(gè)調(diào)試對(duì)象。當(dāng)同時(shí)對(duì)多個(gè)進(jìn)程調(diào)試時(shí),進(jìn)程號(hào)是從0開始的整數(shù)。下圖中顯示了兩個(gè)被調(diào)試的進(jìn)程。

      attach-process

      如何在多個(gè)進(jìn)程間進(jìn)行切換呢?使用s參數(shù)即可,這一點(diǎn)前文已然講過。

      • .tlist [選項(xiàng)] [模塊名]

                  .tlist命令顯示當(dāng)前系統(tǒng)中的進(jìn)程列表,他是目前唯一可在用戶模式下顯示系統(tǒng)當(dāng)前進(jìn)程列表的命令。它有兩個(gè)可選項(xiàng):-v顯示進(jìn)程詳細(xì)信息,-c只顯示當(dāng)前進(jìn)程信息。

                  內(nèi)核模式下同樣可以使用.tlist,但更好的命令是!process。!process在內(nèi)核模式下顯示進(jìn)程列表,和指定進(jìn)程的詳細(xì)信息,也能顯示進(jìn)程中的線程和調(diào)用棧內(nèi)容。典型格式如下:

      • !process: 顯示調(diào)試器當(dāng)前運(yùn)行進(jìn)程信息
      • !process 0 0: 顯示進(jìn)程列表
      • !process PID: PID是進(jìn)程ID,根據(jù)進(jìn)程ID顯示此進(jìn)程詳細(xì)信息。

      dml-process

      此外,還有一個(gè)DML版本的進(jìn)程列表命令,如下:
      • !dml_proc [進(jìn)程號(hào)|進(jìn)程地址]

      此命令可以看成“|”和“!process”命令的DML合并版本,可在用戶與內(nèi)核模式下使用。顯示的進(jìn)程信息偏重于線程和調(diào)用棧。用戶模式下此命令和“|”一樣,只能顯示被調(diào)試進(jìn)程的信息。右圖是內(nèi)核模式下使用此命令的效果:

      進(jìn)程信息

      進(jìn)程環(huán)境塊(Process Enviroment Block)是內(nèi)核結(jié)構(gòu)體,使用!peb命令參看其信息,但也可以用dt命令查看完整的結(jié)構(gòu)體定義。格式如下:

      • !peb  [地址]

      如果未設(shè)置PEB地址,則默認(rèn)為當(dāng)前進(jìn)程。內(nèi)核模式下可通過!process命令獲取PEB結(jié)構(gòu)體地址;用戶模式下只能顯示當(dāng)前進(jìn)程的PEB信息,故而一般不帶參數(shù)。

      • dt  nt!_peb  地址

      此命令顯示系統(tǒng)nt模塊中所定義的內(nèi)核結(jié)構(gòu)體PEB詳細(xì)內(nèi)容。使用之前必須先熟悉結(jié)構(gòu)體定義。

      進(jìn)程切換

      進(jìn)程環(huán)境的切換,將伴隨著與進(jìn)程相關(guān)的寄存器、堆棧的切換。在不同進(jìn)程環(huán)境中進(jìn)行的調(diào)試結(jié)果有天壤之別。上文在講“|”命令的時(shí)候,講過用戶環(huán)境下多進(jìn)程間如何互相切換,使用命令:

      • |  [進(jìn)程號(hào)]  s

      那么內(nèi)核模式下,情況又不同了。內(nèi)核模式下的進(jìn)程切換,不同于用戶模式下的被調(diào)試進(jìn)程間切換,而是系統(tǒng)存在的多進(jìn)程間切換。內(nèi)核環(huán)境下,以進(jìn)程地址作為參數(shù),調(diào)用如下命令以進(jìn)行進(jìn)程環(huán)境切換:

      • .process [進(jìn)程地址]

      如果不使用任何參數(shù),.process命令將顯示當(dāng)前進(jìn)程地址。所謂進(jìn)程地址,即ERPCESS結(jié)構(gòu)體地址。

      或以頁(yè)目錄地址為參數(shù),調(diào)用下面命令切換用戶地址空間:

      • .context [頁(yè)目錄地址]

      如果不使用任何參數(shù),.context命令將顯示當(dāng)前頁(yè)目錄地址。頁(yè)目錄地址就是!process命令中顯示的DirBase值。

      進(jìn)程切換后,為了檢測(cè)是否正確切換,可再用!peb命令檢查當(dāng)前進(jìn)程的環(huán)境信息。

      3.2 線程命令

      命令“~”能夠進(jìn)行線程相關(guān)的操作。不帶任何參數(shù)的情況下,它列出當(dāng)前調(diào)試進(jìn)程的線程。下圖是計(jì)算器進(jìn)程某時(shí)刻的線程列表:

      ||0:0:001> ~
      #  0  Id: f78.374 Suspend: 0 Teb: 000007ff`fffdc000 Unfrozen
      .  1  Id: f78.fb0 Suspend: 0 Teb: 000007ff`fffda000 Unfrozen
         2  Id: f78.a4c Suspend: 0 Teb: 000007ff`fffd8000 Unfrozen
         3  Id: f78.22c8 Suspend: 0 Teb: 000007ff`fff9a000 Unfrozen
         4  Id: f78.2658 Suspend: 0 Teb: 000007ff`fffd4000 Unfrozen
         5  Id: f78.cbc Suspend: 0 Teb: 000007ff`fff96000 Unfrozen
         6  Id: f78.21ec Suspend: 0 Teb: 000007ff`fffd6000 Unfrozen

      使用此命令可進(jìn)行的線程操作包括:線程切換、線程環(huán)境、線程時(shí)間等。

      線程冰凍

      參數(shù)f與u分別代表freeze和unfress,前者是指凍住指定線程,后者將被冰凍線程解凍。

      • ~2f

      表示把2號(hào)線程凍住,在解凍之前,不再分發(fā)CPU時(shí)間給它。

      若要讓指定線程重新運(yùn)行,需使用參數(shù)u:

      • ~2u

      針對(duì)這兩個(gè)命令,下面有一個(gè)小實(shí)驗(yàn):

      運(yùn)行Windbg,并選擇調(diào)試記事本程序(Notepad.exe)。程序起來后,CTL+BREAK中斷程序運(yùn)行,輸入命令: 
      
      ~0f 
      
      再輸入g命令讓記事本繼續(xù)運(yùn)行。 
      
      此時(shí)嘗試用鼠標(biāo)定位到notepad軟件,發(fā)現(xiàn)軟件界面無(wú)法被定位、移動(dòng)、最大小化,甚至“清空桌面”操作也無(wú)濟(jì)于事。這是因?yàn)?號(hào)線程為notepad的主線程,被凍住后整個(gè)軟件都失去響應(yīng)。 
      
      更嚴(yán)重的是,“清空桌面”操作(Win + D)也會(huì)失效,應(yīng)是Notepad拒絕響應(yīng)的緣故。
      
      線程掛起 
      
      參數(shù)n和m分別代表increase和resume,前者增加一個(gè)線程掛起計(jì)數(shù),后者減少一個(gè)線程掛起計(jì)數(shù)。如果兩次增加線程掛起計(jì)數(shù)(即達(dá)到2),則必須兩次resume才能讓線程恢復(fù)到運(yùn)行狀態(tài)。

      把上面實(shí)驗(yàn)中的~0f命令改變成~0n,也能達(dá)到相似的效果。

      線程切換

      查看指定線程的信息,用下面的命令:

      • ~ 線程號(hào)

      線程號(hào)是由調(diào)試器軟件內(nèi)部維護(hù)的線程ID值,是一個(gè)從0開始的整數(shù),和線程ID不是一回事。

      線程信息中包括有線程環(huán)境塊地址,可通過!teb命令查看環(huán)境塊信息:

      • !teb  [teb地址]

      如要在多線程間作切換,需使用~命令的s參數(shù):

      • ~  線程號(hào) s

      由于線程號(hào)在外部是沒有太大意義的,所以另一個(gè)線程切換命令是以線程ID來標(biāo)識(shí)一個(gè)線程的。這個(gè)命令比較奇怪,以雙波浪線打頭,格式如下:

      • ~~[線程ID]  s

      注意這個(gè)命令中的[]并非可選符,而是命令的一部分。例如命令:~~[11a0] s,它將當(dāng)前線程切換到線程ID為0x11a0的線程。線程ID是系統(tǒng)維護(hù)的系統(tǒng)唯一的ID值。

      thread-info

      下圖是關(guān)于線程切換的:

      0:014> ~
         0  Id: 4f0.4f4 Suspend: 1 Teb: 00000000`7efdb000 Unfrozen
         1  Id: 4f0.5e0 Suspend: 1 Teb: 00000000`7ef9d000 Unfrozen
         2  Id: 4f0.604 Suspend: 1 Teb: 00000000`7ef9a000 Unfrozen
         3  Id: 4f0.61c Suspend: 1 Teb: 00000000`7ef97000 Unfrozen
         4  Id: 4f0.600 Suspend: 1 Teb: 00000000`7ef94000 Unfrozen
         5  Id: 4f0.610 Suspend: 1 Teb: 00000000`7ef91000 Unfrozen
         6  Id: 4f0.608 Suspend: 1 Teb: 00000000`7ef8e000 Unfrozen
         7  Id: 4f0.60c Suspend: 1 Teb: 00000000`7ef8b000 Unfrozen
         8  Id: 4f0.614 Suspend: 1 Teb: 00000000`7ef88000 Unfrozen
         9  Id: 4f0.5fc Suspend: 1 Teb: 00000000`7ef85000 Unfrozen
        10  Id: 4f0.5f4 Suspend: 1 Teb: 00000000`7ef82000 Unfrozen
        11  Id: 4f0.5f8 Suspend: 1 Teb: 00000000`7ef7f000 Unfrozen
        12  Id: 4f0.f58 Suspend: 1 Teb: 00000000`7ef76000 Unfrozen
        13  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 Unfrozen
      
      0:016> ~~[5fc]s
      wow64cpu!CpupSyscallStub+0x9:
      00000000`74412e09 c3              ret
      
      0:009> ~
         0  Id: 4f0.4f4 Suspend: 1 Teb: 00000000`7efdb000 Unfrozen
         1  Id: 4f0.5e0 Suspend: 1 Teb: 00000000`7ef9d000 Unfrozen
         2  Id: 4f0.604 Suspend: 1 Teb: 00000000`7ef9a000 Unfrozen
         3  Id: 4f0.61c Suspend: 1 Teb: 00000000`7ef97000 Unfrozen
         4  Id: 4f0.600 Suspend: 1 Teb: 00000000`7ef94000 Unfrozen
         5  Id: 4f0.610 Suspend: 1 Teb: 00000000`7ef91000 Unfrozen
         6  Id: 4f0.608 Suspend: 1 Teb: 00000000`7ef8e000 Unfrozen
         7  Id: 4f0.60c Suspend: 1 Teb: 00000000`7ef8b000 Unfrozen
         8  Id: 4f0.614 Suspend: 1 Teb: 00000000`7ef88000 Unfrozen
      .  9  Id: 4f0.5fc Suspend: 1 Teb: 00000000`7ef85000 Unfrozen
        10  Id: 4f0.5f4 Suspend: 1 Teb: 00000000`7ef82000 Unfrozen
        11  Id: 4f0.5f8 Suspend: 1 Teb: 00000000`7ef7f000 Unfrozen
        12  Id: 4f0.f58 Suspend: 1 Teb: 00000000`7ef76000 Unfrozen
        13  Id: 4f0.28e8 Suspend: 1 Teb: 00000000`7efd5000 Unfrozen
        14  Id: 4f0.1b80 Suspend: 1 Teb: 00000000`7ef79000 Unfrozen
        15  Id: 4f0.17cc Suspend: 1 Teb: 00000000`7efd8000 Unfrozen
      # 16  Id: 4f0.1538 Suspend: 1 Teb: 00000000`7ef7c000 Unfrozen

      第一個(gè)命令“~”運(yùn)行時(shí),當(dāng)前線程是14號(hào)線程,請(qǐng)注意3號(hào)線程前面有一小點(diǎn);運(yùn)行第二個(gè)命令,將當(dāng)前線程切換為9號(hào)線程;為了檢驗(yàn)結(jié)果,再次運(yùn)行“~”命令,此時(shí)注意到小點(diǎn)移到9號(hào)線程前,表明9號(hào)線程為當(dāng)前線程。 

      線程遍歷

      仍然是~命令。它除了能夠作為線程列表命令外,還可用來對(duì)線程進(jìn)行遍歷,并執(zhí)行指定命令。只需借助通配符“*”即可。如:

      • ~*k

      顯示所有線程棧信息(此命令意指:對(duì)所有線程執(zhí)行k指令)。下圖中,當(dāng)前進(jìn)程共包含兩個(gè)線程,顯示了這兩個(gè)線程各自的棧信息:

      0:009> ~*k
      
         0  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+0xe
      
         1  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(執(zhí)行)的縮寫,后可跟一個(gè)或多個(gè)Windbg命令。它遍歷線程并對(duì)每個(gè)線程執(zhí)行指定命令,如:

      • ~*e  k;r

      此命令意為:在所用線程環(huán)境中(~*),分別執(zhí)行(e)棧指令(k)和寄存器指令(r)。

      線程時(shí)間

      在軟件調(diào)試的時(shí)候,若發(fā)現(xiàn)某線程占用執(zhí)行時(shí)間過多,就需要當(dāng)心是否有問題。線程執(zhí)行時(shí)間的多少,其實(shí)就是占用CPU執(zhí)行工作的時(shí)間多少。某線程占用越多,此長(zhǎng)彼消,則系統(tǒng)中其它線程占用CPU的時(shí)間就越少。

      線程的時(shí)間信息包括三個(gè)方面:自創(chuàng)建之初到現(xiàn)在的總消耗時(shí)間、用戶模式執(zhí)行時(shí)間、內(nèi)核模式執(zhí)行時(shí)間。需注意的是,消耗時(shí)間一定會(huì)遠(yuǎn)遠(yuǎn)大于用戶時(shí)間+內(nèi)核時(shí)間,多出來的是大量空閑時(shí)間(為Idle進(jìn)程占用)。使用下面的命令查看線程時(shí)間:

      • .ttime
      • !runaway 7

      在!runaway命令中加入標(biāo)志值7,將顯示線程的全部三種時(shí)間值。

      這兩個(gè)命令的區(qū)別之處是,.ttime只能顯示當(dāng)前線程的時(shí)間信息,!runaway能顯示當(dāng)前進(jìn)程的所有線程時(shí)間。下圖是這兩個(gè)命令的使用情況:

      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 7
       User Mode Time
        Thread       Time
         2:604       0 days 0:00:51.729
         4:600       0 days 0:00:47.159
         0:4f4       0 days 0:00:00.031
         3:61c       0 days 0:00:00.000
         1:5e0       0 days 0:00:00.000
       Kernel Mode Time
        Thread       Time
         4:600       0 days 0:13:45.073
         2:604       0 days 0:08:44.100
         3:61c       0 days 0:00:00.000
         1:5e0       0 days 0:00:00.000
         0:4f4       0 days 0:00:00.000
       Elapsed Time
        Thread       Time
         0:4f4       16 days 0:14:02.254
         4:600       16 days 0:14:02.207
         3:61c       16 days 0:14:02.207
         2:604       16 days 0:14:02.207
         1:5e0       16 days 0:14:02.207

                  對(duì)上二圖進(jìn)行對(duì)比,能看出兩個(gè)命令之間的功能差異。

      3.3 異常與事件

      在調(diào)試器語(yǔ)境中,事件是一個(gè)基本概念,Windbg是事件驅(qū)動(dòng)的。Windows操作系統(tǒng)的調(diào)試子系統(tǒng),是“事件”的發(fā)生源。調(diào)試器的所有操作,都是因事件而動(dòng),因事件被處理而中繼。Windows定義了9類調(diào)試事件,異常是其中一類(ID為1)。所以異常和事件,這二者是前者包含于后者的關(guān)系。

      系統(tǒng)對(duì)各種異常和調(diào)試事件進(jìn)行了分類,執(zhí)行sx命令可以列出針對(duì)當(dāng)前調(diào)試目標(biāo)的異?;蚍钱惓J录奶幚?。下面是一個(gè)片段:

      0:009> sx
        ct - Create thread - ignore
        et - Exit thread - ignore
       cpr - Create process - ignore
       epr - Exit process - break
        ld - Load module - break
             (only break for livekd.exe)
        ud - Unload module - ignore
       ser - System error - ignore
       ibp - Initial breakpoint - break
       iml - Initial module load - ignore
       out - Debuggee output - output
      
        av - Access violation - break - not handled
      asrt - Assertion failure - break - not handled
       aph - Application hang - break - not handled
       bpe - Break instruction exception - break
      bpec - Break instruction exception continue - handled
        eh - C++ EH exception - second-chance break - not handled
       clr - CLR exception - second-chance break - not handled
      clrn - CLR notification exception - second-chance break - handled
       cce - Control-Break exception - break
        cc - Control-Break exception continue - handled
       cce - Control-C exception - break
        cc - Control-C exception continue - handled
        dm - Data misaligned - break - not handled
      dbce - Debugger command exception - ignore - handled
        gp - Guard page violation - break - not handled
        ii - Illegal instruction - second-chance break - not handled
        ip - In-page I/O error - break - not handled
        dz - Integer divide-by-zero - break - not handled
       iov - Integer overflow - break - not handled
        ch - Invalid handle - break
        hc - Invalid handle continue - not handled
       lsq - Invalid lock sequence - break - not handled
       isc - Invalid system call - break - not handled
        3c - Port disconnected - second-chance break - not handled
       svh - Service hang - break - not handled
       sse - Single step exception - break
      ssec - Single step exception continue - handled
       sbo - Security check failure or stack buffer overrun - break - not handled
       sov - Stack overflow - break - not handled
        vs - Verifier stop - break - not handled
      vcpp - Visual C++ exception - ignore - handled
       wkd - Wake debugger - break - not handled
       rto - Windows Runtime Originate Error - second-chance break - not handled
       rtt - Windows Runtime Transform Error - second-chance break - not handled
       wob - WOW64 breakpoint - break - handled
       wos - WOW64 single step exception - break - handled
      
         * - Other exception - second-chance break - not handled

      可以看到這幾個(gè)調(diào)試事件,當(dāng)發(fā)生進(jìn)程退出(Exit Process)和初始化斷點(diǎn)(Initial breakpoint)事件的時(shí)候,調(diào)試器應(yīng)當(dāng)被中斷(Break)。模塊加載(Load Modual)以及有調(diào)試輸出(Debuggen Output)時(shí),需要輸出相關(guān)信息;其他的都被忽略掉,不做處理(Ignore)。     我們分析一下前兩個(gè)事件。使用調(diào)試器調(diào)試記事本進(jìn)程時(shí),不管是用.attach掛載方式還是.create創(chuàng)建方式,在調(diào)試器正式侵入記事本進(jìn)程前,都會(huì)有一個(gè)中斷(Initial breakpoint異常);調(diào)試開始后運(yùn)行一段時(shí)間,在外面將記事本關(guān)閉,又會(huì)發(fā)生一個(gè)中斷(Exit Process異常)。

      可以通過Debug|Event Filters…打開事件設(shè)置對(duì)話框。這個(gè)對(duì)話框中列出了全部調(diào)試事件,用戶可分別對(duì)它們進(jìn)行設(shè)置。

      sx

                  這個(gè)對(duì)話框列出了對(duì)于當(dāng)前調(diào)試會(huì)話可用的全部調(diào)試事件。針對(duì)每個(gè)調(diào)試事件,可設(shè)置其屬性。右列Execution和Continue兩組單選鍵,分別表示事件的中斷屬性中繼屬性。右列Argument按鈕可設(shè)置調(diào)試事件執(zhí)行參數(shù)(上圖中Load Module事件有一個(gè)Kernel32.dll參數(shù),即當(dāng)Kernel32.dll模塊被加載時(shí),調(diào)試器將被中斷),Commands按鈕可設(shè)置事件兩輪機(jī)會(huì)發(fā)生時(shí)的執(zhí)行命令。

      更細(xì)致的內(nèi)容,本章無(wú)力鋪陳,請(qǐng)讀者參閱《Windows 高級(jí)調(diào)試》(Mario & Daniel 2009 機(jī)械工業(yè)出版社)第三章,及《軟件調(diào)試》(張銀奎 2008 電子工業(yè)出版社)第9、30章相關(guān)內(nèi)容。 

      sxr:

            此命令將當(dāng)前所有對(duì)調(diào)試事件的設(shè)置,恢復(fù)到調(diào)試器的默認(rèn)設(shè)置。最后一個(gè)字母r表示Reset。

      sx{e|d|n|i}

            這4個(gè)命令分別代表了圖8-38中Execution組(中斷屬性)中的四個(gè)按鈕,即Enable、Disable、Output、Ignore。Enable是開啟中斷,Disable是禁止事件中斷(但對(duì)于異常,只禁止第一輪機(jī)會(huì),第二輪機(jī)會(huì)到來時(shí)仍會(huì)中斷到調(diào)試器),Output是禁止中斷但會(huì)輸出相關(guān)信息,Ignore表示完全忽略這個(gè)事件(對(duì)于異常,Output和Ignore兩選項(xiàng)使得兩輪機(jī)會(huì)都不會(huì)中斷到調(diào)試器)。

      sx{e|d|n|i} -h

            上述命令如果帶上-h選項(xiàng),就不是設(shè)置中斷屬性,而是設(shè)置中繼屬性了。對(duì)應(yīng)了圖8-38中的Continue組。其中sxe –h表示Handled,se{d|n|i} –h都表示Not Handled。

            下面繼續(xù)介紹異常、事件相關(guān)的其他調(diào)試命令:

      .lastevent:

      顯示最近發(fā)生的一個(gè)調(diào)試事件,往往是導(dǎo)致中斷發(fā)生的那個(gè)。下圖顯示的是一個(gè)很典型的初始化斷點(diǎn)引發(fā)的中斷事件。

      .exr:

            此命令顯示一個(gè)異常記錄的詳細(xì)內(nèi)容,傳入一個(gè)異常記錄地址:

      • .exr 記錄地址

      如果僅僅為了顯示最近的一條異常記錄,可以用-1代替異常記錄地址:

      • .exr -1

      由于異常是事件的一種,所以使用.exr -1命令得到的異常,可能和使用.lastevent命令獲取的事件(其實(shí)是異常),是同一個(gè)。但二者顯示的信息各有側(cè)重點(diǎn)。請(qǐng)對(duì)照?qǐng)D8-39看下面,同樣的初始化斷點(diǎn)異常,使用.exr命令時(shí)所顯示的信息:

      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)

      還有一個(gè)類似的命令:!cppexr,他分析并顯示一個(gè)C++異常信息。

      .bugcheck

      此命令不帶參數(shù)。在內(nèi)核環(huán)境下,顯示當(dāng)前bug check的詳細(xì)信息;可用于活動(dòng)調(diào)試或者crash dump調(diào)試環(huán)境中。用戶環(huán)境不可用。見下圖:

      0:009> .exr -1
      ExceptionAddress: 0000000077090530 (ntdll!DbgBreakPoint)
         ExceptionCode: 80000003 (Break instruction exception)
        ExceptionFlags: 00000000
      NumberParameters: 1
         Parameter[0]: 0000000000000000 

      !analyze:  

      此命令分析當(dāng)前最近的異常事件(如果在進(jìn)行dump分析,則是bug check),并顯示分析結(jié)果。這個(gè)異常事件,就是上面.lastevent命令對(duì)應(yīng)的事件。

      • -v:顯示異常的詳細(xì)信息,這個(gè)選項(xiàng)在調(diào)試錯(cuò)誤的時(shí)候,最有用。
      • -f:f是force的縮寫。強(qiáng)制將任何事件都當(dāng)作異常來分析,即使僅僅是普通的斷點(diǎn)事件。將因此多輸出一些內(nèi)容。
      • -hang:這個(gè)選項(xiàng)很有用,對(duì)于遇到死鎖的情況,它會(huì)分析原因。在內(nèi)核環(huán)境中,它分析內(nèi)核鎖和DPC棧;在用戶環(huán)境中,它分析線程的調(diào)用棧。用戶環(huán)境中,調(diào)試器只會(huì)對(duì)當(dāng)前線程進(jìn)行分析,所以一定要將線程環(huán)境切換到最可能引起問題的那個(gè)線程中去,才有幫助。這個(gè)參數(shù)非常有用,當(dāng)真的遇到死鎖時(shí),它可以救命(另一個(gè)分析死鎖的有效命令是!locks)。
      •  -show bug-check-代碼 [參數(shù)]:在內(nèi)核環(huán)境下,顯示指定的bug check的詳細(xì)信息。

      !error:

      此命令和VC里面內(nèi)置的errlook工具類似(請(qǐng)有興趣的讀者使用作者編寫的免費(fèi)軟件e-look,它比errlook功能更好且易于使用)。用來根據(jù)錯(cuò)誤碼,查看對(duì)應(yīng)的可讀錯(cuò)誤信息。微軟系統(tǒng)中常用的全局錯(cuò)誤碼有兩套,一套是Win32錯(cuò)誤碼,通過函數(shù)GetLastError()獲得的值;另一套是NTSTATUS值。!error命令對(duì)這二者都能支持。區(qū)別的方法,若錯(cuò)誤碼后面無(wú)參數(shù)1,則為win32錯(cuò)誤碼;否則就是NTSTATUS錯(cuò)誤碼。

                  比如,獲取錯(cuò)誤碼為2的Win32錯(cuò)誤信息,可用:!error 2

                  獲取錯(cuò)誤碼為2的NTSTATUS錯(cuò)誤信息,可用:!error 2 1 

      !gle:

      此命令是Get Last Error的縮寫。它調(diào)用Win32接口函數(shù)GetLastError()取得線程的錯(cuò)誤值,并打印分析結(jié)果。如果帶有-all選項(xiàng),則針對(duì)當(dāng)前進(jìn)程的所有線程(內(nèi)核環(huán)境下為所有用戶線程)執(zhí)行GetLastError()操作;否則僅針對(duì)當(dāng)前線程。

      gh/gn這兩個(gè)命令是g命令的擴(kuò)展。

                  gh是go with Exception handled的縮寫,意思是:把異常標(biāo)識(shí)為已處理而并繼續(xù)執(zhí)行程序;注意這里面的措辭,僅僅把異?!皹?biāo)識(shí)為”已處理,而并非真的被處理了。gh的作用在于,當(dāng)遇到某個(gè)可以忽略的非致命異常時(shí),將它先放過一邊,而繼續(xù)執(zhí)行程序。

                  而gn是go with Exception not handled的縮寫,意思是,對(duì)異常不進(jìn)行任何處理,而繼續(xù)執(zhí)行程序。這時(shí)候,程序自己的異常處理模塊將有機(jī)會(huì)處理異常。

      3.4 局部變量

      有兩個(gè)命令可以打印當(dāng)前的局部變量列表:x 和dv。x命令前文已經(jīng)講過。dv是Display local Variable的縮寫。下面是對(duì)一段簡(jiǎn)單的Win32控制臺(tái)代碼獲取其局部變量的情況:

      local-variable

                  上圖無(wú)法體現(xiàn)dv比x命令在顯示局部變量上的高明之處。dv命令有幾個(gè)開關(guān)選項(xiàng),介紹如下:

      •             /v:顯示虛擬地址(virtual);
      •             /i:顯示變量詳細(xì)信息(information),包括局部變量、全局變量、形參、函數(shù)變量等。
      •             /t:顯示變量類型(type),如int、char等等。
      •             /f:可指定進(jìn)行分析的函數(shù),需指定函數(shù)名。

               命令中選項(xiàng)/f wmain是指針對(duì)wmain函數(shù)(即_tmain)分析其局部變量??吹谝粋€(gè)變量argc,“prv param”對(duì)應(yīng)/i開關(guān)選項(xiàng);“@ebp+0×08”對(duì)應(yīng)/v開關(guān)選項(xiàng);“int”對(duì)應(yīng)/t開關(guān)選項(xiàng)。

      3.5 顯示類型

                  利用dt命令可以查看結(jié)構(gòu)體的類型定義。命令dt是Display Type的縮寫。當(dāng)我們要查看一些內(nèi)核結(jié)構(gòu)體的定義時(shí),dt命令是最直接有效的手段。

      4. 斷點(diǎn)

      4.1 軟件斷點(diǎn)

      軟件斷點(diǎn)的本質(zhì)是代碼改寫,即:將INT 3(代碼為0xCC)指令替換到斷點(diǎn)所在指令處(第一個(gè)字節(jié)),并保存被替換掉的代碼(即一個(gè)字節(jié)內(nèi)容)。等執(zhí)行到斷點(diǎn)處時(shí),調(diào)試器將因斷點(diǎn)而中斷,并將被替換的一字節(jié)內(nèi)容恢復(fù)到原內(nèi)存中。其原理和代碼補(bǔ)丁是一樣的。

      源碼或匯編模式下,最簡(jiǎn)單的斷點(diǎn)設(shè)置方式,是定位到正確的代碼處,并按下F9鍵。此外還有三種設(shè)置軟件斷點(diǎn)的指令,分別講解如下:

      bp

      命令bp是BreakPoint的縮寫。其指令格式如下:

      bp[ID] [Options] [Address [Passes]] ["CommandString"]

      參數(shù)Address表示需設(shè)置斷點(diǎn)的地址,缺省情況下使用當(dāng)前指令指針(EIP)的地址。ID是斷點(diǎn)號(hào),一般不手動(dòng)設(shè)置,由調(diào)試器內(nèi)部編號(hào)。Passes是一個(gè)整數(shù)值,即第幾次經(jīng)過斷點(diǎn)所在代碼時(shí),調(diào)試器才需要中斷執(zhí)行,默認(rèn)為1,即每次都中斷。CommandString用來設(shè)置一組命令,當(dāng)斷點(diǎn)發(fā)生的時(shí)候,就執(zhí)行這一組命令,比如可以把它設(shè)置為“k”,這樣斷點(diǎn)處就會(huì)輸出當(dāng)前的調(diào)用棧。

      Options是一組可選開關(guān)項(xiàng),有下面幾種:

      /1:即阿拉伯?dāng)?shù)字1。這個(gè)選項(xiàng)表明這個(gè)被設(shè)置的斷點(diǎn)只會(huì)在第一次有效,此后斷點(diǎn)信息即被刪除。

      /p:這個(gè)開關(guān)項(xiàng)后跟一個(gè)EPCOESS結(jié)構(gòu)體指針,只能用在內(nèi)核調(diào)試環(huán)境下。內(nèi)核調(diào)試環(huán)境下,如果要把斷點(diǎn)設(shè)置到用戶程序地址(即用戶空間地址),需要使用這個(gè)開關(guān),因?yàn)橛脩舻刂肥沁M(jìn)程相關(guān)的。

      /t:這個(gè)開關(guān)項(xiàng)后跟一個(gè)ETHREAD結(jié)構(gòu)體指針,只能用在內(nèi)核調(diào)試環(huán)境下。此開關(guān)項(xiàng)與/p起到類似的作用,只不過前者定位到進(jìn)程,后者更進(jìn)一步定位到線程。

      /c與/C:c或者C代表CallStack(調(diào)用棧)。這兩個(gè)開關(guān)項(xiàng)和調(diào)用棧深度有關(guān),都后跟一個(gè)整數(shù)值。前者表示調(diào)用棧深度如果小于這個(gè)整數(shù)值,斷點(diǎn)才會(huì)引發(fā)中斷,后者表示調(diào)用棧深度如果大于這個(gè)整數(shù)值,斷點(diǎn)才會(huì)引發(fā)中斷。

      bu

      此命令格式與bp類似,u代表了Unresolved。使用此命令設(shè)置的斷點(diǎn)雖登記到調(diào)試器,但它具體對(duì)應(yīng)到哪處代碼或指令,尚未確定。

      比如某EXE程序使用動(dòng)態(tài)加載的方式加載DLL(使用函數(shù)LoadLibrary()),那么當(dāng)DLL尚未加載時(shí),就可用bu指令設(shè)置DLL中的代碼斷點(diǎn),等到DLL加載時(shí),調(diào)試器再正式落實(shí)此斷點(diǎn)。

      bm

      此命令用來批量設(shè)置代碼斷點(diǎn),它帶有一個(gè)通配符字符串,凡是符合通配符格式的地址都將被設(shè)置斷點(diǎn),如:

      • bm /a ntdll!NtCreate*File

      則諸如NtCreateFile\NtCreateMailslotFile\NtCreateNamedPipeFile等函數(shù)都將被設(shè)置斷點(diǎn)。

      4.2 硬件斷點(diǎn)

      硬件斷點(diǎn)的原理和軟件斷點(diǎn)完全不同,硬件斷點(diǎn)是通過CPU的斷點(diǎn)寄存器來實(shí)現(xiàn)的,亦即依靠硬件方式實(shí)現(xiàn)。由于CPU的調(diào)試寄存器數(shù)量是有限的,所以能設(shè)置的硬件斷點(diǎn)數(shù)量也是有限的。設(shè)置硬件斷點(diǎn)的命令是ba,a代表了Address。指令格式如下:

      ba[ID] Access Size [Options] [Address [Passes]] ["CommandString"]

      參數(shù)ID、Options、Passes及CommandString,含義與前文bp指令相同,此處不述。

      參數(shù)Address是內(nèi)存地址,有別于前文的指令地址,內(nèi)存地址既可以是指令地址,也可以是數(shù)據(jù)地址。缺省為當(dāng)前指令寄存器地址(EIP)。參數(shù)Size表示地址長(zhǎng)度,x86系統(tǒng)可選值為1、2、4,X64系統(tǒng)可選值為1、2、4、8。需要注意的是,Address地址必須對(duì)齊到Size,即Address值必須是Size的整數(shù)倍。參數(shù)Access是內(nèi)存訪問類型,有下面幾種:

      e:作為指令執(zhí)行;r:讀,或者寫;w:寫;i:執(zhí)行IN/OUT操作。     比如:

      • ba r4 @ebp-0×08

      地址@ebp-8一定是一個(gè)局部變量地址,所以當(dāng)CPU對(duì)這個(gè)局部變量執(zhí)行讀寫操作時(shí),將引發(fā)硬件中斷。

      4.3 其他操作

                  其他的斷點(diǎn)操作包括:顯示斷點(diǎn)列表、禁止或恢復(fù)斷點(diǎn)、刪除斷點(diǎn)等。

      • bl:列出所有斷點(diǎn)
      • bd:禁止斷點(diǎn),d代表Disable。如bd 1,禁止斷點(diǎn)1。斷點(diǎn)被禁止后將不起作用,但亦未刪除。
      • be:恢復(fù)斷點(diǎn),e代表Enable?;謴?fù)被禁止的斷點(diǎn)。如be 1恢復(fù)1號(hào)斷點(diǎn)。
      • bc:清除斷點(diǎn),如:bc 1,清除斷點(diǎn)1;bc *,清除全部斷點(diǎn)。
      • br:序號(hào)管理,r代表ReNumber,即重新排序。如:br 2 0,將2號(hào)斷點(diǎn)重設(shè)為0號(hào)斷點(diǎn)。

      5. 內(nèi)存命令

      這一節(jié)里面,我們學(xué)習(xí)如何查看內(nèi)存信息。內(nèi)存是存儲(chǔ)數(shù)據(jù)、代碼的地方,通過內(nèi)存查看命令可以分析很多問題。相關(guān)命令可以分為:內(nèi)存查看命令和內(nèi)存統(tǒng)計(jì)命令。內(nèi)存統(tǒng)計(jì)命令用來分析內(nèi)存的使用狀況。

      5.1 查看內(nèi)存

                  有非常豐富的內(nèi)存查看命令,它們被統(tǒng)一為d*格式,如下所示:

      • d[類型]  [地址范圍]

                  d代表Display,類型包括:字符、字符串、雙字等。具體來說,d*命令共有這幾種:d、 da、db、dc、dd、dD、df、dp、dq、du、dw、dW、dyb、dyd、ds、dS。解釋如下:

      內(nèi)存類型 

      基本類型

      • dw = 雙字節(jié)WORD格式;
      • dd = 4字節(jié)DWORD格式 ;
      • dq = 8字節(jié)格式;
      • df = 4字節(jié)單精度浮點(diǎn)數(shù)格式;
      • dD =8字節(jié)雙精度浮點(diǎn)數(shù)格式;
      • dp = 指針大小格式,32位系統(tǒng)下4字節(jié),64位系統(tǒng)下為8字節(jié)。

      基本字符串

      • da = ASCII字符串格式;
      • du = UNICODE字符串格式;
      • db =字節(jié) + ASCII字符串;
      • dW = 雙字節(jié)WORD + ASCII字符串;
      • dc = 4字節(jié)DWORD + ASCII字符串。

      高級(jí)字符串:

      • ds = ANSI_STRING類型字符串格式;
      • dS = UNICODE_STRING類型字符串格式。

      二進(jìn)制 + 基本類型

      • byb = 二進(jìn)制 + 字節(jié);
      • byd = 二進(jìn)制 + DWORD值

      如果讀者對(duì)此感覺不明白,特別是組合模式究竟是何種情形?看下例應(yīng)能清楚。下例將同一個(gè)ASCII字符串,分別以ASCII字符串、Unicode字符串以及組合模式等共五種方式顯示:

      read-mem

      此系列命令還有一些可加利用的開關(guān)選項(xiàng),介紹如下:

      /c 列數(shù):指定列數(shù)。默認(rèn)情況下,列數(shù) 等于16除以列長(zhǎng),如dd命令的默認(rèn)列數(shù)即為4列(=16/4)。例:

      • dd  /c  8

      此命令每列顯示8個(gè)DWORD數(shù),即32字節(jié)內(nèi)容。

      /p:此選項(xiàng)用來顯示物理內(nèi)存信息,只能用于內(nèi)核模式中。不使用此命令時(shí),都將顯示虛擬內(nèi)存信息。如:

      • d  /p  [地址范圍]

      L 長(zhǎng)度: 默認(rèn)情況下,d命令只顯示固定長(zhǎng)度的內(nèi)存,一般為128或64字節(jié)。L可指定長(zhǎng)度,如下面的命令將顯示地址0×80000000開始處的0×100個(gè)字節(jié)內(nèi)容:

      • db  0×80000000  L100 

      數(shù)組形式內(nèi)存

      難能可貴的是,d*命令還能夠以數(shù)組形式顯示一段內(nèi)存信息,包括:dda, ddp、 ddu、dds、dpa、dpp、dpu、dps、dqa、dqp、dqu、dqs。

      何謂“以數(shù)組形式顯示”呢?這一組命令能夠?qū)⒅付ǖ刂诽幍膬?nèi)容,作為一系列指針,進(jìn)而顯示指針?biāo)柑巸?nèi)容。聽上去有點(diǎn)拗口吧,讀者這樣想會(huì)清楚些:前一組命令顯示address值,本節(jié)這一組命令顯示*address值。

      程序代碼中如有類似“char *array[10]”的數(shù)組變量,可使用這些命令顯示數(shù)組內(nèi)容。下面會(huì)有例子。這一系列命令實(shí)則由第一組命令演化而來,可分為三組:

      • 4字節(jié)DWORD為單位的dd*系列數(shù)組指令;
      • 指針長(zhǎng)度為單位的dp*系列數(shù)組指令;
      • 8字節(jié)為單位的dq*系列數(shù)組指令。

      查看鏈表內(nèi)存

                  最后,d*命令的另一個(gè)變體是以鏈表形式顯示內(nèi)存內(nèi)容。命令如下:

      • dl  開始地址

      默認(rèn)情況下,以正向從頭到尾遍歷鏈表;也可反向(由尾向頭)遍歷,指定b開關(guān)選項(xiàng):

      • dl  b  尾地址

      應(yīng)注意的是,命令dl是Display List的縮寫,這里的鏈表不能是用戶自定義的鏈表,而專指符合LIST_ENTRY或SINGLE_LIST_ENTRY格式的鏈表。

      5.2 內(nèi)存信息

      系統(tǒng)的內(nèi)核空間很大的,想知道這么廣大的內(nèi)存空間里面都有些什么東西嗎?想要知道一個(gè)內(nèi)存地址,到底是被一個(gè)內(nèi)核棧使用著,亦或被堆管理器使用著嗎?我們這一節(jié)就領(lǐng)大家看看內(nèi)存的地理概況。首先看Address命令:

      • !address  [地址]

                  顯示進(jìn)程或系統(tǒng)的內(nèi)存狀態(tài)、信息,!address是最好的工具。不加任何參數(shù),在用戶模式下此命令將以內(nèi)存塊為單位,列出從地址0開始到0×80000000(略小于)的全部地址空間信息;內(nèi)核模式下,將列出從地址0×80000000開始到0xFFFFFFFF(略小于)的全部地址空間信息;如指定地址值,則將顯示此地址所在內(nèi)存塊的內(nèi)存信息(此命令在Vista以后系統(tǒng)中,不能在內(nèi)核模式下正常使用,此Bug應(yīng)會(huì)在Windbg的以后版本中被修正)。下面分別截取了用戶與內(nèi)核空間中的內(nèi)存信息片段:

      0:009> !address                            
      
              BaseAddress      EndAddress+1        RegionSize     Type       State                 Protect             Usage
      ------------------------------------------------------------------------------------------------------------------------
      +        0`00000000        0`00010000        0`00010000             MEM_FREE    PAGE_NOACCESS                      Free       
      +        0`00010000        0`00020000        0`00010000 MEM_MAPPED  MEM_COMMIT  PAGE_READWRITE                     Heap64     [ID: 1; Handle: 0000000000010000; Type: Segment]
      +        0`00020000        0`00021000        0`00001000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                       
      +        0`00021000        0`00030000        0`0000f000             MEM_FREE    PAGE_NOACCESS                      Free       
      +        0`00030000        0`00031000        0`00001000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                       
      // 省略很多...

                  上圖截取了兩段用戶區(qū)內(nèi)存,第一段從0開始,長(zhǎng)度0×10000,狀態(tài)為釋放(FREE),表明這一段地址空間不可用;第二段從0×10000開始,長(zhǎng)度0×1000,屬于私有內(nèi)存,狀態(tài)為已提交,保護(hù)模式為可讀寫,此內(nèi)存塊被用于環(huán)境塊。

      下面給大家解釋一下內(nèi)存塊中的幾個(gè)值:

      內(nèi)存類型:即Type值,共有四種:第一種是什么都不是,即尚未被使用的;第二種是MEM_IMAGE,即地址映射于一個(gè)可執(zhí)行鏡像文件片段,如DLL文件;第三種是MEM_ MAPPED,即地址映射于不可執(zhí)行的鏡像文件片段,如頁(yè)文件;第四種是MEM_PRIVATE,即私有有內(nèi)存,這里的私有是針對(duì)進(jìn)程而言的,私有內(nèi)存無(wú)法在多個(gè)進(jìn)程間共享;

      保護(hù)模式:即Protect值,上例中見識(shí)了兩種保護(hù)模式,NOACCESS和READWRITE。從字面即很容易理解其意思,前者是不能做任何訪問的,因?yàn)榭臻e內(nèi)存是無(wú)效內(nèi)存;后者則可讀可寫,但不能執(zhí)行,說明是保存數(shù)據(jù)的地方。所有可用的保護(hù)包括:PAGE_NOACCESS(不可訪問),PAGE_READONLY(只讀),PAGE_READWRITE(讀寫),PAGE_EXECUTE(可執(zhí)行), PAGE_EXECUTE_READ(執(zhí)行并可讀),PAGE_EXECUTE_READWRITE(執(zhí)行并可讀寫),PAGE_WRITECOPY(寫時(shí)拷貝),PAGE_EXECUTE_WRITECOPY(執(zhí)行,并寫時(shí)拷貝), PAGE_GUARD(保護(hù))。

      內(nèi)存狀態(tài):即State值,共三種:MEM_FREE,即空閑內(nèi)存;MEM_RESERVED,即保留內(nèi)存,保留內(nèi)存尚不能被實(shí)際使用,但其地址空間已被預(yù)留,尚需一個(gè)提交動(dòng)作。最后是MEM_COMMIT,即內(nèi)存已被提交,正在被使用。

      內(nèi)存用途:即Usage值,有這樣一些值和用途。RegionUsageIsVAD:表示此地址區(qū)域已被分配;RegionUsageFree:代表此地址區(qū)域已被釋放,既沒有保留也沒有被提交,將來可以申請(qǐng)使用;RegionUsageImage:代表此地址區(qū)域被映射到二進(jìn)制文件的鏡像;Region UsageStack:代表此地址區(qū)域用于線程棧;RegionUsageTeb:代表此地址區(qū)域用于保存目標(biāo)進(jìn)程的所有線程的TEB結(jié)構(gòu);RegionUsageHeap:代表此地址區(qū)域用于堆內(nèi)存;RegionUsage Pdb:代表此地址區(qū)域用于保存目標(biāo)進(jìn)程的PEB結(jié)構(gòu);RegionUsageProcessParameters:代表此內(nèi)存塊用于保存目標(biāo)進(jìn)程的啟動(dòng)參數(shù);RegionUsageEnviromentBlock:代表此地址區(qū)域用于保存目標(biāo)進(jìn)程的環(huán)境塊。

      用戶環(huán)境下可使用下面的命令顯示內(nèi)存統(tǒng)計(jì)信息,包括內(nèi)存用途、內(nèi)存類型、內(nèi)存狀態(tài)。

      • !address  -summary
      0:009> !address  -summary
      
      --- Usage Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
      Free                                    101        0`7a5ba000 (   1.912 Gb)           95.60%
      Image                                   294        0`022b8000 (  34.719 Mb)  38.49%    1.70%
                                       7        0`0113a000 (  17.227 Mb)  19.10%    0.84%
      Stack32                                  51        0`01100000 (  17.000 Mb)  18.84%    0.83%
      Heap32                                   26        0`006e0000 (   6.875 Mb)   7.62%    0.34%
      MappedFile                               12        0`0069e000 (   6.617 Mb)   7.34%    0.32%
      Stack64                                  51        0`00440000 (   4.250 Mb)   4.71%    0.21%
      Other                                     8        0`001c1000 (   1.754 Mb)   1.94%    0.09%
      Heap64                                    9        0`00190000 (   1.563 Mb)   1.73%    0.08%
      TEB64                                    17        0`00022000 ( 136.000 kb)   0.15%    0.01%
      TEB32                                    17        0`00011000 (  68.000 kb)   0.07%    0.00%
      PEB64                                     1        0`00001000 (   4.000 kb)   0.00%    0.00%
      PEB32                                     1        0`00001000 (   4.000 kb)   0.00%    0.00%
      
      --- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal
      MEM_PRIVATE                             181        0`02f11000 (  47.066 Mb)  52.17%    2.30%
      MEM_IMAGE                               295        0`022b9000 (  34.723 Mb)  38.49%    1.70%
      MEM_MAPPED                               18        0`0086c000 (   8.422 Mb)   9.34%    0.41%
      
      --- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal
      MEM_FREE                                101        0`7a5ba000 (   1.912 Gb)           95.60%
      MEM_RESERVE                              94        0`02f5d000 (  47.363 Mb)  52.50%    2.31%
      MEM_COMMIT                              400        0`02ad9000 (  42.848 Mb)  47.50%    2.09%
      
      --- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal
      PAGE_EXECUTE_READ                        56        0`01414000 (  20.078 Mb)  22.26%    0.98%
      PAGE_READONLY                           129        0`0117a000 (  17.477 Mb)  19.37%    0.85%
      PAGE_READWRITE                          153        0`004b5000 (   4.707 Mb)   5.22%    0.23%
      PAGE_WRITECOPY                           26        0`0004c000 ( 304.000 kb)   0.33%    0.01%
      PAGE_READWRITE|PAGE_GUARD                34        0`00048000 ( 288.000 kb)   0.31%    0.01%
      PAGE_EXECUTE_READWRITE                    2        0`00002000 (   8.000 kb)   0.01%    0.00%
      
      --- Largest Region by Usage ----------- Base Address -------- Region Size ----------
      Free                                      0`030b0000        0`6cf40000 (   1.702 Gb)
      Image                                     0`75d71000        0`00879000 (   8.473 Mb)
                                       0`7f0e0000        0`00f00000 (  15.000 Mb)
      Stack32                                   0`00cd0000        0`000fd000 (1012.000 kb)
      Heap32                                    0`02f13000        0`0019d000 (   1.613 Mb)
      MappedFile                                0`01a90000        0`002cf000 (   2.809 Mb)
      Stack64                                   0`00160000        0`00039000 ( 228.000 kb)
      Other                                     0`006b0000        0`00181000 (   1.504 Mb)
      Heap64                                    0`02b90000        0`000bf000 ( 764.000 kb)
      TEB64                                     0`7ef76000        0`00002000 (   8.000 kb)
      TEB32                                     0`7ef78000        0`00001000 (   4.000 kb)
      PEB64                                     0`7efdf000        0`00001000 (   4.000 kb)
      PEB32                                     0`7efde000        0`00001000 (   4.000 kb)

      上圖分別以內(nèi)存使用、內(nèi)存類型、內(nèi)存狀態(tài)顯示用戶空間內(nèi)存統(tǒng)計(jì)信息。

      和!address命令類似的,用戶模式下還有下面兩個(gè)命令可用:

      • !vprot  [地址]
      • !vadump  [-v]

      命令!vprot顯示指定內(nèi)存塊的信息,側(cè)重于內(nèi)存保護(hù)信息;命令!vadump顯示整個(gè)內(nèi)存空間信息,dump者傾瀉也,開啟-v選項(xiàng)將顯示詳細(xì)(Verbose)信息。

      上面講過,用戶環(huán)境下使用“!address  –summary”可顯示用戶空間的內(nèi)存統(tǒng)計(jì)信息;現(xiàn)在再看兩個(gè)內(nèi)核命令,在內(nèi)核環(huán)境下顯示內(nèi)存的統(tǒng)計(jì)信息:

      • !memusage

      此命令從物理內(nèi)存角度顯示內(nèi)存統(tǒng)計(jì)信息。無(wú)數(shù)個(gè)頁(yè)表信息將被打印出來,可以說是“最內(nèi)存”的信息。此命令會(huì)查看所有的頁(yè)幀,所以運(yùn)行時(shí)會(huì)非常地耗時(shí)。

      • !vm

      此命令從虛擬內(nèi)存的角度顯示內(nèi)存統(tǒng)計(jì)信息,不僅能從全局角度顯示虛擬內(nèi)存的使用情況,還能以進(jìn)程為單位顯示內(nèi)存使用情況。

      5.3 其他命令

      內(nèi)核模式下,查看文件緩存信息,命令格式如下:

      • !filecache

                  此命令在用戶內(nèi)核模式下,顯示文件緩存和頁(yè)表狀態(tài)。每一行信息表示一個(gè)虛擬地址控制塊 (VACB)。虛擬地址控制塊可能對(duì)應(yīng)著一個(gè)命名文件,也可能對(duì)應(yīng)著一個(gè)元數(shù)據(jù)塊。如果對(duì)應(yīng)著一個(gè)命名文件,則此文件名稱將被顯示,否則顯示元數(shù)據(jù)名稱。

      實(shí)驗(yàn):查看文件緩存

      很多軟件都使用文件緩存的方式保存數(shù)據(jù),比如Office Word。直接查看WORD文檔,由于其
      內(nèi)部格式不透明,故而不便分析。但如果使用WORD打開一個(gè)txt文本文檔,它就會(huì)以文本文檔
      的方式來處理之,并且依舊使用文件緩存的方式。
      
      讀者用WORD打開一個(gè)TXT文檔(比如:測(cè)試.txt)。
      運(yùn)行內(nèi)核調(diào)試器并執(zhí)行!filecache命令,在打印信息中查找“測(cè)試.txt”。

      用戶模式下查看堆信息,命令格式如下:

      • !heap

      下面的清單顯示了某個(gè)進(jìn)程中共有4個(gè)堆:

      0:004> !heap -a
      Index   Address  Name      Debugging options enabled
        1:   00150000
          Segment at 00150000 to 00250000 (00031000 bytes committed)
      
        2:   00250000
          Segment at 00250000 to 00260000 (00006000 bytes committed)
      
        3:   00260000
          Segment at 00260000 to 00270000 (00003000 bytes committed)
      
        4:   00390000
          Segment at 00390000 to 003a0000 (00008000 bytes committed)
          Segment at 01370000 to 01470000 (0007b000 bytes committed)

                  堆資源是屬于進(jìn)程的,每個(gè)進(jìn)程都會(huì)創(chuàng)建若干個(gè)堆,如C運(yùn)行時(shí)堆、進(jìn)程默認(rèn)堆等。以第一個(gè)堆為例,地址范圍是[0x150000,0x250000],已經(jīng)有0×31000個(gè)字節(jié)被申請(qǐng)?zhí)峤弧?/p>

      6 小結(jié)

      本文到此結(jié)束。筆者并非調(diào)試方面的專家,斗膽寫了這許多內(nèi)容,深心惶恐。本章主要著重于基礎(chǔ)知識(shí),和基本指令的介紹,未免掛一漏萬(wàn),遺漏了許多有用指令。此外本書亦未能深入到調(diào)試原理、應(yīng)用技巧等高級(jí)主題,國(guó)內(nèi)著名的調(diào)試專家張銀奎老師所著的《軟件調(diào)試》(電子工業(yè)出版社 2008),是希望深入學(xué)習(xí)調(diào)試技術(shù)的讀者首選之作。

      注:本文是筆者寫作的《竹林蹊徑》一書第8章經(jīng)整理后所發(fā)表。

      14,553 total views, 31 views today

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多