WINDBG的堆调试.docx_第1页
WINDBG的堆调试.docx_第2页
WINDBG的堆调试.docx_第3页
WINDBG的堆调试.docx_第4页
WINDBG的堆调试.docx_第5页
免费预览已结束,剩余14页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

WINDBG的堆调试HEAP的概念堆栈堆栈,在操作系统内存中有两种存储空间,一个是堆,一个是栈。堆主要用于存储用户动态分配的变量,而栈呢,则是存储我们程序过程中的临时变量。当然栈的作用远不止用作存储变量,但这不是我们这篇文章的讨论内容。 堆(HEAP)的分配,使用,回收都是通过微软的API来管理的,最常见的API是malloc和new。在往底层走一点呢,这两个函数都会调用HeapAlloc(RtlAllocateHeap)。同样的相关函数还有HeapFree用来释放堆,HeapCreate用来创建自己的私有堆。下面是这些函数的调用链:HeapCreate-RtlCreateHeap-ZwAllocateVirtualMemory (这里会直接申请一大片内存,至于申请多大内存,由进程PEB结构中的字段觉得,HeapSegmentReserve字段指出要申请多大的虚拟内存,HeapSegmentCommit指明要提交多大内存,对虚拟内存的申请和提交概念不清楚的童鞋,请参见windows核心编程相关内容)HeapAlloc-RtlAllocateHeap(至于这里申请的内存,由于HeapCreate已经申请了一大片内存,堆管理器这片内存中划分一块出来以满足申请的需要。这一步申请操作是堆管理器自己维护的,仅当申请内存不够的时候才会再次调用ZwAllocateVirtualMemory )HeapFree-RtlFreeHeap (对于释放的内存,堆管理器只是简单的把这块内存标志位已释放让后加入到空闲列表中,仅当空闲的内存达到一定阀值的时候会调用ZwFreeVirtualMeMory )HeapDestroy-RtlDestroyHeap-ZwFreeVirtualMeMory (销毁我们申请的堆)如何找到我们的HEAP信息?WINDBG观察堆源码: #include windows.hint main()HANDLE heap_handle = HeapCreate( NULL , 0x1000 , 0x2000 ) ;char *buffer = (char*)HeapAlloc(heap_handle , NULL , 128) ;char *buffer1 = (char*)HeapAlloc(heap_handle , NULL , 121) ;HeapFree(heap_handle, 0 , buffer ) ;HeapFree(heap_handle, 0 , buffer1 ) ;HeapDestroy( heap_handle) ;return 0 ;该源码生成编译生成heap.exe,然后用windbg调试这个程序,在main函数下断,紧接着执行第五行语句,执行结果如下0:000 peax=002e1ca0 ebx=00000000 ecx=6d29b6f0 edx=00000000 esi=00000001 edi=01033374eip=01031012 esp=0022fe8c ebp=0022feac iopl=0 nv up ei pl nz na po nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202heap!main+0x12:01031012 ff150c200301 call dword ptr heap!_imp_HeapCreate (0103200c) ds:0023:0103200c=kernel32!HeapCreateStub (769a29d7)0:000 peax=002c0000 ebx=00000000 ecx=77429897 edx=77498500 esi=00000001 edi=01033374eip=01031018 esp=0022fe98 ebp=0022feac iopl=0 nv up ei pl nz na pe nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206heap!main+0x18:01031018 8945fc mov dword ptr ebp-4,eax ss:0023:0022fea8=6d2222010:000 !heap Index Address Name Debugging options enabled 1: 00300000 2: 00010000 3: 00020000 4: 002e0000 5: 002c0000 HeapCreate执行的返回值存放在eax处,这个函数返回了一个堆句柄:0x002c0000。用!heap命令查看可以看到第五个堆就是我们创建的堆句柄了。每个进程都存在多个堆,我们也可以通过PEB结构来得到进程中存在的堆,结果和!heap命令显示的内容是一样的。heap!_PEB +0x018 ProcessHeap : 0x00300000 Void ; 进程的默认堆 +0x068 NtGlobalFlag : 0 ; 这个标志位记录了当前堆调试模式,0为普通调试模式 +0x078 HeapSegmentReserve : 0x100000 ; 进程在新建堆的时候默认申请的虚拟内存大小 +0x07c HeapSegmentCommit : 0x2000 ; 进程在每次申请提交的虚拟内存大小,在提交的内存用完后,进程会又在一次提交HeapSegmentCommit中指定的内存大小 +0x080 HeapDeCommitTotalFreeThreshold : 0x10000 ; 当释放的内存大小大于这个阀值,就进行内存解除提交操作 +0x084 HeapDeCommitFreeBlockThreshold : 0x1000 ; 当一次性释放的块大小超过这个阀值,就进行内存解除提交操作,只有当满足这两个条件时才会调用ZwFreeVirtualMeMory 释放物理内存 +0x088 NumberOfHeaps : 5 ; 当前进程的堆数目,这个数目对应着!heap命令的堆显示个数 +0x08c MaximumNumberOfHeaps : 0x10 ; 进程所能运行的最大堆数目,若堆数目超过这个值估计HeapCreate就失败了吧 +0x090 ProcessHeaps : 0x77498500 - 0x00300000 Void ;存储堆句柄的数组,这里我们可以得到进程的所有堆句柄我们可以输入如下命令来查看现有的堆句柄0:000 dd 0x77498500 77498500 00300000 00010000 00020000 002e000077498510 002c0000 00000000 00000000 0000000077498520 00000000 00000000 00000000 0000000077498530 00000000 00000000 00000000 0000000077498540 00000000 77498340 7749bb08 7749822077498550 00000000 00000000 00000000 0000000077498560 77498220 00317bd0 00000000 0000000077498570 00000000 00000000 00000000 00000000可以看得到这里面的内容和!heap命令的输出结果是一样的而堆句柄的存放范围,从MaximumNumberOfHeaps 上来看,就是77498500-77498540这0x40个字节,因为每个堆句柄占4个字节,0x10个堆句柄的存放空间就是0x40。HEAP的组织结构堆的管理,我们可以理解为一个内存池,它申请一大块空间,然后负责接管应用程序的申请释放等请求。只有在创建堆,释放堆(注意!是释放堆,不是堆中的空间!)在这之前,我们需要对堆有关的数据结构做一些解释我这里观察到的HEAP结构,HEAP_SEGMENT结构和HEAP_ENTRY结构都和软件调试里面描述的不一样,当年奎哥写软件调试的时候估计还没用上WIN7吧。我的演示系统是WIN7HeapCreate函数返回的堆句柄其实就是一个指向堆管理结构的指针,每个堆都会涉及到这样三个结构:HEAP,HEAP_SEGMENT,HEAP_ENTRYHEAP_ENTRY结构:在堆管理中,每一块申请下来的内存都会有下面所示的固定模式:HEAP_ENTRY(8 bytes)我们new或malloc分配的空间固定填充空间这个结构用来记录所分配的空间的信息,包括用户申请的空间,填充的空间,所在的段号等等信息。所以我们new或者malloc的地址减去8就指向该结构。第三部分的固定填充空间是为了内存对齐而生成的,当然这部分空间还有一部分是用来额外记录这块内存的其它信息,这里就不详细做介绍了。HEAP_SEGMENT结构:我们可以这么认为,堆申请内存的大小是以段为单位的,当新建一个堆的时候,系统会默认为这个堆分配一个段叫0号段,通过刚开始的new和malloc分配的空间都是在这个段上分配的,当这个段用完的时候,如果当初创建堆的时候指明了HEAP_GROWABLE这个标志,那么系统会为这个堆在再分配一个段,这个时候新分配的段就称为1号段了,以下以此类推。每个段的开始初便是HEAP_SEGMENT结构的首地址,由于这个结构也是申请的一块内存,所以它前面也会有个HEAP_ENTRY结构:HEAP_ENTRY(8 bytes)HEAP_SEGMENTHEAP_ENTRY(8 bytes)我们new或malloc分配的空间固定填充空间HEAP_SEGMENT结构会记录段的一些基本信息,该段申请的大小,已经提交内存的大小,第一个HEAP_ENTRY结构的入口点。(我观察看貌似段申请的内存并不会一次性全部提交,而是每次提交一个页的大小,比如一个段大小2个页,那么它会先提交一个页内存,若用完了再提交一个页的内存,若内存还用完了那就新建一个段,这个新建的段也会是先提交一个页内存。)但是0号段很特别,这个段的起始地址就是堆句柄指针指向的值,也就是说,HeapCreate返回的堆句柄总是指向0号段,为什么呢?因为HEAP结构是HEAP_ENTRY,HEAP_SEGMENT的合体加长版HEAP结构:HEAP结构则是记录了这个堆的信息,这个结构可以找到HEAP_SEGMENT链表入口,空闲内存链表的入口,内存分配粒度等等信息。HEAP的首地址便是堆句柄的值,但是堆句柄的值又是0号段的首地址也是堆句柄,何解?其实很简单,0号段的HEAP_SEGMENT就在HEAP结构里面,HEAP结构类定义如这样: struct _HEAP_HEAP_ENTRY Entry ; /HEAP_ENTRY结构,用来描述存储HEAP内存块大小等信息的_HEAP_SEGMENT Segment ; /0号段的首地址 /对于该HEAP的描述信息 ;在我们看来,内存组织结构应该如下所示:HEAP_ENTRY(8 bytes)HEAP_SEGMENTHEAP更确切的说,HEAP结构中本身就包含了HEAP_ENTRY和HEAP_SEGMENT,HEAP_ENTRY结构是HEAP的第一个数据成员,HEAP_SEGMENT是它第二个数据成员。而对于HEAP_SEGMENT,它的第一个数据成员便是HEAP_ENTRY。这里为了方便理解,才在内存组织结构中把它们拆开展示。(注:这里是win7的情况,和软件调试这本书中所描述的有一些差异,也属正常现象,毕竟这部分结构微软并未公开)用WINDBG观察HEAP结构在之前已经演示了如何从PEB结构中找到所有的堆句柄,可以看到002c0000便是我们创建的句柄。然后我们执示例程序的第7行代码。执行完后结果如下:0:000 peax=002c0000 ebx=00000000 ecx=77429897 edx=77498500 esi=00000001 edi=01033374eip=01031026 esp=0022fe8c ebp=0022feac iopl=0 nv up ei pl nz na pe nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206heap!main+0x26:01031026 ff1500200301 call dword ptr heap!_imp_HeapAlloc (01032000) ds:0023:01032000=ntdll!RtlAllocateHeap (774120b5)0:000 peax=002c0590 ebx=00000000 ecx=774134b4 edx=002c0180 esi=00000001 edi=01033374eip=0103102c esp=0022fe98 ebp=0022feac iopl=0 nv up ei pl zr na pe nccs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246heap!main+0x2c:0103102c 8945f0 mov dword ptr ebp-10h,eax ss:0023:0022fe9c=heap!envp (0103301c)可以看到EAX保存的返回值为002c0590。我们通过两种途径来观察我们申请的内存,通过!heap命令观察和通过dt命令观察通过!heap命令观察输入命令!heap a 2c0590得到的结果如下:0:000 !heap -a 2c0000Index Address Name Debugging options enabled 5: 002c0000 Segment at 002c0000 to 002c2000 (00001000 bytes committed) Flags: 00001000 ForceFlags: 00000000 Granularity: 8 bytes Segment Reserve: 00100000 Segment Commit: 00002000 DeCommit Block Thres: 00000200 DeCommit Total Thres: 00002000 Total Free Size: 0000013a Max. Allocation Size: 7ffdefff Lock Variable at: 002c0138 Next TagIndex: 0000 Maximum TagIndex: 0000 Tag Entries: 00000000 PsuedoTag Entries: 00000000 Virtual Alloc List: 002c00a0 Uncommitted ranges: 002c0090 002c1000: 00001000 (4096 bytes) FreeList 00 at 002c00c4: 002c0618 . 002c0618 002c0610: 00088 . 009d0 100 - free Segment00 at 002c0000: Flags: 00000000 Base: 002c0000 First Entry: 002c0588 Last Entry: 002c2000 Total Pages: 00000002 Total UnCommit: 00000001 Largest UnCommit:00000000 UnCommitted Ranges: (1) Heap entries for Segment00 in Heap 002c0000 002c0000: 00000 . 00588 101 - busy (587) 002c0588: 00588 . 00088 101 - busy (80) 002c0610: 00088 . 009d0 100 002c0fe0: 009d0 . 00020 111 - busy (1d) 002c1000: 00001000 - uncommitted bytes.这个命令分别提炼出了HEAP(绿色区域),HEAP_SEGMENT(红色区域)和HEAP_ENTRY(灰色区域)结构中的信息。虽然在灰色区域中,我们找不到2c0590,但是找到了一个2c0588,这个正是2c0590-8的结果,也就是说最右边的地址是每个HEAP_ENTRY的首地址,接着00588这个字段表示了前面一个HEAP_ENTRY所占用的大小,后面的0088表示这个内存块的总大小,即我们申请的内存+HEAP_ENTRY(128+8=0x80+0x8=0x88),101是这块内存的标志位,最右边一位为1表示该内存块被占用。然后busy(80)就是解释说这块内存是被占用的(非空闲的),它申请的内存为0x80,转化成十进制正好就是我们申请的128字节大小。但是这里用dt _HEAP_ENTRY 2c0588命令却没办法查看对应的结构信息,真是怪哉,有篇博文也提到win2008中HEAP相关结构也有变,看来到NT6后,HEAP结构变得不小,起码windbg中直接dt HEAP_ENTRY是无法原始数据的了,貌似对HEAP_ENTRY做了编码。 通过dt命令观察同样的,已知HEAP的首地址,那么先从HEAP下手好了,dt _HEAP 002c0000可以显示HEAP的数据结构ntdll!_HEAP +0x000 Entry : _HEAP_ENTRY +0x008 SegmentSignature : 0xffeeffee +0x00c SegmentFlags : 0 +0x010 SegmentListEntry : _LIST_ENTRY 0x2c00a8 - 0x2c00a8 +0x018 Heap : 0x002c0000 _HEAP +0x01c BaseAddress : 0x002c0000 Void +0x020 NumberOfPages : 2 +0x024 FirstEntry : 0x002c0588 _HEAP_ENTRY +0x028 LastValidEntry : 0x002c2000 _HEAP_ENTRY +0x02c NumberOfUnCommittedPages : 1 +0x030 NumberOfUnCommittedRanges : 1 +0x034 SegmentAllocatorBackTraceIndex : 0 +0x036 Reserved : 0 +0x038 UCRSegmentList : _LIST_ENTRY 0x2c0ff0 - 0x2c0ff0 +0x040 Flags : 0x1000 +0x044 ForceFlags : 0 +0x048 CompatibilityFlags : 0 +0x04c EncodeFlagMask : 0x100000 +0x050 Encoding : _HEAP_ENTRY +0x058 PointerKey : 0x17c06e63 +0x05c Interceptor : 0 +0x060 VirtualMemoryThreshold : 0xfe00 +0x064 Signature : 0xeeffeeff +0x068 SegmentReserve : 0x100000 +0x06c SegmentCommit : 0x2000 +0x070 DeCommitFreeBlockThreshold : 0x200 +0x074 DeCommitTotalFreeThreshold : 0x2000 +0x078 TotalFreeSize : 0x13a +0x07c MaximumAllocationSize : 0x7ffdefff +0x080 ProcessHeapsListIndex : 5 +0x082 HeaderValidateLength : 0x138 +0x084 HeaderValidateCopy : (null) +0x088 NextAvailableTagIndex : 0 +0x08a MaximumTagIndex : 0 +0x08c TagEntries : (null) +0x090 UCRList : _LIST_ENTRY 0x2c0fe8 - 0x2c0fe8 +0x098 AlignRound : 0xf +0x09c AlignMask : 0xfffffff8 +0x0a0 VirtualAllocdBlocks : _LIST_ENTRY 0x2c00a0 - 0x2c00a0 +0x0a8 SegmentList : _LIST_ENTRY 0x2c0010 - 0x2c0010 +0x0b0 AllocatorBackTraceIndex : 0 +0x0b4 NonDedicatedListLength : 0 +0x0b8 BlocksIndex : 0x002c0150 Void +0x0bc UCRIndex : (null) +0x0c0 PseudoTagEntries : (null) +0x0c4 FreeLists : _LIST_ENTRY 0x2c0618 - 0x2c0618 +0x0cc LockVariable : 0x002c0138 _HEAP_LOCK +0x0d0 CommitRoutine : 0x17c06e63 long +17c06e63 +0x0d4 FrontEndHeap : (null) +0x0d8 FrontHeapLockCount : 0 +0x0da FrontEndHeapType : 0 +0x0dc Counters : _HEAP_COUNTERS +0x130 TuningParameters : _HEAP_TUNING_PARAMETERS就如本文前面所述的,第一个字段是HEAP_ENTRY结构,接着应该是HEAP_SEGMENT,这里只不过把HEAP_SEGMENT结构的字段展开了,可以dt _HEAP_SEGMENT来观察下这个结构的字段0:000 dt _heap_segmentntdll!_HEAP_SEGMENT +0x000 Entry : _HEAP_ENTRY +0x008 SegmentSignature : Uint4B +0x00c SegmentFlags : Uint4B +0x010 SegmentListEntry : _LIST_ENTRY +0x018 Heap : Ptr32 _HEAP +0x01c BaseAddress : Ptr32 Void +0x020 NumberOfPages : Uint4B +0x024 FirstEntry : Ptr32 _HEAP_ENTRY +0x028 LastValidEntry : Ptr32 _HEAP_ENTRY +0x02c NumberOfUnCommittedPages : Uint4B +0x030 NumberOfUnCommittedRanges : Uint4B +0x034 SegmentAllocatorBackTraceIndex : Uint2B +0x036 Reserved : Uint2B +0x038 UCRSegmentList : _LIST_ENTRY可以看到HEAP结构中灰色部分是和HEAP_SEGMENT结构中的字段是重复的,也就是说灰色部分字段便是HEAP_SEGMENT结构。在HEAP_SEGMENT结构中,我们可以找到FirstEntry字段,这里指的便是我们的分配的内存,不过HEAP_ENTRY结构无法观察,这里便没办法枚举出所有的HEAP_ENTRY结构了,但是说一下思路:每个HEAP_ENTRY和它对应的内存我们可以称为一个内存块,计算下一个内存块需要用到现有内存块中的2个字段,Size和UnsedBytes,Size的值乘上粒度(就是0:000 !heap -a 2c0000命令显示的信息中的Granularity: 8 bytes字段,这里是8字节),下一个内存块地址就是 本内存块地址+Size*8+UnsedBytes。当然这里的粒度可以通过HEAP字段中的AlignMask 字段算出来。HEAP的分配粒度在HEAP结构中指明了分配粒度,这个分配粒度是说每次堆分配的时候,都以这个粒度为最小单位,这里看到粒度为8字节。所以这里就有了第二次分配内存的实验,我们让程序执行第9行,然后用!heap -a 002c0000观察分配情况Heap entries for Segment00 in Heap 002c0000 002c0000: 00000 . 00588 101 - busy (587) 002c0588: 00588 . 00088 101 - busy (80) 002c0610: 00088 . 00088 101 - busy (79) 002c0698: 00088 . 00948 100 002c0fe0: 00948 . 00020 111 - busy (1d) 002c1000: 00001000 - uncommitted bytes.这里可以看出多出了一个占用块,大小是0x79(121) bytes,但是实际分配的大小还是0x 88 (128)bytes,这是因为系统是以8 bytes为粒度分配的,所以为这块121 bytes的内存自动填充了7个字节,可见申请121 bytes和申请128 bytes所使用的空间是一样的。HEAP的释放和销毁执行了11行和12行的代码后,堆中的内容分别如下:执行11行代码的堆情况FreeList 00 at 002c00c4: 002c06a0 . 002c0590 002c0588: 00588 . 00088 100 free ;空闲列表中多出了一块内存 002c0698: 00088 . 00948 100 free ;空闲内存,空闲空间为948Heap entries for Segment00 in Heap 002c0000002c0000: 00000 . 00588 101 - busy (587)002c0588: 00588 . 00088 100 ;原先的这块内存释放掉了002c0610: 00088 . 00088 101 - busy (79)002c0698: 00088 . 00948 100 ; 空闲内存002c0fe0: 00948 . 00020 111 - busy (1d)002c1000: 00001000 - uncommitted bytes.执行12行代码的堆情况FreeList 00 at 005c00c4: 005c0590 . 005c0590 005c0588: 00588 . 00a58 100 free ;回收了buffer1的内存后,由于由于空闲内存是连续的,所以直接合并成一块内存。可以看到之前内存free空间是948,现在合并了以后便是948+88+88=a58,也就是当前内存大小Heap entries for Segment00 in Heap 005c0000 005c0000: 00000 . 00588 101 - busy (587) 005c0588: 00588 . 00a58 100 005c0fe0: 00a58 . 00020 111 - busy (1d) 005c1000: 00001000 - uncommitted bytes.最后执行14行代码,对堆进行释放,释放后我们通过!heap也可以看到只有4个堆了,我们申请的堆被释放了.0:000 !heap Index Address Name Debugging options enabled1: 00300000 2: 00010000 3: 00020000 4: 002e0000 至于HEAP_ENTRY结构的问题,有时间在调试看看是怎么回事吧另外,这里说明下,new和malloc内部都会调用HeapAlloc来申请内存,但是堆句柄从哪来呢?它会检测_crtheap变量是否为空,若不为空则拿_crtheap变量来作为自己的堆句柄去调用HeapAlloc.此文会涉及到一些普通堆的知识,这些内容可以参见我之前的文章WINDBG的堆调试-了解HEAP组织堆破坏所谓的堆破坏,是说没控制好自己的指针,把不属于你分配的那块内存给写覆盖了。这块内存可能是你程序的数据,也可能是堆的管理结构。那么这个会导致怎样的后果呢?可能的情况我们来yy下1. 把程序里的计算结果覆盖了,这也许会让你重复看了N次代码,校验了N次计算逻辑也搞不明白为何计算结果还是有问题2. 堆管理结构被破坏了,new/delete,或者malloc/free操作失败3. 等等等等堆破坏较为理想的情况是被修改的数据会马上导致程序crash,最差的情况是你的堆数据莫名其妙在今天被改了,但明天才crash。这个时候在去分析crash,就如我们的警察叔叔现在接手一桩10年前的案子一般-无从下手。老外称之为heap corruption是很贴切的,有时候咱堆数据被意外篡改是无声无息的,你也许没法从界面甚至日志文件中看到它被篡改的一点迹象,当到某一个时刻,这种错误会暴露出来,然而这个时候查看堆信息也许会是毫无头绪。所以对于堆破坏,咱的策略是尽早发现我们的堆被篡改了,最好能够在堆数据被意外篡改的那一时刻诱发一个异常来提醒我们-兄弟,你的堆被腐蚀了。微软提供了一些方案,来帮助我们诊断堆破坏。一般来说,堆破坏往往都是写数据越界造成的(yy的第二种情况,如果是第一种情况其实还简单,下个内存断点就好),所以微软在堆分配上,给程序员门额外提供了2种堆分配模式-完全页堆(full page heap),准页堆(normal page heap),用来检测堆被写越界的情况。完全页堆(full page heap)检测原理完全页堆的检测基本思路是通过分配相邻的一个页,并将其设为不可访问属性,然后用户数据块会被分配到内存页的最末端,从而实现越界访问的检测。当我们对堆中分配的内存读写越界后便会访问到那个不可读的页,系统捕获到改次异常后会试图中断执行并将该异常上报给debugger,或者崩溃。具体的内存组织结构如下图摘自软件调试与普通堆不同的是,内存块前面的HEAP_ENTRY结构被DPH_BLOCK_INFORMATION结构取代,这个结构内部记录了页堆模式下这个内存块的一些基本信息。如果用户数据区前面的数据,也就是DPH_BLOCK_INFORMATION结构被破坏了,那么在释放内存块的时候系统会报错,如果编程者对这块内存块读写越界了,当然,这里越界有几种情况:1. 读越界,但只是访问了块尾填充部分数据,那么系统不会报错2. 写越界,但只篡改了图中块尾填充的部分,那么在堆块释放的时候会报错3. 读越界,且超过了块尾填充的部分,访问到了栅栏页,那么系统会立即抛出一个异常并中断执行4. 写越界,且超过了块尾填充部分,写到了栅栏页,那么系统会立即抛出一个异常并中断执行这里需要注意的还是块尾填充不一定存在,块尾填充是因为要满足堆内存的最小分配粒度,如果本身内存块的分配粒度就已经是最小分配粒度的倍数了,那么块尾填充就不存在了,比如堆内存分配粒度是是8 bytes,那么如果申请了14 bytes的话会有2 bytes的大徐小的块尾填充块,如果申请了24bytes,那么就没有块尾填充了,因为24正好是8的倍数。示例开启全页堆(用windbg目录下的gflags或者装一个appverifier都可以开启),通过自己写的一个heap.exe来看一下如何使用全页堆检测堆破坏情况heap.exe代码如下:#include windows.hint main()HANDLE heap_handle = HeapCreate( NULL , 1024 , 0 ) ;char *temp = NULL ;char *buffer = (char*)HeapAlloc(heap_handle , NULL , 128) ;char *buffer1 = (char*)HeapAlloc(heap_handle , NULL , 121) ;temp = buffer ;for( int i = 0 ; i g(1f50.1f54): Access violation - code c0000005 (first chance)First chance exceptions are reported before any exception handling.This exception may be expected and handled.eax=00000080 ebx=00000000 ecx=02596000 edx=02596000 esi=00000001 edi=00193374eip=00191068 esp=0016fdc8 ebp=0016fddc iopl=0 nv up ei ng nz ac pe cycs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010297heap!main+0x68:00191068 c60161 mov byte ptr ecx,61h ds:0023:02596000=?报了一个内存访问错误,然后看一下调用堆栈0:000 kbChildEBP RetAddr Args to Child0016fddc 0019120f 0000

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论