堆(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 存储所有堆地址的数组
只有当本次释放时
-
本次释放的堆块大小超过了_PEB中的HeapDeCommitFreeBlockThreshold字段的值。
-
空闲空间的总大小超过了_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 字段算出来。