周六,不想去加班了,在家边看录像边继续学习堆。工程上的dmp文件提示堆校验问题,前天学习后用windbg输出了一些内容,还是不太够,今天继续学习堆知识,看看有没有其他思路。总之内存堆被破坏,是很难找到问题的。
windows的内存管理
内存管理器:是操作系统内核的一部分。管理内容是在物理内存中还是被虚拟到磁盘中。
内核池分为分页池和非分页池,供驱动程序选择。非分页池分的一定在物理内存,分页池里面分的内存可能在物理内存,也可能被交换到磁盘上了。
Win32堆是内核的内存管理在用户态的一个零售机构,CRT堆是C的运行时提供的堆管理器。
CRT堆何时时用Win32堆?首先判断分配的堆块大小,如果比较小(小于1000个字节),就直接使用低碎片堆策略分配,如果超过1000字节,就调用win32堆。
应用程序也可以直接使用Virtual API分配内存,比如VirtualAlloc,对程序员有较高要求,一般不要用。大部分还是使用new,malloc等CRT函数,用CRT堆分配。
图中绿色的是堆管理器的示例。每个进程有一个缺省堆,这是进程内的第一个win32堆,是系统代码初始化进程时创建的第一个堆。应用程序运行中会创建更多的堆,这叫应用程序的私有堆。C运行时还创建CRT堆,可能有多个。
NTDLL.DLL是一个很特殊的模块,是内核派驻到用户态的一个代表,每个进程都有这个DLL的示例,这个DLL中提供服务也是恰当的。HeapCreate,HeapAlloc,HeapFree用于用户创建堆。
DEMO:通过windbg查看一个程序的堆
DEMO:c:\dgblabs\bin\debug\FreCheck.exe --这个程序哪里去找?
其实看着很熟悉,是软件调试那本书里面的代码。第23章 堆和堆检查。从高端调试网站可以下载
Debugging Wars
下载卷2的实验材料,解压就可以了。下面开始做实验:
windbg打开FreCheck.exe这个小程序,程序停留在进程初始化的早期:
这时可以!heap检查堆
0:000> !heap
Failed to read heap keySEGMENT HEAP ERROR: failed to initialize the extention
NtGlobalFlag enables following debugging aids for new heaps: tail checking
free checking
validate parameters
Index Address Name Debugging options enabled
1: 006d0000 tail checking free checking validate parameters
启用的调试支持可以使用!gflag观察
0:000> !gflag
Current NtGlobalFlag contents: 0x00000070
htc - Enable heap tail checking
hfc - Enable heap free checking
hpc - Enable heap parameter checking
也可以在进程环境块中看到堆信息
0:000> !peb
PEB at 002b6000
InheritedAddressSpace: No
ReadImageFileExecOptions: No
BeingDebugged: Yes
ImageBaseAddress: 00400000
NtGlobalFlag: 70
NtGlobalFlag2: 0
Ldr 77d3eb20
Ldr.Initialized: Yes
Ldr.InInitializationOrderModuleList: 006d4450 . 006d4ba0
Ldr.InLoadOrderModuleList: 006d4558 . 006e3508
Ldr.InMemoryOrderModuleList: 006d4560 . 006e3510
Base TimeStamp Module
400000 459fad77 Jan 06 22:08:55 2007 C:\BaiduNetdiskDownload\sdbgv2\bin\Debug\FreCheck.exe
77c10000 C:\Windows\SYSTEM32\ntdll.dll
77130000 C:\Windows\System32\KERNEL32.DLL
75860000 C:\Windows\System32\KERNELBASE.dll
71fa0000 32b1ab3a Dec 14 03:15:06 1996 C:\Windows\SYSTEM32\apphelp.dll
6f740000 627c60ba May 12 09:19:54 2022 C:\Windows\System32\ghijt32win10.dll
77670000 04eaefda Aug 13 06:14:50 1972 C:\Windows\System32\ADVAPI32.dll
75f00000 C:\Windows\System32\msvcrt.dll
761e0000 03443ef9 Sep 27 15:23:37 1971 C:\Windows\System32\sechost.dll
777b0000 C:\Windows\System32\bcrypt.dll
776f0000 33dc7745 Jul 28 18:41:09 1997 C:\Windows\System32\RPCRT4.dll
13140000 654af1ff Nov 08 10:27:11 2023 C:\Windows\SYSTEM32\winhafnt.dll
774c0000 30bb6904 Nov 29 03:54:44 1995 C:\Windows\System32\USER32.dll
77440000 C:\Windows\System32\win32u.dll
77820000 C:\Windows\System32\GDI32.dll
760f0000 7f460c3c Aug 31 10:04:44 2037 C:\Windows\System32\gdi32full.dll
773c0000 3d06f74d Jun 12 15:25:01 2002 C:\Windows\System32\msvcp_win.dll
779d0000 C:\Windows\System32\ucrtbase.dll
74fb0000 1a626adb Jan 11 16:24:59 1984 C:\Windows\SYSTEM32\VERSION.dll
SubSystemData: 00000000
ProcessHeap: 006d0000
ProcessParameters: 006d2540
CurrentDirectory: 'C:\Program Files (x86)\Windows Kits\10\Debuggers\'
WindowTitle: 'C:\BaiduNetdiskDownload\sdbgv2\bin\Debug\FreCheck.exe'
ImageFile: 'C:\BaiduNetdiskDownload\sdbgv2\bin\Debug\FreCheck.exe'
CommandLine: 'C:\BaiduNetdiskDownload\sdbgv2\bin\Debug\FreCheck.exe'
这个唯一堆就是进程的默认堆。继续用!heap观察这个堆,堆的句柄值就是这个堆的基地址。
0:000> !heap 006d0000
Failed to read heap keySEGMENT HEAP ERROR: failed to initialize the extention
Index Address Name Debugging options enabled
1: 006d0000
Segment at 006d0000 to 007cf000 (00014000 bytes committed)
0:000> dt 6d0000 _HEAP --第一个段放的_HEAP结构
ntdll!_HEAP
+0x000 Segment : _HEAP_SEGMENT
+0x000 Entry : _HEAP_ENTRY
+0x008 SegmentSignature : 0xffeeffee
+0x00c SegmentFlags : 2
+0x010 SegmentListEntry : _LIST_ENTRY [ 0x6d00a4 - 0x6d00a4 ]
+0x018 Heap : 0x006d0000 _HEAP
+0x01c BaseAddress : 0x006d0000 Void
+0x020 NumberOfPages : 0xff
+0x024 FirstEntry : 0x006d04a8 _HEAP_ENTRY
+0x028 LastValidEntry : 0x007cf000 _HEAP_ENTRY
+0x02c NumberOfUnCommittedPages : 0xeb
+0x030 NumberOfUnCommittedRanges : 1
+0x034 SegmentAllocatorBackTraceIndex : 0
+0x036 Reserved : 0
+0x038 UCRSegmentList : _LIST_ENTRY [ 0x6e3ff0 - 0x6e3ff0 ]
+0x040 Flags : 0x40000062
+0x044 ForceFlags : 0x40000060
+0x048 CompatibilityFlags : 0x20000000
+0x04c EncodeFlagMask : 0x100000
+0x050 Encoding : _HEAP_ENTRY
+0x058 Interceptor : 0
+0x05c VirtualMemoryThreshold : 0xfe00
+0x060 Signature : 0xeeffeeff
+0x064 SegmentReserve : 0x100000
+0x068 SegmentCommit : 0x2000
+0x06c DeCommitFreeBlockThreshold : 0x200
+0x070 DeCommitTotalFreeThreshold : 0x2000
+0x074 TotalFreeSize : 0x154
+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 [ 0x6e3fe8 - 0x6e3fe8 ]
+0x094 AlignRound : 0x17
+0x098 AlignMask : 0xfffffff8
+0x09c VirtualAllocdBlocks : _LIST_ENTRY [ 0x6d009c - 0x6d009c ]
+0x0a4 SegmentList : _LIST_ENTRY [ 0x6d0010 - 0x6d0010 ]
+0x0ac AllocatorBackTraceIndex : 0
+0x0b0 NonDedicatedListLength : 0
+0x0b4 BlocksIndex : 0x006d0270 Void
+0x0b8 UCRIndex : (null)
+0x0bc PseudoTagEntries : (null)
+0x0c0 FreeLists : _LIST_ENTRY [ 0x6da258 - 0x6e3780 ]
+0x0c8 LockVariable : 0x006d0258 _HEAP_LOCK
+0x0cc CommitRoutine : 0x88a148a2 long +ffffffff88a148a2
+0x0d0 StackTraceInitVar : _RTL_RUN_ONCE
+0x0d4 CommitLimitData : _RTL_HEAP_MEMORY_LIMIT_DATA
+0x0e4 FrontEndHeap : (null)
+0x0e8 FrontHeapLockCount : 0
+0x0ea FrontEndHeapType : 0 ''
+0x0eb RequestedFrontEndHeapType : 0 ''
+0x0ec FrontEndHeapUsageData : 0x006d04b0 -> 0
+0x0f0 FrontEndHeapMaximumIndex : 0x80
+0x0f2 FrontEndHeapStatusBitmap : [257] ""
+0x1f4 Counters : _HEAP_COUNTERS
+0x250 TuningParameters : _HEAP_TUNING_PARAMETERS
还可以显示堆的相信信息
0:000> !heap 006d0000 -a //-a显示详细信息
Failed to read heap keySEGMENT HEAP ERROR: failed to initialize the extention
Index Address Name Debugging options enabled
1: 006d0000
Segment at 006d0000 to 007cf000 (00014000 bytes committed)
Flags: 40000062
ForceFlags: 40000060
Granularity: 8 bytes --分配粒度是8字节,32位栈是4字节,注意不同
Segment Reserve: 00100000
Segment Commit: 00002000
DeCommit Block Thres: 00000200
DeCommit Total Thres: 00002000
Total Free Size: 00000154
Max. Allocation Size: 7ffdefff
Lock Variable at: 006d0258
Next TagIndex: 0000
Maximum TagIndex: 0000
Tag Entries: 00000000
PsuedoTag Entries: 00000000
Virtual Alloc List: 006d009c
Uncommitted ranges: 006d008c
006e4000: 000eb000 (962560 bytes)
FreeList[ 00 ] at 006d00c0: 006e3780 . 006da258
006da250: 00028 . 00010 [104] - free
006d4c58: 000d0 . 00010 [104] - free
006dfa00: 00028 . 00018 [104] - free
006dcae0: 00040 . 00018 [104] - free
006d4930: 00030 . 00018 [104] - free
006e1e58: 00048 . 00030 [104] - free
006e2c30: 000d0 . 00048 [104] - free
006e2950: 000d0 . 00048 [104] - free
006e36d8: 00028 . 00048 [104] - free
006e2a40: 00050 . 00050 [104] - free
006e3398: 00028 . 00078 [104] - free
006e3778: 00058 . 00868 [104] - free
Segment00 at 006d0000:
Flags: 00000000
Base: 006d0000
First Entry: 006d04a8
Last Entry: 007cf000
Total Pages: 000000ff
Total UnCommit: 000000eb
Largest UnCommit:00000000
UnCommitted Ranges: (1)
Heap entries for Segment00 in Heap 006d0000 --堆块,堆上的基本单位。每块开头都是heap entry 结构。
address: psize . size flags state (requested size)
006d0000: 00000 . 004a8 [101] - busy (4a7)
006d04a8: 004a8 . 00118 [107] - busy (117), tail fill Internal
006d05c0: 00118 . 000e8 [107] - busy (d0), tail fill
006d06a8: 000e8 . 00090 [107] - busy (78), tail fill
006d0738: 00090 . 00140 [107] - busy (128), tail fill
006d0878: 00140 . 00138 [107] - busy (120), tail fill
006d09b0: 00138 . 00040 [107] - busy (24), tail fill
006d09f0: 00040 . 00020 [107] - busy (4), tail fill
006d0a10: 00020 . 00028 [107] - busy (c), tail fill
006d0a38: 00028 . 000e8 [107] - busy (d0), tail fill
006d0b20: 000e8 . 000e8 [107] - busy (d0), tail fill
006d0c08: 000e8 . 000e8 [107] - busy (d0), tail fill
006d0cf0: 000e8 . 017b0 [107] - busy (1792), tail fill
006d24a0: 017b0 . 00098 [107] - busy (80), tail fill
006d2538: 00098 . 01de0 [107] - busy (1dc4), tail fill
006d4318: 01de0 . 00058 [107] - busy (3c), tail fill
006d4370: 00058 . 00048 [107] - busy (30), tail fill
006d43b8: 00048 . 00080 [107] - busy (62), tail fill
006d4438: 00080 . 000d0 [107] - busy (b8), tail fill
006d4508: 000d0 . 00048 [107] - busy (2c), tail fill
006d4550: 00048 . 000d0 [107] - busy (b8), tail fill
006d4620: 000d0 . 00048 [107] - busy (2c), tail fill
006d4668: 00048 . 00238 [107] - busy (220), tail fill
006d48a0: 00238 . 00030 [107] - busy (18), tail fill
006d48d0: 00030 . 00030 [107] - busy (18), tail fill
006d4900: 00030 . 00030 [107] - busy (18), tail fill
006d4930: 00030 . 00018 [104] free fill
006d4948: 00018 . 000d0 [107] - busy (b8), tail fill
006d4a18: 000d0 . 00048 [107] - busy (2c), tail fill
006d4a60: 00048 . 00060 [107] - busy (42), tail fill
006d4ac0: 00060 . 00030 [107] - busy (18), tail fill
006d4af0: 00030 . 00030 [107] - busy (18), tail fill
006d4b20: 00030 . 00030 [107] - busy (18), tail fill
006d4b50: 00030 . 00038 [107] - busy (18), tail fill
006d4b88: 00038 . 000d0 [107] - busy (b8), tail fill
006d4c58: 000d0 . 00010 [104] free fill
006d4c68: 00010 . 00028 [107] - busy (10), tail fill
006d4c90: 00028 . 00058 [107] - busy (40), tail fill
006d4ce8: 00058 . 00030 [107] - busy (18), tail fill
006d4d18: 00030 . 00020 [107] - busy (8), tail fill
006d4d38: 00020 . 000d0 [107] - busy (b8), tail fill
006d4e08: 000d0 . 00048 [107] - busy (2c), tail fill
006d4e50: 00048 . 00060 [107] - busy (46), tail fill
......
006e3640: 00028 . 00070 [107] - busy (54), tail fill
006e36b0: 00070 . 00028 [107] - busy (10), tail fill
006e36d8: 00028 . 00048 [104] free fill
006e3720: 00048 . 00058 [107] - busy (40), tail fill
006e3778: 00058 . 00868 [104] free fill
006e3fe0: 00868 . 00020 [111] - busy (1d)
006e4000: 000eb000 - uncommitted bytes.
找heap entry的基本描述
0:000> !heap -x 006d0000
Failed to read heap keySEGMENT HEAP ERROR: failed to initialize the extention
Entry User Heap Segment Size PrevSize Unused Flags
-----------------------------------------------------------------------------
006d0000 006d0008 006d0000 006d0000 4a8 0 1 busy
0:000> !heap -x 006d0000+4a8 --entry+size就是下一个堆块的entry
Failed to read heap keySEGMENT HEAP ERROR: failed to initialize the extention
Entry User Heap Segment Size PrevSize Unused Flags
-----------------------------------------------------------------------------
006d04a8 006d04b0 006d0000 006d0000 118 4a8 1 busy extra fill internal
著名的_HEAP_ENTRY结构体
0:000> dt _HEAP_ENTRY --录像介绍共8个字节,我的windows比较新,长度增加了。
ntdll!_HEAP_ENTRY
+0x000 UnpackedEntry : _HEAP_UNPACKED_ENTRY
+0x000 Size : Uint2B --这时最重要内容,当前块大小
+0x002 Flags : UChar
+0x003 SmallTagIndex : UChar
+0x000 SubSegmentCode : Uint4B
+0x004 PreviousSize : Uint2B--前面块的大小,16bit,size最大值65535
+0x006 SegmentOffset : UChar
+0x006 LFHFlags : UChar
+0x007 UnusedBytes : UChar
+0x000 ExtendedEntry : _HEAP_EXTENDED_ENTRY
+0x000 FunctionIndex : Uint2B
+0x002 ContextValue : Uint2B
+0x000 InterceptorValue : Uint4B
+0x004 UnusedBytesLength : Uint2B
+0x006 EntryOffset : UChar
+0x007 ExtendedBlockSignature : UChar
+0x000 Code1 : Uint4B
+0x004 Code2 : Uint2B
+0x006 Code3 : UChar
+0x007 Code4 : UChar
+0x004 Code234 : Uint4B
+0x000 AgregateCode : Uint8B
size有限制,所以一个堆块只能new几百K,如果超过几百K,就需要使用大虚拟块分配,如图:
大的虚拟块在如下结构中定义:
0:000> dt _HEAP_VIRTUAL_ALLOC_ENTRY
ntdll!_HEAP_VIRTUAL_ALLOC_ENTRY
+0x000 Entry : _LIST_ENTRY
+0x008 ExtraStuff : _HEAP_ENTRY_EXTRA
+0x010 CommitSize : Uint4B
+0x014 ReserveSize : Uint4B
+0x018 BusyBlock : _HEAP_ENTRY
还有个空闲块定义:
0:000> dt _HEAP_FREE_ENTRY --前8个字节也是_HEAP_ENTRY,释放时前面字节不修改,只是放到FreeList中,可以提高性能,下次分配还能找到这个块,如果大小匹配,直接用。
ntdll!_HEAP_FREE_ENTRY
+0x000 HeapEntry : _HEAP_ENTRY
+0x000 UnpackedEntry : _HEAP_UNPACKED_ENTRY
+0x000 Size : Uint2B
+0x002 Flags : UChar
+0x003 SmallTagIndex : UChar
+0x000 SubSegmentCode : Uint4B
+0x004 PreviousSize : Uint2B
+0x006 SegmentOffset : UChar
+0x006 LFHFlags : UChar
+0x007 UnusedBytes : UChar
+0x000 ExtendedEntry : _HEAP_EXTENDED_ENTRY
+0x000 FunctionIndex : Uint2B
+0x002 ContextValue : Uint2B
+0x000 InterceptorValue : Uint4B
+0x004 UnusedBytesLength : Uint2B
+0x006 EntryOffset : UChar
+0x007 ExtendedBlockSignature : UChar
+0x000 Code1 : Uint4B
+0x004 Code2 : Uint2B
+0x006 Code3 : UChar
+0x007 Code4 : UChar
+0x004 Code234 : Uint4B
+0x000 AgregateCode : Uint8B
+0x008 FreeList : _LIST_ENTRY