內存是存儲數據、代碼的地方,通過內存查看命令可以分析很多問題。相關命令可以分為:內存查看命令和內存統計命令。內存統計命令用來分析內存的使用狀況。
一、查看內存
有非常豐富的內存查看命令,它們被容易為d*格式,如下所示:
- d[類型] [地址范圍]
d代表Display,類型包括:字符、字符串、雙字等。具體來說,d*命令共有這幾種:d、da、db、dc、dd、dD、df、dp、dq、du、dw、dW、dyb、dyd、ds、dS。
1、內存類型
基本類型:
- dw = 雙字節WORD格式;
- dd = 4字節DWORD格式 ;
- dq = 8字節格式;
- df = 4字節單精度浮點數格式;
- dD =8字節雙精度浮點數格式;
- dp = 指針大小格式,32位系統下4字節,64位系統下為8字節;
基本字符串:
- da = ASCII字符串格式;
- du = UNICODE字符串格式;
- db =字節 + ASCII字符串;
- dW = 雙字節WORD + ASCII字符串;
- dc = 4字節DWORD + ASCII字符串;
高級字符串:
- ds = ANSI_STRING類型字符串格式;
- dS = UNICODE_STRING類型字符串格式;
二進制 + 基本類型:
- byb = 二進制 + 字節;
- byd = 二進制 + DWORD值;
/c 列數:指定列數。默認情況下,列數 等於16除以列長,如dd命令的默認列數即為4列(=16/4)。例:
- dd /c 8
此命令每列顯示8個DWORD數,即32字節內容。
/p:此選項用來顯示物理內存信息,只能用於內核模式中。不使用此命令時,都將顯示虛擬內存信息。如:
- d /p [地址范圍]
L 長度: 默認情況下,d命令只顯示固定長度的內存,一般為128或64字節。L可指定長度,如下面的命令將顯示地址0×80000000開始處的0×100個字節內容:
- db 0×80000000 L100
2、數組形式內存
難能可貴的是,d*命令還能夠以數組形式顯示一段內存信息,包括:dda, ddp、 ddu、dds、dpa、dpp、dpu、dps、dqa、dqp、dqu、dqs。
何謂“以數組形式顯示”呢?這一組命令能夠將指定地址處的內容,作為一系列指針,進而顯示指針所指處內容。聽上去有點拗口吧,讀者這樣想會清楚些:前一組命令顯示address值,本節這一組命令顯示*address值。
程序代碼中如有類似“char *array[10]”的數組變量,可使用這些命令顯示數組內容。下面會有例子。這一系列命令實則由第一組命令演化而來,可分為三組:
- 4字節DWORD為單位的dd*系列數組指令;
- 指針長度為單位的dp*系列數組指令;
- 8字節為單位的dq*系列數組指令。
3、查看鏈表內存
最后,d*命令的另一個變體是以鏈表形式顯示內存內容。命令如下:
- dl 開始地址
默認情況下,以正向從頭到尾遍歷鏈表;也可反向(由尾向頭)遍歷,指定b開關選項:
- dl b 尾地址
應注意的是,命令dl是Display List的縮寫,這里的鏈表不能是用戶自定義的鏈表,而專指符合LIST_ENTRY或SINGLE_LIST_ENTRY格式的鏈表。
二、內存信息
系統的內核空間很大的,想知道這么廣大的內存空間里面都有些什么東西嗎?想要知道一個內存地址,到底是被一個內核棧使用着,亦或被堆管理器使用着嗎?我們這一節就領大家看看內存的地理概況。首先看Address命令:
- !address [地址]
顯示進程或系統的內存狀態、信息,!address是最好的工具。不加任何參數,在用戶模式下此命令將以內存塊為單位,列出從地址0開始到0×80000000(略小於)的全部地址空間信息;內核模式下,將列出從地址0×80000000開始到0xFFFFFFFF(略小於)的全部地址空間信息;如指定地址值,則將顯示此地址所在內存塊的內存信息(此命令在Vista以后系統中,不能在內核模式下正常使用,此Bug應會在Windbg的以后版本中被修正)。下面分別截取了用戶與內核空間中的內存信息片段:
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 // 省略很多...
上圖截取了兩段用戶區內存,第一段從0開始,長度0×10000,狀態為釋放(FREE),表明這一段地址空間不可用;第二段從0×10000開始,長度0×1000,屬於私有內存,狀態為已提交,保護模式為可讀寫,此內存塊被用於環境塊。
下面給大家解釋一下內存塊中的幾個值:
內存類型:即Type值,共有四種:第一種是什么都不是,即尚未被使用的;第二種是MEM_IMAGE,即地址映射於一個可執行鏡像文件片段,如DLL文件;第三種是MEM_ MAPPED,即地址映射於不可執行的鏡像文件片段,如頁文件;第四種是MEM_PRIVATE,即私有有內存,這里的私有是針對進程而言的,私有內存無法在多個進程間共享;
保護模式:即Protect值,上例中見識了兩種保護模式,NOACCESS和READWRITE。從字面即很容易理解其意思,前者是不能做任何訪問的,因為空閑內存是無效內存;后者則可讀可寫,但不能執行,說明是保存數據的地方。所有可用的保護包括:PAGE_NOACCESS(不可訪問),PAGE_READONLY(只讀),PAGE_READWRITE(讀寫),PAGE_EXECUTE(可執行), PAGE_EXECUTE_READ(執行並可讀),PAGE_EXECUTE_READWRITE(執行並可讀寫),PAGE_WRITECOPY(寫時拷貝),PAGE_EXECUTE_WRITECOPY(執行,並寫時拷貝), PAGE_GUARD(保護)。
內存狀態:即State值,共三種:MEM_FREE,即空閑內存;MEM_RESERVED,即保留內存,保留內存尚不能被實際使用,但其地址空間已被預留,尚需一個提交動作。最后是MEM_COMMIT,即內存已被提交,正在被使用。
內存用途:即Usage值,有這樣一些值和用途。RegionUsageIsVAD:表示此地址區域已被分配;RegionUsageFree:代表此地址區域已被釋放,既沒有保留也沒有被提交,將來可以申請使用;
- RegionUsageImage:代表此地址區域被映射到二進制文件的鏡像;Region UsageStack:代表此地址區域用於線程棧;RegionUsageTeb:代表此地址區域用於保存目標進程的所有線程的TEB結構;
- RegionUsageHeap:代表此地址區域用於堆內存;RegionUsage Pdb:代表此地址區域用於保存目標進程的PEB結構;RegionUsageProcessParameters:代表此內存塊用於保存目標進程的啟動參數;
- RegionUsageEnviromentBlock:代表此地址區域用於保存目標進程的環境塊。
用戶環境下可使用下面的命令顯示內存統計信息,包括內存用途、內存類型、內存狀態。
- !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)
上圖分別以內存使用、內存類型、內存狀態顯示用戶空間內存統計信息。
和!address命令類似的,用戶模式下還有下面兩個命令可用:
- !vprot [地址]
- !vadump [-v]
命令!vprot顯示指定內存塊的信息,側重於內存保護信息;命令!vadump顯示整個內存空間信息,dump者傾瀉也,開啟-v選項將顯示詳細(Verbose)信息。
上面講過,用戶環境下使用“!address –summary”可顯示用戶空間的內存統計信息;現在再看兩個內核命令,在內核環境下顯示內存的統計信息:
- !memusage
此命令從物理內存角度顯示內存統計信息。無數個頁表信息將被打印出來,可以說是“最內存”的信息。此命令會查看所有的頁幀,所以運行時會非常地耗時。
- !vm
此命令從虛擬內存的角度顯示內存統計信息,不僅能從全局角度顯示虛擬內存的使用情況,還能以進程為單位顯示內存使用情況。
三、其他命令
內核模式下,查看文件緩存信息,命令格式如下:
- !filecache
此命令在用戶內核模式下,顯示文件緩存和頁表狀態。每一行信息表示一個虛擬地址控制塊 (VACB)。虛擬地址控制塊可能對應着一個命名文件,也可能對應着一個元數據塊。如果對應着一個命名文件,則此文件名稱將被顯示,否則顯示元數據名稱。
實驗:查看文件緩存
很多軟件都使用文件緩存的方式保存數據,比如Office Word。直接查看WORD文檔,由於其
內部格式不透明,故而不便分析。但如果使用WORD打開一個txt文本文檔,它就會以文本文檔
的方式來處理之,並且依舊使用文件緩存的方式。
讀者用WORD打開一個TXT文檔(比如:測試.txt)。
運行內核調試器並執行!filecache命令,在打印信息中查找“測試.txt”。
用戶模式下查看堆信息,命令格式如下:
- !heap
下面的清單顯示了某個進程中共有4個堆:
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)
堆資源是屬於進程的,每個進程都會創建若干個堆,如C運行時堆、進程默認堆等。以第一個堆為例,地址范圍是[0x150000,0x250000],已經有0×31000個字節被申請提交。