软件调试纵横谈-16-堆概要

16.堆概要_哔哩哔哩_bilibili  

周六,不想去加班了,在家边看录像边继续学习堆。工程上的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


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值