• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Ay's Blog@CNSSUESTC

            WINDBG的堆調(diào)試—full page heap的堆破壞檢測原理

            @作者: ay @文章出處: cnss-ay的博客@Notice: 轉(zhuǎn)載請注明出處!若文章顯示不完整,可以到文章出處閱讀。

             

            此文會(huì)涉及到一些普通堆的知識,這些內(nèi)容可以參見我之前的文章 WINDBG的堆調(diào)試--了解HEAP組織

            堆破壞

            所謂的堆破壞,是說沒控制好自己的指針,把不屬于你分配的那塊內(nèi)存給寫覆蓋了。這塊內(nèi)存可能是你程序的數(shù)據(jù),也可能是堆的管理結(jié)構(gòu)。那么這個(gè)會(huì)導(dǎo)致怎樣的后果呢?可能的情況我們來yy下

            1. 把程序里的計(jì)算結(jié)果覆蓋了,這也許會(huì)讓你重復(fù)看了N次代碼,校驗(yàn)了N次計(jì)算邏輯也搞不明白為何計(jì)算結(jié)果還是有問題
            2. 堆管理結(jié)構(gòu)被破壞了,new/delete,或者malloc/free操作失敗
            3. 等等等等~

            堆破壞較為理想的情況是被修改的數(shù)據(jù)會(huì)馬上導(dǎo)致程序crash,最差的情況是你的堆數(shù)據(jù)莫名其妙在今天被改了,但明天才crash。這個(gè)時(shí)候在去分析crash,就如我們的警察叔叔現(xiàn)在接手一樁10年前的案子一般----無從下手。老外稱之為heap corruption是很貼切的,有時(shí)候咱堆數(shù)據(jù)被意外篡改是無聲無息的,你也許沒法從界面甚至日志文件中看到它被篡改的一點(diǎn)跡象,當(dāng)?shù)侥骋粋€(gè)時(shí)刻,這種錯(cuò)誤會(huì)暴露出來,然而這個(gè)時(shí)候查看堆信息也許會(huì)是毫無頭緒。所以對于堆破壞,咱的策略是盡早發(fā)現(xiàn)我們的堆被篡改了,最好能夠在堆數(shù)據(jù)被意外篡改的那一時(shí)刻誘發(fā)一個(gè)異常來提醒我們----兄弟,你的堆被腐蝕了。

            微軟提供了一些方案,來幫助我們診斷堆破壞。一般來說,堆破壞往往都是寫數(shù)據(jù)越界造成的(yy的第二種情況,如果是第一種情況其實(shí)還簡單,下個(gè)內(nèi)存斷點(diǎn)就好),所以微軟在堆分配上,給程序員門額外提供了2種堆分配模式--完全頁堆(full page heap),準(zhǔn)頁堆(normal page heap),用來檢測堆被寫越界的情況。

            完全頁堆(full page heap)

            檢測原理

            完全頁堆的檢測基本思路是通過分配相鄰的一個(gè)頁,并將其設(shè)為不可訪問屬性,然后用戶數(shù)據(jù)塊會(huì)被分配到內(nèi)存頁的最末端,從而實(shí)現(xiàn)越界訪問的檢測。當(dāng)我們對堆中分配的內(nèi)存讀寫越界后便會(huì)訪問到那個(gè)不可讀的頁,系統(tǒng)捕獲到改次異常后會(huì)試圖中斷執(zhí)行并將該異常上報(bào)給debugger,或者崩潰。具體的內(nèi)存組織結(jié)構(gòu)如下圖

            image

            摘自《軟件調(diào)試》

             

            與普通堆不同的是,內(nèi)存塊前面的HEAP_ENTRY結(jié)構(gòu)被DPH_BLOCK_INFORMATION結(jié)構(gòu)取代,這個(gè)結(jié)構(gòu)內(nèi)部記錄了頁堆模式下這個(gè)內(nèi)存塊的一些基本信息。如果用戶數(shù)據(jù)區(qū)前面的數(shù)據(jù),也就是DPH_BLOCK_INFORMATION結(jié)構(gòu)被破壞了,那么在釋放內(nèi)存塊的時(shí)候系統(tǒng)會(huì)報(bào)錯(cuò),如果編程者對這塊內(nèi)存塊讀寫越界了,當(dāng)然,這里越界有幾種情況:

            1. 讀越界,但只是訪問了塊尾填充部分?jǐn)?shù)據(jù),那么系統(tǒng)不會(huì)報(bào)錯(cuò)
            2. 寫越界,但只篡改了圖中塊尾填充的部分,那么在堆塊釋放的時(shí)候會(huì)報(bào)錯(cuò)
            3. 讀越界,且超過了塊尾填充的部分,訪問到了柵欄頁,那么系統(tǒng)會(huì)立即拋出一個(gè)異常并中斷執(zhí)行
            4. 寫越界,且超過了塊尾填充部分,寫到了柵欄頁,那么系統(tǒng)會(huì)立即拋出一個(gè)異常并中斷執(zhí)行

            這里需要注意的還是塊尾填充不一定存在,塊尾填充是因?yàn)橐獫M足堆內(nèi)存的最小分配粒度,如果本身內(nèi)存塊的分配粒度就已經(jīng)是最小分配粒度的倍數(shù)了,那么塊尾填充就不存在了,比如堆內(nèi)存分配粒度是是8 bytes,那么如果申請了14 bytes的話會(huì)有2 bytes的大徐小的塊尾填充塊,如果申請了24bytes,那么就沒有塊尾填充了,因?yàn)?4正好是8的倍數(shù)

             

            示例

            開啟全頁堆(用windbg目錄下的gflags或者裝一個(gè)appverifier都可以開啟),通過自己寫的一個(gè)heap.exe來看一下如何使用全頁堆檢測堆破壞情況heap.exe代碼如下:

            #include "windows.h"
            
            int main()
            {
            	HANDLE heap_handle = HeapCreate( NULL , 1024 , 0 ) ;
            	char *temp = NULL ;
            
            	char *buffer = (char*)HeapAlloc(heap_handle , NULL , 128) ;
            	char *buffer1 = (char*)HeapAlloc(heap_handle , NULL , 121) ;
            	temp = buffer ;
            
            	for( int i = 0 ; i < 138 ; ++i )
            	{
            			*(temp++) = 'a' ;
            	}
            
            	HeapFree(heap_handle, 0 , buffer ) ;
            	HeapFree(heap_handle, 0 , buffer1 ) ;
            	HeapDestroy( heap_handle) ;
            	return 0 ;
            }

            在第14行向buffer寫入138字節(jié),這顯然越界了,然后在用windbg啟動(dòng)heap.exe,直接運(yùn)行,會(huì)發(fā)現(xiàn)報(bào)錯(cuò)如下

            0:000> g
            (1f50.1f54): Access violation - code c0000005 (first chance)
            First chance exceptions are reported before any exception handling.
            This exception may be expected and handled.
            eax=00000080 ebx=00000000 ecx=02596000 edx=02596000 esi=00000001 edi=00193374
            eip=00191068 esp=0016fdc8 ebp=0016fddc iopl=0         nv up ei ng nz ac pe cy
            cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010297
            heap!main+0x68:
            00191068 c60161          mov     byte ptr [ecx],61h         ds:0023:02596000=??

            報(bào)了一個(gè)內(nèi)存訪問錯(cuò)誤,然后看一下調(diào)用堆棧

            0:000> kb
            ChildEBP RetAddr  Args to Child             
            0016fddc 0019120f 00000001 023fbfd0 0239df48 heap!main+0x68 [d:\projects\heap\main.cpp @ 14]
            0016fe20 765b1114 7ffd3000 0016fe6c 778eb429 heap!__tmainCRTStartup+0x10f [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 582]
            0016fe2c 778eb429 7ffd3000 757369d8 00000000 kernel32!BaseThreadInitThunk+0xe
            0016fe6c 778eb3fc 00191357 7ffd3000 00000000 ntdll!__RtlUserThreadStart+0x70
            0016fe84 00000000 00191357 7ffd3000 00000000 ntdll!_RtlUserThreadStart+0x1b

            可以看到是第14行報(bào)的錯(cuò),但是14行的代碼運(yùn)行了那么多次,我們再看一下這個(gè)時(shí)候變量i的值是多少

            0:000> dv i
                          i = 0n128

            顯然,在填充第128字節(jié)的時(shí)候,我們的temp指針訪問到了柵欄頁,從而報(bào)出了一個(gè)內(nèi)存違規(guī)的異常。

            這里順帶看一下如果我們分配的內(nèi)存不是8 bytes的情況(一般堆內(nèi)存分配粒度是8 bytes,所以申請128 bytes的內(nèi)存時(shí)是不會(huì)有塊尾填充部分的)

            那我們接下來看另外一段代碼

            我們把第10行的temp = buffer改成temp = buffer1

            因?yàn)閎uffer1申請了121 bytes,也就是說它有7 bytes的填充字節(jié)

            0:000> g
            (1ba0.1ba4): Access violation - code c0000005 (first chance)
            First chance exceptions are reported before any exception handling.
            This exception may be expected and handled.
            eax=00000080 ebx=00000000 ecx=024c8000 edx=024c8000 esi=00000001 edi=00033374
            eip=00031068 esp=002cfb80 ebp=002cfb94 iopl=0         nv up ei ng nz ac pe cy
            cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010297
            heap!main+0x68:
            00031068 c60161          mov     byte ptr [ecx],61h         ds:0023:024c8000=??
            0:000> dv i
                          i = 0n128

            可以看到變量i還是128,也就是說我們還是在訪問到第128字節(jié)后才引發(fā)訪問異常,而不是我們期望的121字節(jié)后就引發(fā)異常。

            這里也就是說如果我們的代碼中對申請的堆內(nèi)存寫越界了,寫數(shù)據(jù)覆蓋塊尾填充部分的時(shí)候并不會(huì)引發(fā)異常!

            但是,這并不代表我們的寫越界問題不會(huì)被發(fā)現(xiàn)。塊尾填充部分是會(huì)被填充上固定數(shù)據(jù)的,系統(tǒng)在適合的時(shí)機(jī)(比如銷毀堆的時(shí)候)會(huì)校驗(yàn)塊尾填充塊,如果發(fā)現(xiàn)塊尾填充塊數(shù)據(jù)有變,那么便會(huì)報(bào)一個(gè)verifier異常,比如我們把代碼中的for循環(huán)次數(shù)改為124

                for( int i = 0 ; i < 124 ; ++i )

            那么windbg會(huì)中斷在第19行

                HeapDestroy( heap_handle) ;

            提示內(nèi)容如下
            =======================================
            VERIFIER STOP 0000000F: pid 0x1E3C: Corrupted suffix pattern for heap block.

                025A1000 : Heap handle used in the call.
                025A7F80 : Heap block involved in the operation.
                00000079 : Size of the heap block.
                025A7FF9 : Corruption address.


            =======================================
            This verifier stop is not continuable. Process will be terminated
            when you use the `go' debugger command.

            =======================================

            (1e3c.143c): Break instruction exception - code 80000003 (first chance)
            eax=6c75e994 ebx=6c75cf58 ecx=00000002 edx=002bf461 esi=00000000 edi=000001ff
            eip=6c753c38 esp=002bf6b4 ebp=002bf8b8 iopl=0         nv up ei pl nz na po nc
            cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
            vrfcore!VerifierStopMessageEx+0x543:
            6c753c38 cc              int     3

            提示說的很清楚了,appverifier指出了堆和具體的內(nèi)存塊,我們這個(gè)時(shí)候查看buffer1的值是0x025a7f80 ,正好就是出問題的堆塊,出問題的地址是0x025a7ff79,正好就是buffer1內(nèi)存塊的邊界,錯(cuò)誤原因是Corrupted suffix pattern for heap block,也就是說咱塊尾填充部分(suffix pattern for heap block)被破壞(corrupted)了

            結(jié)論:只要寫越界,系統(tǒng)都能夠檢測出來,只不過如果寫越界寫到了柵欄頁會(huì)理解觸發(fā)異常中斷,而寫越界只寫了塊尾填充部分,那么系統(tǒng)在適當(dāng)時(shí)機(jī)(比如堆被銷毀,或者這塊內(nèi)存被重新分配等時(shí)機(jī))會(huì)對塊尾填充部分做完整性檢測,如果發(fā)現(xiàn)被破壞了,就會(huì)報(bào)錯(cuò)。當(dāng)然,你可以根據(jù)錯(cuò)誤號(藍(lán)色字體部分)信息去appverifier的幫助文檔中查找更詳細(xì)的錯(cuò)誤說明。

            結(jié)構(gòu)詳解

            這次咱來倒敘,先從最基本的內(nèi)存堆塊結(jié)構(gòu)DPH_BLOCK_INFORMATION開始介紹,DPH_BLOCK_INFORMATION結(jié)構(gòu)微軟也有對應(yīng)文檔介紹

            ms220938.Local_-1265171613_fphbs(en-US,VS.80).gif

            (摘自MSDN)

             

            其中prefix start magic和prefix end magic是校驗(yàn)塊,用來檢測DPH_BLOCK_INFORMATION是否被破壞,這些檢測部分屬于DPH_BLOCK_INFORMATION結(jié)構(gòu)。我們先來用windbg探究下DPH_BLOCK_INFORMATION這個(gè)最基本的結(jié)構(gòu).再一次,我們打開windbg調(diào)試heap.exe.運(yùn)行到第10行,這個(gè)時(shí)候變量的值是

            0:000> dv heap_handle
                heap_handle = 0x024a0000
            0:000> dv buffer
                     buffer = 0x024a5f80 "???"
            0:000> dv buffer1
                    buffer1 = 0x024a7f80 "???"

            這里可以看到一個(gè)很有趣的現(xiàn)象,buffer1和buffer的地址正好相差8K,也就是兩個(gè)頁的大小.這當(dāng)然是因?yàn)轫摱训脑蚶?其實(shí)這兩塊內(nèi)存分配是相鄰著的,虛擬內(nèi)存結(jié)構(gòu)如下圖所示

            buffer內(nèi)存塊(4K) 柵欄頁(4K) buffer1內(nèi)存塊(4K) 柵欄頁(4K)

             

            由于buffer和buffer1分配的大小是一樣的(buffer1加上尾部填充塊和buffer的大小相同),所以這兩塊內(nèi)存正好相差8K

            而DPH_BLOCK_INFORMATION就在我們申請的內(nèi)存塊指針的前0x20字節(jié)處,用dt命令看的結(jié)果如下:

            0:000> dt _DPH_BLOCK_INFORMATION 0x024a5f80-0x20
            verifier!_DPH_BLOCK_INFORMATION
               +0x000 StartStamp       : 0xabcdbbbb
               +0x004 Heap             : 0x024a1000 Void
               +0x008 RequestedSize    : 0x80
               +0x00c ActualSize       : 0x1000
               +0x010 Internal         : _DPH_BLOCK_INTERNAL_INFORMATION
               +0x018 StackTrace       : 0x003d9854 Void
               +0x01c EndStamp         : 0xdcbabbbb

             

            0x024a5f80-0x20就是DPH_BLOCK_INFORMATION結(jié)構(gòu)的地址。DPH_BLOCK_INFORMATION結(jié)構(gòu)在已分配和已釋放的狀態(tài)下,StartStamp和EndStamp(也就是MSDN圖中的prefix start magic和prefix end magic)是不同的,顯然dt輸出的結(jié)果看來,這個(gè)內(nèi)存塊是已分配狀態(tài)。StackTrace記錄了分配這個(gè)內(nèi)存塊時(shí)的調(diào)用棧,可以用dds來看一下這個(gè)內(nèi)存塊被分配時(shí)候的調(diào)用棧

            0:000> dds 0x003d9854
            003d9854  00000000
            003d9858  00004001
            003d985c  00090000
            003d9860  5b3b8e89 verifier!AVrfDebugPageHeapAllocate+0x229
            003d9864  776d5c4e ntdll!RtlDebugAllocateHeap+0x30
            003d9868  77697e5e ntdll!RtlpAllocateHeap+0xc4
            003d986c  776634df ntdll!RtlAllocateHeap+0x23a
            003d9870  003b1030 heap!main+0x30 [d:\projects\heap\main.cpp @ 8]
            003d9874  003b120c heap!__tmainCRTStartup+0x10f [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 582]
            003d9878  76451114 kernel32!BaseThreadInitThunk+0xe
            003d987c  7766b429 ntdll!__RtlUserThreadStart+0x70
            003d9880  7766b3fc ntdll!_RtlUserThreadStart+0x1b

            輸出結(jié)果我們可以看到這個(gè)內(nèi)存塊是在main.cpp,也就是我們的示例代碼的第8行分配的,第8行是char *buffer = (char*)HeapAlloc(heap_handle , NULL , 128) 正好就是分配buffer內(nèi)存的那條語句。這個(gè)結(jié)構(gòu)的其它字段,顧名思義,ActualSize指明了實(shí)際分配字節(jié)數(shù),0x1000 bytes也就是4K大小,Internal這個(gè)字段保存了個(gè)內(nèi)部結(jié)構(gòu),用windbg也看不出這個(gè)結(jié)構(gòu)信息。

            當(dāng)然為了防止內(nèi)存塊前面的數(shù)據(jù)被沖刷掉,除了DPH_BLOCK_INFORMATION外,系統(tǒng)還通過DPH_HEAP_BLOCK保存了所分配內(nèi)存塊的信息,

            通過!heap –p –h [address] 可以查看到頁堆的信息

            0:000> !heap -p -h 0x024a0000                            //heap_handle的值
                _DPH_HEAP_ROOT @ 24a1000
                Freed and decommitted blocks
                  DPH_HEAP_BLOCK : VirtAddr VirtSize
                Busy allocations
                  DPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize
                    024a1f6c : 024a5f80 00000080 - 024a5000 00002000
                    024a1f38 : 024a7f80 00000079 - 024a7000 00002000


            可以看到,buffer內(nèi)存塊對應(yīng)的DPH_HEAP_BLOCK結(jié)構(gòu)地址是024a1f6c

            0:000> dt _DPH_HEAP_BLOCK 024a1f6c
            verifier!_DPH_HEAP_BLOCK
               +0x000 NextFullPageHeapDelayedNode : 0x024a1020 _DPH_HEAP_BLOCK
               +0x004 DelayQueueEntry  : _DPH_DELAY_FREE_QUEUE_ENTRY
               +0x000 LookasideEntry   : _LIST_ENTRY [ 0x24a1020 - 0x0 ]
               +0x000 UnusedListEntry  : _LIST_ENTRY [ 0x24a1020 - 0x0 ]
               +0x000 VirtualListEntry : _LIST_ENTRY [ 0x24a1020 - 0x0 ]
               +0x000 FreeListEntry    : _LIST_ENTRY [ 0x24a1020 - 0x0 ]
               +0x000 TableLinks       : _RTL_BALANCED_LINKS
               +0x010 pUserAllocation  : 0x024a5f80  "???"
               +0x014 pVirtualBlock    : 0x024a5000  "???"
               +0x018 nVirtualBlockSize : 0x2000
               +0x01c Flags            : _DPH_HEAP_BLOCK_FLAGS
               +0x020 nUserRequestedSize : 0x80
               +0x024 AdjacencyEntry   : _LIST_ENTRY [ 0x24a1f5c - 0x24a1fc4 ]
               +0x02c ThreadId         : 0x3f4
               +0x030 StackTrace       : 0x003d9854 Void

            從dt的數(shù)據(jù)看來,這個(gè)結(jié)構(gòu)大小為0x34,buffer和buffer1的DPH_HEAP_BLOCK結(jié)構(gòu)首地址正好也是相差0x34,說明這兩個(gè)結(jié)構(gòu)是緊挨著的,下一步在讓我們來看看DPH_HEAP_BLOCK結(jié)構(gòu)是如何組織的。

            image

            摘自《軟件調(diào)試》

             

            這個(gè)是整個(gè)的頁堆結(jié)構(gòu)圖,我們先來說說DPH_HEAP_BLOCK的組織吧,在圖中0x16d00000是頁堆的首地址,也就是頁堆的句柄,我們調(diào)試器中,頁堆首地址則是0x024a0000,為了數(shù)據(jù)統(tǒng)一,我還是拿0x024a0000作為堆句柄來講解。我們的DPH_HEAP_BLOCK其實(shí)就在堆塊節(jié)點(diǎn)池里邊,我們可以近似把這個(gè)節(jié)點(diǎn)池看成一個(gè)大型的DPH_HEAP_BLOCK數(shù)組,但有個(gè)地方在軟件調(diào)試中沒有提到,就是在win7下,運(yùn)行時(shí)這些DPH_HEAP_BLOCK結(jié)構(gòu)都是以二叉平衡數(shù)的結(jié)構(gòu)來組織的,這個(gè)樹的結(jié)構(gòu)的入口正是在TableLinks字段內(nèi),這么做的原因也大概是因?yàn)槟軌蛟诜峙鋾r(shí)更快的索。我們再看看DPH_HEAP_ROOT結(jié)構(gòu),這個(gè)結(jié)構(gòu)儲(chǔ)存了整個(gè)頁堆的必要信息,它就相當(dāng)于普通堆的_HEAP結(jié)構(gòu)。

            0:000> dt _dph_heap_root 24a1000
            verifier!_DPH_HEAP_ROOT
               +0x000 Signature        : 0xffeeddcc
               +0x004 HeapFlags        : 0x1002
               +0x008 HeapCritSect     : 0x024a16cc _RTL_CRITICAL_SECTION
               +0x00c NodesCount       : 0x2c
               +0x010 VirtualStorageList : _LIST_ENTRY [ 0x24a1fa0 - 0x24a1fa0 ]
               +0x018 VirtualStorageCount : 1
               +0x01c PoolReservedLimit : 0x024a5000 Void
               +0x020 BusyNodesTable   : _RTL_AVL_TABLE
               +0x058 NodeToAllocate   : (null)
               +0x05c nBusyAllocations : 2
               +0x060 nBusyAllocationBytesCommitted : 0x4000
               +0x064 pFreeAllocationListHead : (null)
               +0x068 FullPageHeapDelayedListTail : (null)
               +0x06c DelayFreeQueueHead : (null)
               +0x070 DelayFreeQueueTail : (null)
               +0x074 DelayFreeCount   : 0
               +0x078 LookasideList    : _LIST_ENTRY [ 0x24a1078 - 0x24a1078 ]
               +0x080 LookasideCount   : 0
               +0x084 UnusedNodeList   : _LIST_ENTRY [ 0x24a1ed0 - 0x24a16e4 ]
               +0x08c UnusedNodeCount  : 0x28
               +0x090 nBusyAllocationBytesAccessible : 0x2000
               +0x094 GeneralizedFreeList : _LIST_ENTRY [ 0x24a1f04 - 0x24a1f04 ]
               +0x09c FreeCount        : 1
               +0x0a0 PoolCommitLimit  : 0x024a2000 Void
               +0x0a4 NextHeap         : _LIST_ENTRY [ 0x5b3e9a58 - 0x23a10a4 ]
               +0x0ac ExtraFlags       : 3
               +0x0b0 Seed             : 0xfed6f13a
               +0x0b4 NormalHeap       : 0x027d0000 Void
               +0x0b8 CreateStackTrace : 0x003d9824 _RTL_TRACE_BLOCK
               +0x0bc ThreadInHeap     : (null)
               +0x0c0 BusyListHead     : _LIST_ENTRY [ 0x24a10c0 - 0x24a10c0 ]
               +0x0c8 SpecializedFreeList : [64] _LIST_ENTRY [ 0x24a10c8 - 0x24a10c8 ]
               +0x2c8 DelayFreeListLookup : [257] (null)
               +0x6cc HeapCritSectionStorage : _RTL_CRITICAL_SECTION

            這里邊維護(hù)了很多運(yùn)行時(shí)信息,比如說DPH_BLOCK_INFORMATION中的那個(gè)二叉樹入口其實(shí)就是保存在BusyNodesTable 字段,這里面記錄了所有被分配了的內(nèi)存塊所對應(yīng)的DPH_BLOCK_INFORMATION。當(dāng)然,這里面一些信息軟件調(diào)試?yán)锩娑加薪榻B,很多看名字也能夠猜到大概意思,看名字猜不到啥意思的字段,其實(shí)我也猜不到。。。-_-|||在創(chuàng)建頁堆后,所有內(nèi)存分配都分配在頁堆中,通過分配的地址也能看得出來(我們分配的內(nèi)存都是024a打頭),而非普通頁堆中,普通頁堆也僅僅只是保存一些系統(tǒng)內(nèi)部使用的數(shù)據(jù)。一般來說,堆塊節(jié)點(diǎn)池加上DPH_HEAP_ROOT結(jié)構(gòu)大小正好是4個(gè)內(nèi)存頁,也就是16K。

            優(yōu)缺點(diǎn)

            缺點(diǎn):消耗大量虛擬內(nèi)存,每塊內(nèi)存的分配粒度是2個(gè)頁(8K),

            優(yōu)點(diǎn):能夠立即捕獲越界讀寫操作,通過調(diào)用棧就可以追溯到問題源頭。能夠快速定位問題代碼。

            使用建議:32位下不適宜跑配置文件結(jié)構(gòu)比較復(fù)雜的軟件,讓我們來假設(shè)一個(gè)xml配置文件下有3000個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)有5個(gè)字符串描述屬性,如果把這些配置文件信息轉(zhuǎn)化為stl結(jié)構(gòu)來保存,那么每個(gè)節(jié)點(diǎn)則需要為此分配5*8K的空間,3000項(xiàng)配置則需要3000*5*8K=117MB虛擬內(nèi)存,如果每個(gè)節(jié)點(diǎn)信息再多一些呢?這樣會(huì)導(dǎo)致虛擬內(nèi)存耗盡從而出現(xiàn)一系列內(nèi)存問題(比如,new失敗)。當(dāng)然64位就不存在這種問題了7T的虛擬內(nèi)存空間,現(xiàn)在看來應(yīng)該是夠用了。

             

            對于調(diào)試堆破壞來說,其實(shí)我們只要了解DPH_BLOCK_INFORMATION結(jié)構(gòu)和DPH_HEAP_BLOCK中的基本字段就差不多了,這樣更方便我們定位出錯(cuò)源頭。比如在appverifier報(bào)錯(cuò)后(或者你程序自己莫名其妙崩潰或者數(shù)據(jù)被篡改后,要知道appverifier并不總是可信的),我們可以自己手動(dòng)調(diào)試出錯(cuò)的堆塊結(jié)構(gòu)(DPH_BLOCK_INFORMATION,DPH_HEAP_BLOCK和DPH_HEAP_ROOT),檢測以下這些點(diǎn):

            1. 檢測堆塊管理結(jié)構(gòu)的校驗(yàn)字段是否完整
            2. 是否塊尾填充部分有被修改過
            3. 檢測到未釋放或者重復(fù)釋放堆資源時(shí),查看問題的堆塊被分配時(shí)的調(diào)用棧

            其實(shí)頁堆還好,它有較強(qiáng)的實(shí)時(shí)性,所以并不需要太多手工調(diào)試的操作,越界讀寫都會(huì)立即觸發(fā)異常并且中斷,所以從這點(diǎn)看來,它是一些軟件用來檢測堆資源是否正確使用的必備良藥~ 但是相對于頁堆,準(zhǔn)頁堆的調(diào)試則需要更好的去了解準(zhǔn)頁堆工作原理了,因?yàn)樗峁┑亩褖K檢測不是實(shí)時(shí)的,所以發(fā)現(xiàn)問題后,需要咱“精湛的調(diào)試內(nèi)功“去找出源頭,關(guān)于準(zhǔn)頁堆的東西,下回再說吧,敬請期待~

            posted on 2012-01-05 09:17 __ay 閱讀(10939) 評論(0)  編輯 收藏 引用 所屬分類: Debugging

            久久青青草原精品影院| 国产99久久久国产精品小说| 久久精品国产免费| 成人精品一区二区久久| 久久亚洲精品无码aⅴ大香| 看久久久久久a级毛片| 伊人久久综在合线亚洲2019| 污污内射久久一区二区欧美日韩 | 精品国产91久久久久久久a | 国产成人AV综合久久| 久久久久免费精品国产| 国产一区二区精品久久凹凸| 97精品国产97久久久久久免费 | 亚洲中文精品久久久久久不卡| 久久久精品2019免费观看| 久久这里只有精品视频99| 久久电影网2021| 成人综合伊人五月婷久久| 久久中文字幕人妻丝袜| 免费精品久久久久久中文字幕 | 色综合久久88色综合天天 | 国产美女久久精品香蕉69| 97久久国产综合精品女不卡| 激情久久久久久久久久| 久久精品免费一区二区三区| 精品久久人妻av中文字幕| 久久精品国产亚洲av高清漫画| 精品视频久久久久| 久久精品亚洲乱码伦伦中文| 久久99国产精品99久久| 国产婷婷成人久久Av免费高清| 久久九九兔免费精品6| 99久久国产精品免费一区二区| 久久狠狠爱亚洲综合影院| 久久国产亚洲精品| 国产午夜精品久久久久九九电影| 国产三级久久久精品麻豆三级| 韩国无遮挡三级久久| 青青青国产成人久久111网站| …久久精品99久久香蕉国产| 国产成人精品白浆久久69|