1. 程式人生 > >Windows C++程式碼heap分析詳解

Windows C++程式碼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 handleheap address 一樣,而對於頁堆,其也有很大的區別。

另外值得一提的是!heap –p –a 這條神奇命令,別看這條命令帶-p 其實這條命令根本不需要開啟頁堆選項,只要有 +ust就行了

!heap -p -a [UserAddr....UserAddr+AlloSize]

正常情況下這條命令可以打相應的callstack.