Windows堆

堆(HEAP)的分配,使用,回收都是通过微软的API来管理的,最常见的API是malloc和new。在往底层走一点呢,这两个函数都会调 用HeapAlloc(RtlAllocateHeap)。同样的相关函数还有HeapFree用来释放堆,HeapCreate用来创建自己的私有堆。 下面是这些函数的调用链:

HeapCreate->RtlCreateHeap->ZwAllocateVirtualMemory (这里会直接申请一大片内存,至于申请多大内存,由进程PEB结构中的字段觉得,HeapSegmentReserve字段指出要申请多大的虚拟内 存,HeapSegmentCommit指明要提交多大内存)

HeapAlloc->RtlAllocateHeap(至于这里申请的内存,由于HeapCreate已经申请了一大片内存,堆管理器这片内存中划分一块出来以满足申请的需要。这一步申请操作是堆管理器自己维护的,仅当申请内存不够的时候才会再次调用ZwAllocateVirtualMemory )

HeapFree->RtlFreeHeap (对于释放的内存,堆管理器只是简单的把这块内存标志位已释放让后加入到空闲列表中,仅当空闲的内存达到一定阀值的时候会调用ZwFreeVirtualMeMory )

HeapDestroy->RtlDestroyHeap->ZwFreeVirtualMeMory (销毁我们申请的堆)

在Windows10和服务器2016之前,只有一种堆类型,我们称之为NT堆。Windows 10引入了一种称为段堆(segment heap)的新堆类型。这两种堆类型包括公共元素,但结构和实现方式不同。默认情况下,所有UWP应用程序和某些系统进程都使用段堆,而所有其他进程都使用NT堆。这可以在注册表中更改。

在一个进程中一般也分为两种堆,进程的默认堆和自己创建的私有堆。在程序启动时,系统在刚刚创建的进程虚拟地址空间中创建一个进程的默认堆,而且程序也可以通过 HeapCreate 函数来调用 ntdll 中的RtlCreateHeap 来创建自己的私有堆,所以一个进程中可以存在多个堆。

默认堆和私有堆只是返回的句柄不同,默认堆的句柄保存在PEB中

0:004> .process
Implicit process is now 005d5000
0:004> dt _PEB 005d5000
ntdll!_PEB
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0x1 ''
   +0x003 BitField         : 0x4 ''
   +0x003 ImageUsesLargePages : 0y0
   +0x003 IsProtectedProcess : 0y0
   +0x003 IsImageDynamicallyRelocated : 0y1
   +0x003 SkipPatchingUser32Forwarders : 0y0
   +0x003 IsPackagedProcess : 0y0
   +0x003 IsAppContainer   : 0y0
   +0x003 IsProtectedProcessLight : 0y0
   +0x003 IsLongPathAwareProcess : 0y0
   +0x004 Mutant           : 0xffffffff Void
   +0x008 ImageBaseAddress : 0x00810000 Void
   +0x00c Ldr              : 0x773d5d80 _PEB_LDR_DATA
   +0x010 ProcessParameters : 0x00ae0af0 _RTL_USER_PROCESS_PARAMETERS
   +0x014 SubSystemData    : (null) 
   +0x018 ProcessHeap      : 0x00ae0000 Void
   +0x01c FastPebLock      : 0x773d5b40 _RTL_CRITICAL_SECTION
   +0x020 AtlThunkSListPtr : (null) 
   +0x024 IFEOKey          : (null) 
   +0x028 CrossProcessFlags : 0
   +0x028 ProcessInJob     : 0y0
   +0x028 ProcessInitializing : 0y0
   +0x028 ProcessUsingVEH  : 0y0
   +0x028 ProcessUsingVCH  : 0y0
   +0x028 ProcessUsingFTH  : 0y0
   +0x028 ProcessPreviouslyThrottled : 0y0
   +0x028 ProcessCurrentlyThrottled : 0y0
   +0x028 ProcessImagesHotPatched : 0y0
   +0x028 ReservedBits0    : 0y000000000000000000000000 (0)
   +0x02c KernelCallbackTable : (null) 
   +0x02c UserSharedInfoPtr : (null) 
   +0x030 SystemReserved   : 0
   +0x034 AtlThunkSListPtr32 : (null) 
   +0x038 ApiSetMap        : 0x002d0000 Void
   +0x03c TlsExpansionCounter : 0
   +0x040 TlsBitmap        : 0x773d5d30 Void
   +0x044 TlsBitmapBits    : [2] 0x10001
   +0x04c ReadOnlySharedMemoryBase : 0x7f620000 Void
   +0x050 SharedData       : (null) 
   +0x054 ReadOnlyStaticServerData : 0x7f620750  -> (null) 
   +0x058 AnsiCodePageData : 0x7f780000 Void
   +0x05c OemCodePageData  : 0x7f780000 Void
   +0x060 UnicodeCaseTableData : 0x7f7b0028 Void
   +0x064 NumberOfProcessors : 8
   +0x068 NtGlobalFlag     : 0
   +0x070 CriticalSectionTimeout : _LARGE_INTEGER 0xffffe86d`079b8000
   +0x078 HeapSegmentReserve : 0x100000
   +0x07c HeapSegmentCommit : 0x2000
   +0x080 HeapDeCommitTotalFreeThreshold : 0x10000
   +0x084 HeapDeCommitFreeBlockThreshold : 0x1000
   +0x088 NumberOfHeaps    : 1
   +0x08c MaximumNumberOfHeaps : 0x10
   +0x090 ProcessHeaps     : 0x773d4840  -> 0x00ae0000 Void
   +0x094 GdiSharedHandleTable : (null) 
   +0x098 ProcessStarterHelper : (null) 
   +0x09c GdiDCAttributeList : 0
   +0x0a0 LoaderLock       : 0x773d3390 _RTL_CRITICAL_SECTION
   +0x0a4 OSMajorVersion   : 0xa
   +0x0a8 OSMinorVersion   : 0
   +0x0ac OSBuildNumber    : 0x4a62
   +0x0ae OSCSDVersion     : 0
   +0x0b0 OSPlatformId     : 2
   +0x0b4 ImageSubsystem   : 3
   +0x0b8 ImageSubsystemMajorVersion : 6
   +0x0bc ImageSubsystemMinorVersion : 0
   +0x0c0 ActiveProcessAffinityMask : 0xff
   +0x0c4 GdiHandleBuffer  : [34] 0
   +0x14c PostProcessInitRoutine : (null) 
   +0x150 TlsExpansionBitmap : 0x773d5d18 Void
   +0x154 TlsExpansionBitmapBits : [32] 1
   +0x1d4 SessionId        : 1
   +0x1d8 AppCompatFlags   : _ULARGE_INTEGER 0x0
   +0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER 0x0
   +0x1e8 pShimData        : 0x00350000 Void
   +0x1ec AppCompatInfo    : (null) 
   +0x1f0 CSDVersion       : _UNICODE_STRING ""
   +0x1f8 ActivationContextData : 0x00340000 _ACTIVATION_CONTEXT_DATA
   +0x1fc ProcessAssemblyStorageMap : (null) 
   +0x200 SystemDefaultActivationContextData : 0x00330000 _ACTIVATION_CONTEXT_DATA
   +0x204 SystemAssemblyStorageMap : (null) 
   +0x208 MinimumStackCommit : 0
   +0x20c SparePointers    : [4] (null) 
   +0x21c SpareUlongs      : [5] 0
   +0x230 WerRegistrationData : (null) 
   +0x234 WerShipAssertPtr : (null) 
   +0x238 pUnused          : (null) 
   +0x23c pImageHeaderHash : (null) 
   +0x240 TracingFlags     : 0
   +0x240 HeapTracingEnabled : 0y0
   +0x240 CritSecTracingEnabled : 0y0
   +0x240 LibLoaderTracingEnabled : 0y0
   +0x240 SpareTracingBits : 0y00000000000000000000000000000 (0)
   +0x248 CsrServerReadOnlySharedMemoryBase : 0x00007df4`77ac0000
   +0x250 TppWorkerpListLock : 0
   +0x254 TppWorkerpList   : _LIST_ENTRY [ 0x91fca0 - 0xcdfe2c ]
   +0x25c WaitOnAddressHashTable : [128] (null) 
   +0x45c TelemetryCoverageHeader : (null) 
   +0x460 CloudFileFlags   : 0
   +0x464 CloudFileDiagFlags : 0
   +0x468 PlaceholderCompatibilityMode : 0 ''
   +0x469 PlaceholderCompatibilityModeReserved : [7]  ""
   +0x470 LeapSecondData   : 0x7f770000 _LEAP_SECOND_DATA
   +0x474 LeapSecondFlags  : 0
   +0x474 SixtySecondEnabled : 0y0
   +0x474 Reserved         : 0y0000000000000000000000000000000 (0)
   +0x478 NtGlobalFlag2    : 0

+0x018 ProcessHeap : 0x00ae0000 Void 默认堆的地址
+0x078 HeapSegmentReserve : 0x100000 默认堆的保留大小
+0x07c HeapSegmentCommit : 0x2000 默认堆的初始提交大小
+0x080 HeapDeCommitTotalFreeThreshold : 0x10000 与堆释放有关的阈值
+0x084 HeapDeCommitFreeBlockThreshold : 0x1000 与堆释放有关的阈值
+0x088 NumberOfHeaps : 1 程序中堆的数量
+0x08c MaximumNumberOfHeaps : 0x10 程序中最大的堆的数量
+0x090 ProcessHeaps : 0x773d4840 -> 0x00ae0000 Void 存储所有堆地址的数组

只有当本次释放时

  1. 本次释放的堆块大小超过了_PEB中的HeapDeCommitFreeBlockThreshold字段的值。

  2. 空闲空间的总大小超过了_PEB中的eapDeCommitTotalFreeThreshold字段的值。

堆管理器才会将该内存交还给内存管理器,否则继续由堆管理器管理

HEAP的组织结构

HeapCreate函数返回的堆句柄其实就是一个指向堆管理结构的指针,每个堆都会涉及到这样三个结构:HEAP,HEAP_SEGMENT,HEAP_ENTRY

HEAP结构

列出当前进程的所有堆

!heap -h列出当前进程的所有堆

0:004> !heap -h
Index   Address  Name      Debugging options enabled
  1:   00ae0000 
    Segment at 00ae0000 to 00bdf000 (0000e000 bytes committed)

在 Windows 的堆中管理着许多的堆段 (Segment),在堆创建时同时创建第一个堆段,称为 0 号段,之后如果一个段不够,如果指明了 HEAP_GROWABLE 标志,会创建其他的堆段,但是最多有 64 个堆段,而这一个个堆段,正是由堆块构成。

0:004> dt _HEAP 00ae0000
ntdll!_HEAP
   +0x000 Segment          : _HEAP_SEGMENT
   +0x000 Entry            : _HEAP_ENTRY
   +0x008 SegmentSignature : 0xffeeffee
   +0x00c SegmentFlags     : 2
   +0x010 SegmentListEntry : _LIST_ENTRY [ 0xae00a4 - 0xae00a4 ]
   +0x018 Heap             : 0x00ae0000 _HEAP
   +0x01c BaseAddress      : 0x00ae0000 Void
   +0x020 NumberOfPages    : 0xff
   +0x024 FirstEntry       : 0x00ae04a8 _HEAP_ENTRY
   +0x028 LastValidEntry   : 0x00bdf000 _HEAP_ENTRY
   +0x02c NumberOfUnCommittedPages : 0xf1
   +0x030 NumberOfUnCommittedRanges : 1
   +0x034 SegmentAllocatorBackTraceIndex : 0
   +0x036 Reserved         : 0
   +0x038 UCRSegmentList   : _LIST_ENTRY [ 0xaedff0 - 0xaedff0 ]
   +0x040 Flags            : 2
   +0x044 ForceFlags       : 0
   +0x048 CompatibilityFlags : 0
   +0x04c EncodeFlagMask   : 0x100000
   +0x050 Encoding         : _HEAP_ENTRY
   +0x058 Interceptor      : 0
   +0x05c VirtualMemoryThreshold : 0xfe00
   +0x060 Signature        : 0xeeffeeff
   +0x064 SegmentReserve   : 0x100000
   +0x068 SegmentCommit    : 0x2000
   +0x06c DeCommitFreeBlockThreshold : 0x800
   +0x070 DeCommitTotalFreeThreshold : 0x2000
   +0x074 TotalFreeSize    : 0xb8
   +0x078 MaximumAllocationSize : 0x7ffdefff
   +0x07c ProcessHeapsListIndex : 1
   +0x07e HeaderValidateLength : 0x258
   +0x080 HeaderValidateCopy : (null) 
   +0x084 NextAvailableTagIndex : 0
   +0x086 MaximumTagIndex  : 0
   +0x088 TagEntries       : (null) 
   +0x08c UCRList          : _LIST_ENTRY [ 0xaedfe8 - 0xaedfe8 ]
   +0x094 AlignRound       : 0xf
   +0x098 AlignMask        : 0xfffffff8
   +0x09c VirtualAllocdBlocks : _LIST_ENTRY [ 0xae009c - 0xae009c ]
   +0x0a4 SegmentList      : _LIST_ENTRY [ 0xae0010 - 0xae0010 ]
   +0x0ac AllocatorBackTraceIndex : 0
   +0x0b0 NonDedicatedListLength : 0
   +0x0b4 BlocksIndex      : 0x00ae0270 Void
   +0x0b8 UCRIndex         : (null) 
   +0x0bc PseudoTagEntries : (null) 
   +0x0c0 FreeLists        : _LIST_ENTRY [ 0xae4518 - 0xaedd88 ]
   +0x0c8 LockVariable     : 0x00ae0258 _HEAP_LOCK
   +0x0cc CommitRoutine    : 0x46b4f157     long  +46b4f157
   +0x0d0 StackTraceInitVar : _RTL_RUN_ONCE
   +0x0d4 CommitLimitData  : _RTL_HEAP_MEMORY_LIMIT_DATA
   +0x0e4 FrontEndHeap     : 0x007f0000 Void
   +0x0e8 FrontHeapLockCount : 0
   +0x0ea FrontEndHeapType : 0x2 ''
   +0x0eb RequestedFrontEndHeapType : 0x2 ''
   +0x0ec FrontEndHeapUsageData : 0x00ae7fe8  ""
   +0x0f0 FrontEndHeapMaximumIndex : 0x802
   +0x0f2 FrontEndHeapStatusBitmap : [257]  "???"
   +0x1f4 Counters         : _HEAP_COUNTERS
   +0x250 TuningParameters : _HEAP_TUNING_PARAMETERS

+0x05c VirtualMemoryThreshold : 0xfe00 最大分配内存,超过此大小就交由内存管理器分配,单位是颗粒度,0xfe00 * 8 = 508kB
+0x078 MaximumAllocationSize : 0x7ffdefff 最大申请大小
+0x09c VirtualAllocdBlocks : _LIST_ENTRY [ 0xae009c - 0xae009c ] 管理由内存管理器分配内存的链表
+0x0a4 SegmentList : _LIST_ENTRY [ 0xae0010 - 0xae0010 ] 堆段列表
+0x0c0 FreeLists : _LIST_ENTRY [ 0xae4518 - 0xaedd88 ] 空闲堆块的双向链表头指针
+0x0e4 FrontEndHeap : 0x007f0000 Void 指向前端分配器
+0x04c EncodeFlagMask : 0x100000 否启用编码功能
+0x050 Encoding : _HEAP_ENTRY 用于encode(xor)的cookie
+0x008 SegmentSignature : 0xffeeffee 可以用于判断是段堆还是NT堆
+0x098 AlignMask : 0xfffffff8 对齐掩码

HEAP结构则是记录了这个堆的信息,这个结构可以找到HEAP_SEGMENT链表入口,空闲内存链表的入口,内存分配粒度等等信息。 HEAP的首地址便是堆句柄的值,但是堆句柄的值又是0号段的首地址也是堆句柄,何解?其实很简单,0号段的HEAP_SEGMENT就在HEAP结构里 面,HEAP结构类定义如这样:

struct _HEAP
{
_HEAP_ENTRY Entry ; //HEAP_ENTRY结构,用来描述存储HEAP内存块大小等信息的
_HEAP_SEGMENT Segment ;  //0号段的首地址
……  //对于该HEAP的描述信息
} ;
HEAP_SEGMENT结构

我们可以这么认为,堆申请内存的大小是以段为单位的,当新建一个堆的时候,系统会默认为这个堆分配一个段叫0号段,通过刚开始的new和 malloc分配的空间都是在这个段上分配的,当这个段用完的时候,如果当初创建堆的时候指明了HEAP_GROWABLE这个标志,那么系统会为这个堆 在再分配一个段,这个时候新分配的段就称为1号段了,以下以此类推。每个段的开始初便是HEAP_SEGMENT结构的首地址,由于这个结构也是申请的一 块内存,所以它前面也会有个HEAP_ENTRY结构:

内存空间
HEAP_ENTRY(8 bytes)
HEAP_SEGMENT
HEAP_ENTRY(8 bytes)
我们new或malloc分配的空间
填充空间

HEAP_SEGMENT结构会记录段的一些基本信息,该段申请的大小,已经提交内存的大小,第一个HEAP_ENTRY结构的入口点。

但是0号段很特别,这个段的起始地址就是堆句柄指针指向的值,也就是说,HeapCreate返回的堆句柄总是指向0号段,为什么呢?因为HEAP结构是HEAP_ENTRY,HEAP_SEGMENT的合体加长版

内存空间
HEAP_ENTRY(8 bytes)
HEAP_SEGMENT
HEAP

更确切的说,HEAP结构中本身就包含了HEAP_ENTRY和HEAP_SEGMENT,HEAP_ENTRY结构是HEAP的第一个数据成 员,HEAP_SEGMENT是它第二个数据成员。而对于HEAP_SEGMENT,它的第一个数据成员便是HEAP_ENTRY。

//0x40 bytes (sizeof)
struct _HEAP_SEGMENT
{
    struct _HEAP_ENTRY Entry;                                               //0x0
    ULONG SegmentSignature;                                                 //0x8
    ULONG SegmentFlags;                                                     //0xc
    struct _LIST_ENTRY SegmentListEntry;                                    //0x10
    struct _HEAP* Heap;                                                     //0x18
    VOID* BaseAddress;                                                      //0x1c
    ULONG NumberOfPages;                                                    //0x20
    struct _HEAP_ENTRY* FirstEntry;                                         //0x24
    struct _HEAP_ENTRY* LastValidEntry;                                     //0x28
    ULONG NumberOfUnCommittedPages;                                         //0x2c
    ULONG NumberOfUnCommittedRanges;                                        //0x30
    USHORT SegmentAllocatorBackTraceIndex;                                  //0x34
    USHORT Reserved;                                                        //0x36
    struct _LIST_ENTRY UCRSegmentList;                                      //0x38
}; 
HEAP_ENTRY结构:

在堆管理中,每一块申请下来的内存都会有下面所示的固定模式:

内存空间
HEAP_ENTRY(8 bytes)
我们new或malloc分配的空间
填充空间

这个结构用来记录所分配的空间的信息,包括用户申请的空间,填充的空间。

//0x8 bytes (sizeof)
struct _HEAP_ENTRY
{
    union
    {
        struct _HEAP_UNPACKED_ENTRY UnpackedEntry;                          //0x0
        struct
        {
            USHORT Size;                                                    //0x0
            UCHAR Flags;                                                    //0x2
            UCHAR SmallTagIndex;                                            //0x3
        };
        struct
        {
            ULONG SubSegmentCode;                                           //0x0
            USHORT PreviousSize;                                            //0x4
            union
            {
                UCHAR SegmentOffset;                                        //0x6
                UCHAR LFHFlags;                                             //0x6
            };
            UCHAR UnusedBytes;                                              //0x7
        };
        struct _HEAP_EXTENDED_ENTRY ExtendedEntry;                          //0x0
        struct
        {
            USHORT FunctionIndex;                                           //0x0
            USHORT ContextValue;                                            //0x2
        };
        struct
        {
            ULONG InterceptorValue;                                         //0x0
            USHORT UnusedBytesLength;                                       //0x4
            UCHAR EntryOffset;                                              //0x6
            UCHAR ExtendedBlockSignature;                                   //0x7
        };
        struct
        {
            ULONG Code1;                                                    //0x0
            union
            {
                struct
                {
                    USHORT Code2;                                           //0x4
                    UCHAR Code3;                                            //0x6
                    UCHAR Code4;                                            //0x7
                };
                ULONG Code234;                                              //0x4
            };
        };
        ULONGLONG AgregateCode;                                             //0x0
    };
}; 

Heap Entry前八个字节保存结构信息,但是windows为了安全性,对前八个字节进行了编码,编码方式为与HEAP结构+0x050 Encoding偏移处八个字节异或。
保存已分配内存所使用的字段一般是:

//0x8 bytes (sizeof)
struct _HEAP_ENTRY
{
    USHORT Size;              //表示该堆块的大小。其单位为8byte
    UCHAR Flags;               //字段代表堆块的状态
    UCHAR SmallTagIndex;      
    USHORT PreviousSize;      //表示前一个堆块的大小
    UCHAR SegmentIndex;   
    UCHAR UnusedBytes;         //表示多分配的字节数,补齐未使用的字节
}; 

flag标志位
0×01 该块处于占用状态
0×02 该块存在额外描述
0×04 使用固定模式填充堆块
0×08 虚拟分配
0×10 该段最后一个堆块

demo测试

#include <iostream>
#include <windows.h>

struct _HEAP_ENTRY
{
	USHORT Size;              //表示该堆块的大小。其单位为8byte
	UCHAR Flags;               //字段代表堆块的状态
	UCHAR SmallTagIndex;
	USHORT PreviousSize;      //表示前一个堆块的大小
	UCHAR SegmentIndex;
	UCHAR UnusedBytes;         //表示多分配的字节数,补齐未使用的字节
};

int main()
{
	_HEAP_ENTRY stHeapEntry = { 0 };

	//创建堆
	HANDLE hHeap = HeapCreate(HEAP_ZERO_MEMORY, 0, 1024 * 1024);
	//解码字节
	BYTE* pEncoding = (BYTE*)((DWORD)hHeap + 0x50);

	//申请500字节的内存
	BYTE* pBuffer = (BYTE*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 500);
	pBuffer -= sizeof(stHeapEntry);

	BYTE* pTemp = (BYTE*)&stHeapEntry;
	for (int i = 0; i < sizeof(stHeapEntry); i++)
	{
		pTemp[i] = pEncoding[i] ^ pBuffer[i];
	}
	
	std::cout << "Size:" << stHeapEntry.Size * 8 << std::endl;
	std::cout << "Flags:" << (int)stHeapEntry.Flags << std::endl;
	std::cout << "SmallTagIndex:" << (int)stHeapEntry.SmallTagIndex << std::endl;
	std::cout << "PreviousSize:" << stHeapEntry.PreviousSize << std::endl;
	std::cout << "SegmentIndex:" << (int)stHeapEntry.SegmentIndex << std::endl;
	std::cout << "UnusedBytes:" << (int)stHeapEntry.UnusedBytes << std::endl;

	bool bRetVal = HeapFree(hHeap, HEAP_NO_SERIALIZE, pBuffer + sizeof(stHeapEntry));

	return 0;
}
_HEAP_VIRTUAL_ALLOC_ENTRY结构

+0x05c VirtualMemoryThreshold : 0xfe00 最大分配内存,超过此大小就交由内存管理器分配,单位是颗粒度,0xfe00 * 8 = 508kB

//0x20 bytes (sizeof)
struct _HEAP_VIRTUAL_ALLOC_ENTRY
{
    struct _LIST_ENTRY Entry;                                               //0x0
    struct _HEAP_ENTRY_EXTRA ExtraStuff;                                    //0x8
    ULONG CommitSize;                                                       //0x10
    ULONG ReserveSize;                                                      //0x14
    struct _HEAP_ENTRY BusyBlock;                                           //0x18
}; 

//0x8 bytes (sizeof)
struct _LIST_ENTRY
{
    struct _LIST_ENTRY* Flink;                                              //0x0
    struct _LIST_ENTRY* Blink;                                              //0x4
}; 

demo

// HeapMemory.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <cstdio>
#include <windows.h>

struct _HEAP_ENTRY
{
	USHORT Size;              //表示该堆块的大小。其单位为8byte
	UCHAR Flags;               //字段代表堆块的状态
	UCHAR SmallTagIndex;
	USHORT PreviousSize;      //表示前一个堆块的大小
	UCHAR SegmentIndex;
	UCHAR UnusedBytes;         //表示多分配的字节数,补齐未使用的字节
};

//0x8 bytes (sizeof)
struct _HEAP_ENTRY_EXTRA
{
	union
	{
		struct
		{
			USHORT AllocatorBackTraceIndex;                                 //0x0
			USHORT TagIndex;                                                //0x2
			ULONG Settable;                                                 //0x4
		};
		ULONGLONG ZeroInit;                                                 //0x0
	};
};

//0x20 bytes (sizeof)
struct _HEAP_VIRTUAL_ALLOC_ENTRY
{
	struct _LIST_ENTRY Entry;                                               //0x0
	struct _HEAP_ENTRY_EXTRA ExtraStuff;                                    //0x8
	ULONG CommitSize;                                                       //0x10
	ULONG ReserveSize;                                                      //0x14
	struct _HEAP_ENTRY BusyBlock;                                           //0x18
};

//0x258 bytes (sizeof)
struct _HEAP
{
	struct _HEAP_ENTRY Entry;                                       //0x0
	ULONG SegmentSignature;                                         //0x8
	ULONG SegmentFlags;                                             //0xc
	struct _LIST_ENTRY SegmentListEntry;                            //0x10
	struct _HEAP* Heap;                                             //0x18
	VOID* BaseAddress;                                              //0x1c
	ULONG NumberOfPages;                                            //0x20
	struct _HEAP_ENTRY* FirstEntry;                                 //0x24
	struct _HEAP_ENTRY* LastValidEntry;                             //0x28
	ULONG NumberOfUnCommittedPages;                                 //0x2c
	ULONG NumberOfUnCommittedRanges;                                //0x30
	USHORT SegmentAllocatorBackTraceIndex;                          //0x34
	USHORT Reserved;                                                //0x36
	struct _LIST_ENTRY UCRSegmentList;                              //0x38
	ULONG Flags;                                                            //0x40
	ULONG ForceFlags;                                                       //0x44
	ULONG CompatibilityFlags;                                               //0x48
	ULONG EncodeFlagMask;                                                   //0x4c
	struct _HEAP_ENTRY Encoding;                                            //0x50
	ULONG Interceptor;                                                      //0x58
	ULONG VirtualMemoryThreshold;                                           //0x5c
	ULONG Signature;                                                        //0x60
	ULONG SegmentReserve;                                                   //0x64
	ULONG SegmentCommit;                                                    //0x68
	ULONG DeCommitFreeBlockThreshold;                                       //0x6c
	ULONG DeCommitTotalFreeThreshold;                                       //0x70
	ULONG TotalFreeSize;                                                    //0x74
	ULONG MaximumAllocationSize;                                            //0x78
	USHORT ProcessHeapsListIndex;                                           //0x7c
	USHORT HeaderValidateLength;                                            //0x7e
	VOID* HeaderValidateCopy;                                               //0x80
	USHORT NextAvailableTagIndex;                                           //0x84
	USHORT MaximumTagIndex;                                                 //0x86
	struct _HEAP_TAG_ENTRY* TagEntries;                                     //0x88
	struct _LIST_ENTRY UCRList;                                             //0x8c
	ULONG AlignRound;                                                       //0x94
	ULONG AlignMask;                                                        //0x98
	struct _LIST_ENTRY VirtualAllocdBlocks;                                 //0x9c
	struct _LIST_ENTRY SegmentList;                                         //0xa4
	USHORT AllocatorBackTraceIndex;                                         //0xac
	ULONG NonDedicatedListLength;                                           //0xb0
	VOID* BlocksIndex;                                                      //0xb4
	VOID* UCRIndex;                                                         //0xb8
	struct _HEAP_PSEUDO_TAG_ENTRY* PseudoTagEntries;                        //0xbc
	struct _LIST_ENTRY FreeLists;                                           //0xc0
	struct _HEAP_LOCK* LockVariable;                                        //0xc8
	LONG(*CommitRoutine)(VOID* arg1, VOID** arg2, ULONG* arg3);            //0xcc
	union _RTL_RUN_ONCE StackTraceInitVar;                                  //0xd0
	//........
};

void EnumHeapEntry(HANDLE hHeap)
{
	//解码字节
	BYTE* pEncoding = (BYTE*)((DWORD)hHeap + 0x50);

	_HEAP_ENTRY stHeapEntry = { 0 };
	BYTE* pHeapEntry = (BYTE*)&stHeapEntry;
	BYTE* pTemp = (BYTE*)hHeap;
	do 
	{
		for (int i = 0; i < sizeof(stHeapEntry); i++)
		{
			pHeapEntry[i] = pEncoding[i] ^ pTemp[i];
		}
		printf("HeapEntry:%08p\tSize:%08d\tFlags:%x\tSmallTagIndex:%02x\tPreviousSize:%08d\tSegmentIndex:%d\tUnusedBytes:%d\n",
			pTemp, stHeapEntry.Size * 8, stHeapEntry.Flags, stHeapEntry.SmallTagIndex, stHeapEntry.PreviousSize * 8, 
			stHeapEntry.SegmentIndex, stHeapEntry.UnusedBytes);
		pTemp = pTemp + stHeapEntry.Size * 8;
	} while (!(stHeapEntry.Size == 4 && stHeapEntry.UnusedBytes == 3));

	//_HEAP* pHeap = (_HEAP*)hHeap;
	//_HEAP_VIRTUAL_ALLOC_ENTRY* pAllocEntry = (_HEAP_VIRTUAL_ALLOC_ENTRY*)pHeap->VirtualAllocdBlocks.Flink;
	//while (pAllocEntry != pHeap->VirtualAllocdBlocks)
	//{

	//}

	printf("\n");
}

int main()
{
	//创建堆
	HANDLE hHeap = HeapCreate(HEAP_ZERO_MEMORY, 0, 0);
	
	EnumHeapEntry(hHeap);

	//申请500字节的内存
	BYTE* pBuffer1 = (BYTE*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 500);

	EnumHeapEntry(hHeap);

	BYTE* pBuffer2 = (BYTE*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 24);

	EnumHeapEntry(hHeap);

	BYTE* pBuffer3 = (BYTE*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 1024 * 507);

	EnumHeapEntry(hHeap);

	system("pause");

	BYTE* pBuffer4 = (BYTE*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 1024 * 508);

	EnumHeapEntry(hHeap);

	system("pause");

	HeapFree(hHeap, HEAP_NO_SERIALIZE, pBuffer1);
	HeapFree(hHeap, HEAP_NO_SERIALIZE, pBuffer2);
	HeapFree(hHeap, HEAP_NO_SERIALIZE, pBuffer3);
	HeapFree(hHeap, HEAP_NO_SERIALIZE, pBuffer4);

	EnumHeapEntry(hHeap);

	system("pause");

	return 0;
}

查看堆的所有信息

0:004> !heap -a -h 00ae0000
Index   Address  Name      Debugging options enabled
  1:   00ae0000 
    Segment at 00ae0000 to 00bdf000 (0000e000 bytes committed)
    Flags:                00000002
    ForceFlags:           00000000
    Granularity:          8 bytes
    Segment Reserve:      00100000
    Segment Commit:       00002000
    DeCommit Block Thres: 00000800
    DeCommit Total Thres: 00002000
    Total Free Size:      000000b8
    Max. Allocation Size: 7ffdefff
    Lock Variable at:     00ae0258
    Next TagIndex:        0000
    Maximum TagIndex:     0000
    Tag Entries:          00000000
    PsuedoTag Entries:    00000000
    Virtual Alloc List:   00ae009c
    Uncommitted ranges:   00ae008c
            00aee000: 000f1000  (987136 bytes)
    FreeList[ 00 ] at 00ae00c0: 00aedd88 . 00ae4518  
        00ae4510: 00050 . 00020 [100] - free
        00ae3238: 00018 . 00020 [100] - free
        00ae7cd8: 00050 . 00020 [100] - free
        00ae2388: 00018 . 00028 [100] - free
        00ae4788: 00018 . 00040 [100] - free
        00ae3010: 000d8 . 00090 [100] - free
        00ae3150: 000b0 . 00098 [100] - free
        00ae7838: 000e8 . 000b8 [100] - free
        00ae4090: 000b0 . 000b8 [100] - free
        00aedd80: 01008 . 00260 [100] - free

    Segment00 at 00ae0000:
        Flags:           00000000
        Base:            00ae0000
        First Entry:     00ae04a8
        Last Entry:      00bdf000
        Total Pages:     000000ff
        Total UnCommit:  000000f1
        Largest UnCommit:00000000
        UnCommitted Ranges: (1)

    Heap entries for Segment00 in Heap 00ae0000
         address: psize . size  flags   state (requested size)
        00ae0000: 00000 . 004a8 [101] - busy (4a7)
        00ae04a8: 004a8 . 00108 [101] - busy (100)
        00ae05b0: 00108 . 000d8 [101] - busy (d0)
        00ae0688: 000d8 . 00130 [101] - busy (128)
        00ae07b8: 00130 . 00128 [101] - busy (120)
        00ae08e0: 00128 . 00030 [101] - busy (24)
        00ae0910: 00030 . 00010 [101] - busy (4)
        00ae0920: 00010 . 00018 [101] - busy (c)
        00ae0938: 00018 . 000d8 [101] - busy (d0)
        00ae0a10: 000d8 . 000d8 [101] - busy (d0)
        00ae0ae8: 000d8 . 01370 [101] - busy (1362)
        00ae1e58: 01370 . 00048 [101] - busy (3c)
        00ae1ea0: 00048 . 00038 [101] - busy (30)
        00ae1ed8: 00038 . 00070 [101] - busy (62)
        00ae1f48: 00070 . 000b0 [101] - busy (a8)
        00ae1ff8: 000b0 . 00038 [101] - busy (2c)
        00ae2030: 00038 . 000b0 [101] - busy (a8)
        00ae20e0: 000b0 . 00038 [101] - busy (2c)
        00ae2118: 00038 . 00228 [101] - busy (220)
        00ae2340: 00228 . 00030 [101] - busy (24)
        00ae2370: 00030 . 00018 [101] - busy (c)
        00ae2388: 00018 . 00028 [100]
        00ae23b0: 00028 . 00020 [101] - busy (10)
        00ae23d0: 00020 . 000b0 [101] - busy (a8)
        00ae2480: 000b0 . 00038 [101] - busy (2c)
        00ae24b8: 00038 . 00050 [101] - busy (42)
        00ae2508: 00050 . 00128 [101] - busy (120)
        00ae2630: 00128 . 00098 [101] - busy (90)
        00ae26c8: 00098 . 00038 [101] - busy (2c)
        00ae2700: 00038 . 00018 [101] - busy (10)
        00ae2718: 00018 . 00010 [101] - busy (8)
        00ae2728: 00010 . 000b0 [101] - busy (a8)
        00ae27d8: 000b0 . 00038 [101] - busy (2c)
        00ae2810: 00038 . 00050 [101] - busy (46)
        00ae2860: 00050 . 00010 [101] - busy (4)
        00ae2870: 00010 . 00048 [101] - busy (40)
        00ae28b8: 00048 . 00208 [101] - busy (200)
        00ae2ac0: 00208 . 00208 [101] - busy (200)
        00ae2cc8: 00208 . 00030 [101] - busy (28)
        00ae2cf8: 00030 . 00048 [101] - busy (3c)
        00ae2d40: 00048 . 00048 [101] - busy (3c)
        00ae2d88: 00048 . 000d8 [101] - busy (d0)
        00ae2e60: 000d8 . 000d8 [101] - busy (d0)
        00ae2f38: 000d8 . 000d8 [101] - busy (d0)
        00ae3010: 000d8 . 00090 [100]
        00ae30a0: 00090 . 000b0 [101] - busy (a8)
        00ae3150: 000b0 . 00098 [100]
        00ae31e8: 00098 . 00038 [101] - busy (2c)
        00ae3220: 00038 . 00018 [101] - busy (10)
        00ae3238: 00018 . 00020 [100]
        00ae3258: 00020 . 00d68 [101] - busy (d5e)
        00ae3fc0: 00d68 . 00020 [101] - busy (10)
        00ae3fe0: 00020 . 000b0 [101] - busy (a8)
        00ae4090: 000b0 . 000b8 [100]
        00ae4148: 000b8 . 000b0 [101] - busy (a8)
        00ae41f8: 000b0 . 00038 [101] - busy (2c)
        00ae4230: 00038 . 00050 [101] - busy (42)
        00ae4280: 00050 . 00068 [101] - busy (60)
        00ae42e8: 00068 . 001d8 [101] - busy (1cc)
        00ae44c0: 001d8 . 00050 [101] - busy (45)
        00ae4510: 00050 . 00020 [100]
        00ae4530: 00020 . 00068 [101] - busy (60)
        00ae4598: 00068 . 001d8 [101] - busy (1cc)
        00ae4770: 001d8 . 00018 [101] - busy (10)
        00ae4788: 00018 . 00040 [100]
        00ae47c8: 00040 . 00018 [101] - busy (10)
        00ae47e0: 00018 . 00018 [101] - busy (10)
        00ae47f8: 00018 . 00010 [101] - busy (2)
        00ae4808: 00010 . 00050 [101] - busy (42)
        00ae4858: 00050 . 00050 [101] - busy (44)
        00ae48a8: 00050 . 000b8 [101] - busy (a4)
        00ae4960: 000b8 . 002f8 [101] - busy (2ee)
        00ae4c58: 002f8 . 00058 [101] - busy (4a)
        00ae4cb0: 00058 . 00068 [101] - busy (60)
        00ae4d18: 00068 . 001d8 [101] - busy (1cc)
        00ae4ef0: 001d8 . 00090 [101] - busy (84)
        00ae4f80: 00090 . 00030 [101] - busy (28)
        00ae4fb0: 00030 . 006d0 [101] - busy (6c8)
        00ae5680: 006d0 . 00e08 [101] - busy (e00)
        00ae6488: 00e08 . 00228 [101] - busy (220)
        00ae66b0: 00228 . 00228 [101] - busy (220)
        00ae68d8: 00228 . 00020 [101] - busy (17)
        00ae68f8: 00020 . 00018 [101] - busy (e)
        00ae6910: 00018 . 00100 [101] - busy (f8)
        00ae6a10: 00100 . 00048 [101] - busy (3e)
        00ae6a58: 00048 . 00028 [101] - busy (1b)
        00ae6a80: 00028 . 00028 [101] - busy (1d)
        00ae6aa8: 00028 . 00050 [101] - busy (48)
        00ae6af8: 00050 . 00020 [101] - busy (12)
        00ae6b18: 00020 . 00020 [101] - busy (18)
        00ae6b38: 00020 . 00028 [101] - busy (1b)
        00ae6b60: 00028 . 00030 [101] - busy (24)
        00ae6b90: 00030 . 00038 [101] - busy (29)
        00ae6bc8: 00038 . 00028 [101] - busy (1e)
        00ae6bf0: 00028 . 00078 [101] - busy (6b)
        00ae6c68: 00078 . 00020 [101] - busy (17)
        00ae6c88: 00020 . 00048 [101] - busy (39)
        00ae6cd0: 00048 . 00020 [101] - busy (14)
        00ae6cf0: 00020 . 00018 [101] - busy (f)
        00ae6d08: 00018 . 00020 [101] - busy (16)
        00ae6d28: 00020 . 00038 [101] - busy (2f)
        00ae6d60: 00038 . 00038 [101] - busy (2e)
        00ae6d98: 00038 . 00028 [101] - busy (1b)
        00ae6dc0: 00028 . 00038 [101] - busy (2a)
        00ae6df8: 00038 . 00020 [101] - busy (17)
        00ae6e18: 00020 . 00030 [101] - busy (23)
        00ae6e48: 00030 . 00020 [101] - busy (12)
        00ae6e68: 00020 . 00808 [101] - busy (800)
        00ae7670: 00808 . 00088 [101] - busy (80)
        00ae76f8: 00088 . 00028 [101] - busy (20)
        00ae7720: 00028 . 00020 [101] - busy (18)
        00ae7740: 00020 . 00010 [101] - busy (8)
        00ae7750: 00010 . 000e8 [101] - busy (e0)
        00ae7838: 000e8 . 000b8 [100]
        00ae78f0: 000b8 . 00208 [101] - busy (200)
        00ae7af8: 00208 . 000c0 [101] - busy (b8)
        00ae7bb8: 000c0 . 00010 [101] - busy (8)
        00ae7bc8: 00010 . 00010 [101] - busy (8)
        00ae7bd8: 00010 . 00010 [101] - busy (8)
        00ae7be8: 00010 . 00010 [101] - busy (8)
        00ae7bf8: 00010 . 00010 [101] - busy (8)
        00ae7c08: 00010 . 00010 [101] - busy (8)
        00ae7c18: 00010 . 00010 [101] - busy (8)
        00ae7c28: 00010 . 00010 [101] - busy (8)
        00ae7c38: 00010 . 00050 [101] - busy (44)
        00ae7c88: 00050 . 00050 [101] - busy (44)
        00ae7cd8: 00050 . 00020 [100]
        00ae7cf8: 00020 . 00028 [101] - busy (1f)
        00ae7d20: 00028 . 00038 [101] - busy (2f)
        00ae7d58: 00038 . 00040 [101] - busy (37)
        00ae7d98: 00040 . 00048 [101] - busy (3c)
        00ae7de0: 00048 . 00040 [101] - busy (31)
        00ae7e20: 00040 . 00028 [101] - busy (1d)
        00ae7e48: 00028 . 00030 [101] - busy (24)
        00ae7e78: 00030 . 00040 [101] - busy (32)
        00ae7eb8: 00040 . 00040 [101] - busy (31)
        00ae7ef8: 00040 . 00030 [101] - busy (28)
        00ae7f28: 00030 . 00018 [101] - busy (d)
        00ae7f40: 00018 . 00028 [101] - busy (1e)
        00ae7f68: 00028 . 00040 [101] - busy (32)
        00ae7fa8: 00040 . 00028 [101] - busy (1e)
        00ae7fd0: 00028 . 00010 [101] - busy (8)
        00ae7fe0: 00010 . 01010 [101] - busy (100f) Internal 
        00ae8ff0: 01010 . 01f20 [101] - busy (1f1f) Internal 
        00aeaf10: 01f20 . 00208 [101] - busy (200)
        00aeb118: 00208 . 00208 [101] - busy (200) Internal 
        00aeb320: 00208 . 00400 [101] - busy (3f8) Internal 
        00aeb720: 00400 . 00208 [101] - busy (200)
        00aeb928: 00208 . 00448 [101] - busy (440)
        00aebd70: 00448 . 01008 [101] - busy (1000)
        00aecd78: 01008 . 01008 [101] - busy (1000)
        00aedd80: 01008 . 00260 [100]
        00aedfe0: 00260 . 00020 [111] - busy (1d)
        00aee000:      000f1000      - uncommitted bytes.

已经被是用的内存块
Heap entries for Segment00 in Heap 00ae0000
address: psize . size flags state (requested size)
00ae0000: 00000 . 004a8 [101] - busy (4a7)
00ae04a8: 004a8 . 00108 [101] - busy (100)
00ae05b0: 00108 . 000d8 [101] - busy (d0)

00ae04a8: 004a8 . 00108 [101] - busy (100)
00ae04a8 表示内存块的起始地址
004a8 前一个内存块所占用的大小
00108 这个内存块的总大小
[101] 标志位
busy (100) busy 这块内存是被占用的 申请的内存为0x100

计算下一个内存块
每个HEAP_ENTRY和它对应的内存我们可以称为一个内存块,计算下一个内存块需要用到现有内存块中的2个字段,Size和 UnsedBytes,Size的值乘上粒度(就是0:000> !heap -a 2c0000命令显示的信息中的Granularity: 8 bytes字段,这里是8字节),下一个内存块地址就是 本内存块地址+Size*8+UnsedBytes。当然这里的粒度可以通过HEAP字段中的AlignMask 字段算出来。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值