堆介紹

一、前言

  堆對於開發者一般來說是熟悉又陌生的,熟悉是因爲我們常常使用new/delete或者malloc/free使用堆,陌生是因爲我們基本沒有去了解堆的結構。堆在什麼地方?怎麼申請?怎麼釋放?系統又是怎麼管理堆的呢?

  帶着疑問,這兩天看了<軟件漏洞分析技術>與<漏洞戰爭>中關於堆的說明,終於對於堆有一點點的瞭解了。這裏記錄一下在學習和調試中的一點筆記。

 

二、關於堆的基本知識

  1).首先了解空閒雙向鏈表和快速單向鏈表的概念

  1.空閒雙向鏈表(空表)

  空閒堆塊的塊首中包含一對重要的指針,這對指針用於將空閒堆塊組織成雙向鏈表。按照堆塊的大小不同,空表總共被分爲128條。

  堆區一開始的堆表區中有一個128項的指針數組,被稱作空表索引(Freelist array)。該數組的每一項包括兩個指針,用於標識一條空表。

  如圖所示,空表索引的第二項(free[1])標識了堆中所有大小爲8字節的空閒堆塊。之後每個索引項指示的空閒堆塊遞增8字節。例如free[2]爲16字節的空閒堆塊,free[3]爲24字節的空閒堆塊,free[127]爲1016字節的空閒堆塊。
        空閒堆塊的大小 = 索引項(ID) x 8(字節)
  把空閒堆塊按照大小的不同鏈入不同的空表,可以方便堆管理系統高效檢索指定大小的空閒堆塊。需要注意的是,空表索引的第一項(free[0])所標識的空表相對比較特殊。這條雙向鏈表鏈入了所有大於等於1024字節的堆塊(小於512KB),升序排列。

  

  2.快速單項鍊表(快表)

  快表是Windows用來加速堆塊分配而採用的一種堆表。這裏之所以叫做"快表"是因爲這類單項鍊表中從來不會發生堆塊合併(其中的空閒塊塊首被設置爲佔用態,用來防止堆塊合併)

  快表也有128條,組織結構與空表類似,只是其中的堆塊按照單項鍊表組織。

  快表總是被初始化爲空,而且每條快表最多隻有4個結點,故很快就會被填滿。

  

  2)堆塊的結構

  堆塊分爲塊首和塊身,實際上,我們使用函數申請得到的地址指針都會越過8字節(32位系統)的塊首,直接指向數據區(塊身)。堆塊的大小包括塊首在內的,如果申請32字節,實際會分配40字節,8字節的塊首+32字節的塊身。同時堆塊的單位是8字節,不足8字節按8字節分配。堆塊分爲佔用態和空閒態。

  其中空閒態結構爲:

  佔用態結構爲

  空閒態將塊首後8個字節用於存放空表指針了。

  在64位系統,塊首大小爲16字節,按16字節對齊。

  

  3)堆塊的分配和釋放

  1.堆塊分配

  堆塊分配可以分爲三類:快表分配、普通空表分配和零號空表(free[0]分配)。

  從快表中分配堆塊比較簡單,包括尋找到大小匹配的空閒堆塊、將其狀態修改爲佔用態、把它從堆表中"卸下"、最後返回一個指向堆塊快身的指針給程序使用。
  普通空表分配時首先尋找最優的空閒塊分配,若失敗,則尋找次優的空閒塊分配,即最小的能滿足要求的空閒塊。
  零號空表中按照大小升序鏈着大小不同的空閒塊,故在分配時先從free[0]反向查找最後一個塊(即最大塊),看能否滿足要求,如果滿足要求,再正向搜索最小能滿足要求的空閒堆塊進行分配。
  當空表中無法找到匹配的"最優"堆塊時,一個稍大些的塊會被用於分配,這種次優分配發生時,會先從大塊中按請求的大小精確地"割"出一塊進行分配,然後給剩下的部分重新標註塊首,鏈入空表。
  由於快表只有在精確匹配纔會分配,所以不存在上述現象。

  

  2.堆塊的釋放
  釋放堆塊的操作包括將堆塊狀態改爲空閒,鏈入相應的堆表。所有的釋放塊都鏈入堆表的末尾,分配的時候也先從堆表末尾拿。
  另外需要強調,快表最多隻有4項。

 

  3.堆塊的分配和釋放

  在具體進行堆塊分配和釋放時,根據操作內存大小不同,Windows採取的策略也會有所不同。可以把內存按照大小分爲三類:

      小塊:Size < 1KB
      大塊:1KB < Size < 512KB
      巨塊:Size >= 512KB

                            分配  釋放
 小塊  

   首先進行快表分配
   若快表分配失敗,進行普通空表分配
   若普通空表分配失敗,使用堆緩存分配
   若堆緩存分配失敗,嘗試零號空表分配(freelist[0])
   若零號空表分配失敗,進行內存緊縮後在嘗試分配
   若仍無法分配,返回NULL

 

優先鏈入快表(只能鏈入4個空閒塊)
如果快表滿,則將其鏈入相應的空表

 大塊  

   首先使用堆緩存進行分配
   若堆緩存分配失敗,使用free[0]中的大塊進行分配

 

優先將其放入堆緩存
若堆緩存滿,將鏈入freelist[0]

 巨塊  

   一般來說巨塊申請非常罕見,要用到虛分配方法(實際上並不是從堆區分配的)
   這種類型的堆塊在堆溢出利用中幾乎不會遇到

 直接釋放,沒有堆表操作

  

  在分配的過程中需要注意的幾點是:

  (1)快表中的空閒塊被設置爲佔用態,故不會發生堆塊合併操作,且只能精確匹配時纔會分配。

  (2)快表是單鏈表,操作比雙鏈表簡單,插入刪除都少用很多指令

  (3)快表只有4項,很容易被填滿,因此空表也是被頻繁使用的

 

 三、調試堆在PEB中的數據結構

  1)完成C代碼,在x64下編譯爲Release版本,運行

複製代碼
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;

extern "C" PVOID64 _cdecl GetPebx64();
int _tmain(int argc, _TCHAR* argv[])
{
    
    PVOID64 Peb = 0;
    Peb = GetPebx64();
    printf("Peb is 0x%p\r\n",Peb);

    HANDLE hHeap;
    char *heap;
    char str[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";  //0x20

    hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS,0x1000,0xffff);
    getchar();   //用於暫停,便於調試器附加

    heap = (char*)HeapAlloc(hHeap,0,0x20);
    printf("Heap addr:0x%08p\r\n",heap);

    strcpy(heap,str);
    printf("str is %s\r\n",heap);

    cin>>Peb;
    HeapFree(hHeap,0,heap);  //釋放
    HeapDestroy(hHeap);

    cin>>Peb;
    return 0;
}
複製代碼

  其中GetPebx64()函數爲使用.asm文件的彙編,通過gs:[0x60]獲得

.CODE
  GetPebx64 PROC 
    mov rax,gs:[60h]
  ret
  GetPebx64 ENDP
END

  運行結果爲

  我們使用Windbg附加,查看PEB結構

複製代碼
0:001> dt _PEB 0x000007FFFFFDB000
ntdll!_PEB
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0x1 ''
   +0x003 BitField         : 0x8 ''
   +0x003 ImageUsesLargePages : 0y0
   +0x003 IsProtectedProcess : 0y0
   +0x003 IsLegacyProcess  : 0y0
   +0x003 IsImageDynamicallyRelocated : 0y1
   +0x003 SkipPatchingUser32Forwarders : 0y0
   +0x003 SpareBits        : 0y000
   +0x008 Mutant           : 0xffffffff`ffffffff Void
   +0x010 ImageBaseAddress : 0x00000001`3f050000 Void
   +0x018 Ldr              : 0x00000000`77522640 _PEB_LDR_DATA
   +0x020 ProcessParameters : 0x00000000`00242170 _RTL_USER_PROCESS_PARAMETERS
   +0x028 SubSystemData    : (null) 
   +0x030 ProcessHeap      : 0x00000000`00240000 Void        //進程默認堆的地址
   +0x038 FastPebLock      : 0x00000000`7752a960 _RTL_CRITICAL_SECTION
   +0x040 AtlThunkSListPtr : (null) 
   +0x048 IFEOKey          : (null) 
   +0x050 CrossProcessFlags : 0
   +0x050 ProcessInJob     : 0y0
   +0x050 ProcessInitializing : 0y0
   +0x050 ProcessUsingVEH  : 0y0
   +0x050 ProcessUsingVCH  : 0y0
   +0x050 ProcessUsingFTH  : 0y0
   +0x050 ReservedBits0    : 0y000000000000000000000000000 (0)
   +0x058 KernelCallbackTable : (null) 
   +0x058 UserSharedInfoPtr : (null) 
   +0x060 SystemReserved   : [1] 0
   +0x064 AtlThunkSListPtr32 : 0
   +0x068 ApiSetMap        : 0x000007fe`ff710000 Void
   +0x070 TlsExpansionCounter : 0
   +0x078 TlsBitmap        : 0x00000000`77522590 Void
   +0x080 TlsBitmapBits    : [2] 0x11
   +0x088 ReadOnlySharedMemoryBase : 0x00000000`7efe0000 Void
   +0x090 HotpatchInformation : (null) 
   +0x098 ReadOnlyStaticServerData : 0x00000000`7efe0a90  -> (null) 
   +0x0a0 AnsiCodePageData : 0x000007ff`fffa0000 Void
   +0x0a8 OemCodePageData  : 0x000007ff`fffa0000 Void
   +0x0b0 UnicodeCaseTableData : 0x000007ff`fffd0028 Void
   +0x0b8 NumberOfProcessors : 4
   +0x0bc NtGlobalFlag     : 0
   +0x0c0 CriticalSectionTimeout : _LARGE_INTEGER 0xffffe86d`079b8000
   +0x0c8 HeapSegmentReserve : 0x100000             //堆的默認保留大小
   +0x0d0 HeapSegmentCommit : 0x2000                //堆的默認提交大小
   +0x0d8 HeapDeCommitTotalFreeThreshold : 0x10000  //解除提交的總空閒塊閾值
   +0x0e0 HeapDeCommitFreeBlockThreshold : 0x1000   //解除提交的單塊閾值
   +0x0e8 NumberOfHeaps    : 5                      //進程堆的數量
   +0x0ec MaximumNumberOfHeaps : 0x10               //ProcessHeaps數組目前的大小
   +0x0f0 ProcessHeaps     : 0x00000000`7752a6c0  -> 0x00000000`00240000 Void  //一個數組,記錄了每一個堆的地址
   +0x0f8 GdiSharedHandleTable : (null) 
   +0x100 ProcessStarterHelper : (null) 
   +0x108 GdiDCAttributeList : 0
   +0x110 LoaderLock       : 0x00000000`77527490 _RTL_CRITICAL_SECTION
   +0x118 OSMajorVersion   : 6
   +0x11c OSMinorVersion   : 1
   +0x120 OSBuildNumber    : 0x1db1
   +0x122 OSCSDVersion     : 0x100
   +0x124 OSPlatformId     : 2
   +0x128 ImageSubsystem   : 3
   +0x12c ImageSubsystemMajorVersion : 5
   +0x130 ImageSubsystemMinorVersion : 2
   +0x138 ActiveProcessAffinityMask : 0xf
   +0x140 GdiHandleBuffer  : [60] 0
   +0x230 PostProcessInitRoutine : (null) 
   +0x238 TlsExpansionBitmap : 0x00000000`77522580 Void
   +0x240 TlsExpansionBitmapBits : [32] 1
   +0x2c0 SessionId        : 1
   +0x2c8 AppCompatFlags   : _ULARGE_INTEGER 0x0
   +0x2d0 AppCompatFlagsUser : _ULARGE_INTEGER 0x0
   +0x2d8 pShimData        : (null) 
   +0x2e0 AppCompatInfo    : (null) 
   +0x2e8 CSDVersion       : _UNICODE_STRING "Service Pack 1"
   +0x2f8 ActivationContextData : 0x00000000`00140000 _ACTIVATION_CONTEXT_DATA
   +0x300 ProcessAssemblyStorageMap : (null) 
   +0x308 SystemDefaultActivationContextData : 0x00000000`00130000 _ACTIVATION_CONTEXT_DATA
   +0x310 SystemAssemblyStorageMap : (null) 
   +0x318 MinimumStackCommit : 0
   +0x320 FlsCallback      : 0x00000000`0027fe90 _FLS_CALLBACK_INFO
   +0x328 FlsListHead      : _LIST_ENTRY [ 0x00000000`0027fa70 - 0x00000000`0027fa70 ]
   +0x338 FlsBitmap        : 0x00000000`77522570 Void
   +0x340 FlsBitmapBits    : [4] 3
   +0x350 FlsHighIndex     : 1
   +0x358 WerRegistrationData : (null) 
   +0x360 WerShipAssertPtr : (null) 
   +0x368 pContextData     : 0x00000000`00150000 Void
   +0x370 pImageHeaderHash : (null) 
   +0x378 TracingFlags     : 0
   +0x378 HeapTracingEnabled : 0y0
   +0x378 CritSecTracingEnabled : 0y0
   +0x378 SpareTracingBits : 0y000000000000000000000000000000 (0)
複製代碼

  我們可以使用dd 0x00000000`7752a6c0查看進程堆的地址

  也可以使用!heap -h 命令查看進程堆的地址和分配的大小

  而我們可以看到運行結果中分配的地址4A0A90,正好在段4A0000中。

 

  2)堆的相關結構

  我們首先了解下面幾個結構_HEAP_ENTRY,_HEAP_SEGMENT,_HEAP。

  1._HEAP_ENTRY就是塊首,下面是一個64位系統堆塊的結構,我們在申請得到的地址減去0x10,就可以得到HEAP_ENTRY的首地址。

                  

   2._HEAP_SEGMENT是段結構,我們可以這麼認爲,堆申請內存的大小是以段爲單位的,當新建一個堆的時候,系統會默認爲這個堆分配一個段叫0號段,通過剛開始的new和malloc分配的空間都是在這個段上分配的,當這個段用完的時候,如果當初創建堆的時候指明瞭HEAP_GROWABLE這個標誌,那麼系統會爲這個堆在再分配一個段,這個時候新分配的段就稱爲1號段了,以下以此類推。每個段的開始初便是HEAP_SEGMENT結構的首地址,由於這個結構也是申請的一塊內存,所以它前面也會有個HEAP_ENTRY結構: 

                                                

  我們使用Windbg查看HEAP_SEGMENT結構如下:

複製代碼
ntdll!_HEAP_SEGMENT
   +0x000 Entry            : _HEAP_ENTRY
   +0x010 SegmentSignature : Uint4B
   +0x014 SegmentFlags     : Uint4B
   +0x018 SegmentListEntry : _LIST_ENTRY
   +0x028 Heap             : Ptr64 _HEAP    //段所屬的堆
   +0x030 BaseAddress      : Ptr64 Void      //段的基地址
   +0x038 NumberOfPages    : Uint4B      //段的內存頁數
   +0x040 FirstEntry       : Ptr64 _HEAP_ENTRY  //第一個堆塊(HEAP_ENTRY指針,堆塊一般位於HEAP_SEGMENT後面)
   +0x048 LastValidEntry   : Ptr64 _HEAP_ENTRY  //堆塊的邊界值
   +0x050 NumberOfUnCommittedPages : Uint4B    //尚未提交的內存頁數
   +0x054 NumberOfUnCommittedRanges : Uint4B    //UnCommittedRanges數組元素數
   +0x058 SegmentAllocatorBackTraceIndex : Uint2B
   +0x05a Reserved         : Uint2B
   +0x060 UCRSegmentList   : _LIST_ENTRY
複製代碼

  

  3._HEAP結構

  HEAP結構則是記錄了這個堆的信息,這個結構可以找到HEAP_SEGMENT鏈表入口,空閒內存鏈表的入口,內存分配粒度等等信息。HEAP的首地址便是堆句柄的值,但是堆句柄的值又是0號段的首地址也是堆句柄,何解?其實很簡單,0號段的HEAP_SEGMENT就在HEAP結構裏面,HEAP結構類定義如這樣:

複製代碼
0:001> dt ntdll!_HEAP 4a0000
   +0x000 Entry            : _HEAP_ENTRY
   +0x010 SegmentSignature : 0xffeeffee
   +0x014 SegmentFlags     : 0
   +0x018 SegmentListEntry : _LIST_ENTRY [ 0x00000000`004a0128 - 0x00000000`004a0128 ]
   +0x028 Heap             : 0x00000000`004a0000 _HEAP
   +0x030 BaseAddress      : 0x00000000`004a0000 Void
   +0x038 NumberOfPages    : 0x10
   +0x040 FirstEntry       : 0x00000000`004a0a80 _HEAP_ENTRY
   +0x048 LastValidEntry   : 0x00000000`004b0000 _HEAP_ENTRY
   +0x050 NumberOfUnCommittedPages : 0xe
   +0x054 NumberOfUnCommittedRanges : 1
   +0x058 SegmentAllocatorBackTraceIndex : 0
   +0x05a Reserved         : 0
   +0x060 UCRSegmentList   : _LIST_ENTRY [ 0x00000000`004a1fe0 - 0x00000000`004a1fe0 ]
   +0x070 Flags            : 0x1004         //堆標誌
   +0x074 ForceFlags       : 4              //強制標誌
   +0x078 CompatibilityFlags : 0            
   +0x07c EncodeFlagMask   : 0x100000
   +0x080 Encoding         : _HEAP_ENTRY
   +0x090 PointerKey       : 0x5c50b3ba`3fc7668b 
   +0x098 Interceptor      : 0
   +0x09c VirtualMemoryThreshold : 0xff00      //最大堆塊大小
   +0x0a0 Signature        : 0xeeffeeff        //HEAP結構的簽名
   +0x0a8 SegmentReserve   : 0x100000          //段的保留空間大小
   +0x0b0 SegmentCommit    : 0x2000            //每次提交內存的大小
   +0x0b8 DeCommitFreeBlockThreshold : 0x100   //解除提交的單塊閾值
   +0x0c0 DeCommitTotalFreeThreshold : 0x1000  //解除提交的總空閒塊閾值
   +0x0c8 TotalFreeSize    : 0x151             //空閒塊的總大小
   +0x0d0 MaximumAllocationSize : 0x000007ff`fffdefff   //可分配的最大值
   +0x0d8 ProcessHeapsListIndex : 5        //本堆在進程堆列表中的索引
   +0x0da HeaderValidateLength : 0x208     //頭結構的驗證長度
   +0x0e0 HeaderValidateCopy : (null) 
   +0x0e8 NextAvailableTagIndex : 0    //下一個可用的堆塊標記索引
   +0x0ea MaximumTagIndex  : 0         //最大的堆塊標記索引
   +0x0f0 TagEntries       : (null)    //指向用於標記堆塊的標記結構
   +0x0f8 UCRList          : _LIST_ENTRY [ 0x00000000`004a1fd0 - 0x00000000`004a1fd0 ]   //UnCommitedRange Segments
   +0x108 AlignRound       : 0x1f   
   +0x110 AlignMask        : 0xffffffff`fffffff0    //用於地址對齊的掩碼
   +0x118 VirtualAllocdBlocks : _LIST_ENTRY [ 0x00000000`004a0118 - 0x00000000`004a0118 ]
   +0x128 SegmentList      : _LIST_ENTRY [ 0x00000000`004a0018 - 0x00000000`004a0018 ]  //段鏈表HEAP_SEGMENT
   +0x138 AllocatorBackTraceIndex : 0
   +0x13c NonDedicatedListLength : 0    //用於記錄回溯信息
   +0x140 BlocksIndex      : 0x00000000`004a0230 Void
   +0x148 UCRIndex         : (null) 
   +0x150 PseudoTagEntries : (null) 
   +0x158 FreeLists        : _LIST_ENTRY [ 0x00000000`004a0ac0 - 0x00000000`004a0ac0 ]  //空閒塊鏈表數組
   +0x168 LockVariable     : 0x00000000`004a0208 _HEAP_LOCK  //用於串行化控制的同步對象
   +0x170 CommitRoutine    : 0x5c50b3ba`3fc7668b     long  +5c50b3ba3fc7668b
   +0x178 FrontEndHeap     : (null)     //用於快速釋放堆塊的"前端堆"
   +0x180 FrontHeapLockCount : 0      //"前端堆"的鎖定計數
   +0x182 FrontEndHeapType : 0 ''      //"前端堆"的類型
   +0x188 Counters         : _HEAP_COUNTERS
   +0x1f8 TuningParameters : _HEAP_TUNING_PARAMETERS
複製代碼

  對比一下上面HEAP_SEGMENT的結構,可以發現HEAP中就包含一個HEAP_SEGMENT結構。

              

 

 四、定位申請的內存

  我們知道我們申請的內存地址爲4A0A90,根據前面學習的,4A0A90-0x10 = 4A0A80就是_HEAP_ENTRY的地址,而該地址在段4a0000中

  1)我們使用!heap -a 4a0000查該堆的內容

複製代碼
0:001> !heap -a 4a0000
Index   Address  Name      Debugging options enabled
  5:   004a0000 
    Segment at 00000000004a0000 to 00000000004b0000 (00002000 bytes committed)
    Flags:                00001004
    ForceFlags:           00000004
    Granularity:          16 bytes
    Segment Reserve:      00100000
    Segment Commit:       00002000
    DeCommit Block Thres: 00000100
    DeCommit Total Thres: 00001000
    Total Free Size:      00000151
    Max. Allocation Size: 000007fffffdefff
    Lock Variable at:     00000000004a0208
    Next TagIndex:        0000
    Maximum TagIndex:     0000
    Tag Entries:          00000000
    PsuedoTag Entries:    00000000
    Virtual Alloc List:   004a0118
    Uncommitted ranges:   004a00f8
            004a2000: 0000e000  (57344 bytes)
    FreeList[ 00 ] at 00000000004a0158: 00000000004a0ac0 . 00000000004a0ac0  
        00000000004a0ab0: 00030 . 01510 [100] - free

    Segment00 at 004a0000:
        Flags:           00000000
        Base:            004a0000
        First Entry:     004a0a80
        Last Entry:      004b0000
        Total Pages:     00000010
        Total UnCommit:  0000000e
        Largest UnCommit:00000000
        UnCommitted Ranges: (1)

    Heap entries for Segment00 in Heap 00000000004a0000
                 address: psize . size  flags   state (requested size)
        00000000004a0000: 00000 . 00a80 [101] - busy (a7f)
        00000000004a0a80: 00a80 . 00030 [101] - busy (20)
        00000000004a0ab0: 00030 . 01510 [100]
        00000000004a1fc0: 01510 . 00040 [111] - busy (3d)
        00000000004a2000:      0000e000      - uncommitted bytes.
複製代碼

  可以看到內存粒度問16字節

 Granularity:          16 bytes

  繼續往下看可以發現堆中正好有地址爲4a0a80的一項

00000000004a0a80: 00a80 . 00030 [101] - busy (20)

  第一項爲地址,第二項a80爲上一項的堆塊大小,0x30爲該堆塊的大小,[101]爲是這個內存的標誌位,最右邊的1表示內存塊被佔用,然後busy(20)表示這塊內存被佔用,申請的內存爲0x20,加上塊首的大小爲0x10,一共是0x30

  

  2)我們知道了_HEAP_ENTRY的地址爲4a0a80,我們查看該結構體

  

  發現這裏的Size和我們的0x30完全不符!

  我們可以看上面的_HEAP結構,Win7下面的_HEAP結構比XP多了兩項

   +0x07c EncodeFlagMask   : 0x100000
   +0x080 Encoding         : _HEAP_ENTRY

   相對於XP,Vista之後增加了對堆塊的頭結構(HEAP_ENTRY)的編碼。編碼的目的是引入隨機性,增加堆的安全性,防止黑客輕易就可以預測堆的數據結構內容而實施攻擊。在_HEAP結構中新增瞭如下兩個字段:

  其中的EncodeFlagMask用來指示是否啓用編碼功能,Encoding字段是用來編碼的,編碼的方法就是用這個Encoding結構與每個堆塊的頭結構做亦或(XOR)

  讀取_HEAP偏移爲0x80的Encoding子結構:注意Size字段是從偏移8開始的兩個字節,不是從偏移0開始

  

  我們使用dd查看我們的HEAP_ENTRY信息

  

   做異或解碼:

0:001> ?2b552b39^29542b3a
Evaluate expression: 33619971 = 00000000`02010003

  低地址的word是Size字段,所以Size字段是0x3,因爲是以0x10爲內存粒度的,所以字節大小爲

0:001> ?3*0x10
Evaluate expression: 48 = 00000000`00000030

  也就是0x30,與我們顯示出的正好一致

  

  3)內存中的數據

  

  

五、總結

  1.在PEB中保存這進程的堆地址和數量。

  2.HEAP結構記錄HEAP_SEGMENT的方式採用了鏈表,這樣不再受數組大小的約束,同時將HEAP_SEGMENT字段包含進HEAP,這樣各堆段的起始便統一爲HEAP_SEGMENT,不再有xp下0號段與其他段那種區別,可以統一進行管理了。

  3.每個HEAP_SEGMENT都有多個堆塊,每個堆塊包含塊首和塊身,塊身爲我們申請得到的地址。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章