上次寫了一篇文章,Windows代碼heap內存分析實戰
由於時間的關系,寫的不是很詳細,於是有朋友建議寫的詳細些,於是有了本文。
Windows C++代碼heap分析詳解
Windows代碼占用的內存主要是堆和棧,其中棧內存又被稱為自動內存,一般為系統自動管理,所以常見的問題主要發生在堆內存上。系統中如果分配了堆內存而不釋放,或者錯誤釋放,都會產生問題。
首先來分析一下堆內存的主要結構:
對於普通的堆:
1. CreateHeap -> creates a _HEAP
2. AllocHeap -> creates a _HEAP_ENTRY
對於頁堆 (gflags.exe /i +hpa):
1. CreateHeap -> creates a _DPH_HEAP_ROOT (+ _HEAP + 2x _HEAP_ENTRY)
2. AllocHeap -> creates a _DPH_HEAP_BLOCK
也就是說對於頁堆,有額外的數據表征其內容,當讓,對於同樣堆分析命令,對於兩種堆也會輸出不同的內容。
對於普通的堆,分別執行的分析命令和結果如下:
0:001> !heap -s
NtGlobalFlag enables following debugging aids for new heaps:
validate parameters
stack back traces
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
00170000 58000062 1024 64 64 12 3 1 0 0 L
00270000 58001062 64 24 24 11 1 1 0 0 L
00280000 58008060 64 12 12 10 1 1 0 0
00370000 58001062 64 28 28 9 1 1 0 0 L
00390000 58001062 64 56 56 5 2 1 0 0
003a0000 58001062 1088 72 72 16 3 2 0 0 L
003f0000 58001062 64 20 20 5 2 1 0 0 L
01a20000 58001062 64 20 20 1 1 1 0 0 L
02090000 58001062 64 12 12 4 1 1 0 0 L
-----------------------------------------------------------------------------
0:001> !heap -p
Active GlobalFlag bits:
hpc - Enable heap parameter checking
ust - Create user mode stack trace database
StackTraceDataBase @ 00430000 of size 01000000 with 0000037f traces
active heaps:
- 170000
HEAP_GROWABLE HEAP_TAIL_CHECKING_ENABLED HEAP_FREE_CHECKING_ENABLED
- 270000
HEAP_GROWABLE HEAP_TAIL_CHECKING_ENABLED HEAP_FREE_CHECKING_ENABLED HEAP_CLASS_1
- 280000
HEAP_TAIL_CHECKING_ENABLED HEAP_FREE_CHECKING_ENABLED HEAP_CLASS_8
- 370000
HEAP_GROWABLE HEAP_TAIL_CHECKING_ENABLED HEAP_FREE_CHECKING_ENABLED HEAP_CLASS_1
- 390000
HEAP_GROWABLE HEAP_TAIL_CHECKING_ENABLED HEAP_FREE_CHECKING_ENABLED HEAP_CLASS_1
- 3a0000
HEAP_GROWABLE HEAP_TAIL_CHECKING_ENABLED HEAP_FREE_CHECKING_ENABLED HEAP_CLASS_1
- 3f0000
HEAP_GROWABLE HEAP_TAIL_CHECKING_ENABLED HEAP_FREE_CHECKING_ENABLED HEAP_CLASS_1
- 1a20000
HEAP_GROWABLE HEAP_TAIL_CHECKING_ENABLED HEAP_FREE_CHECKING_ENABLED HEAP_CLASS_1
- 2090000
HEAP_GROWABLE HEAP_TAIL_CHECKING_ENABLED HEAP_FREE_CHECKING_ENABLED HEAP_CLASS_1
0:001> !heap -stat
_HEAP 003a0000
Segments 00000002
Reserved bytes 00110000
Committed bytes 00012000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 00170000
Segments 00000001
Reserved bytes 00100000
Committed bytes 00010000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 00390000
Segments 00000001
Reserved bytes 00010000
Committed bytes 0000e000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 00370000
Segments 00000001
Reserved bytes 00010000
Committed bytes 00007000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 00270000
Segments 00000001
Reserved bytes 00010000
Committed bytes 00006000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 01a20000
Segments 00000001
Reserved bytes 00010000
Committed bytes 00005000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 003f0000
Segments 00000001
Reserved bytes 00010000
Committed bytes 00005000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 02090000
Segments 00000001
Reserved bytes 00010000
Committed bytes 00003000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
_HEAP 00280000
Segments 00000001
Reserved bytes 00010000
Committed bytes 00003000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
對於頁堆堆,分別執行的分析命令和結果如下:
0:001> !heap -s
Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast
(k) (k) (k) (k) length blocks cont. heap
-----------------------------------------------------------------------------
00270000 00000002 1024 12 12 4 1 1 0 0 L
00380000 00001002 64 24 24 16 1 1 0 0 L
00390000 00008000 64 12 12 10 1 1 0 0
003e0000 00001002 64 12 12 4 1 1 0 0 L
018e0000 00001002 64 12 12 4 1 1 0 0 L
01ad0000 00001002 64 32 32 24 1 1 0 0 L
02020000 00001002 64 12 12 4 1 1 0 0 L
02130000 00001002 64 12 12 4 1 1 0 0 L
02bd0000 00001002 64 12 12 4 1 1 0 0 L
02ce0000 00001002 64 12 12 4 1 1 0 0 L
-----------------------------------------------------------------------------
0:001> !heap -p
Active GlobalFlag bits:
hpa - Place heap allocations at ends of pages
StackTraceDataBase @ 00430000 of size 01000000 with 00000376 traces
PageHeap enabled with options:
ENABLE_PAGE_HEAP
COLLECT_STACK_TRACES
active heaps:
+ 170000
ENABLE_PAGE_HEAP COLLECT_STACK_TRACES
NormalHeap - 270000
HEAP_GROWABLE
ReadMemory error for address eeddccee
Use `!address eeddccee' to check validity of the address.
0:001> !heap -stat
ReadMemory error for address eeddccee
Use `!address eeddccee' to check validity of the address.
_HEAP 00170000
Segments 00000001
Reserved bytes 00100000
Committed bytes 00003000
VirtAllocBlocks 00000000
VirtAlloc bytes 00000000
從分析結果我們可以看出!heap -p 命令對於不同的堆輸出的內容是不同的,對於普通的堆,!heap –s 和!heap –p 產生相同的地址,而對於頁堆,他們的輸出就有了顯著的不同 。另外對於普通的堆,heap handle和heap address 一樣,而對於頁堆,其也有很大的區別。
另外值得一提的是!heap –p –a 這條神奇命令,別看這條命令帶-p 其實這條命令根本不需要開啟頁堆選項,只要有 +ust就行了
!heap -p -a [UserAddr....UserAddr+AlloSize]
正常情況下這條命令可以打相應的callstack.