??xml version="1.0" encoding="utf-8" standalone="yes"?>精品九九久久国内精品,国产午夜久久影院,99久久精品国产综合一区http://www.shnenglu.com/ay19880703/category/18447.htmlzh-cnSat, 06 Apr 2013 05:29:09 GMTSat, 06 Apr 2013 05:29:09 GMT60让xp加蝲指定的内核版?-别以为xp加蝲的内核L来自于ntoskrnl.exe!!!http://www.shnenglu.com/ay19880703/archive/2013/04/06/199145.html__ay__aySat, 06 Apr 2013 05:16:00 GMThttp://www.shnenglu.com/ay19880703/archive/2013/04/06/199145.htmlhttp://www.shnenglu.com/ay19880703/comments/199145.htmlhttp://www.shnenglu.com/ay19880703/archive/2013/04/06/199145.html#Feedback0http://www.shnenglu.com/ay19880703/comments/commentRss/199145.htmlhttp://www.shnenglu.com/ay19880703/services/trackbacks/199145.html我ida是ntoskrnl.exe ....  而windows中加载的版本?span style="font-size: 14px;">ntkrpamp.exe

忘了操作pȝ会根据处理器型号加蝲不同版本内核?...
正好看到高端调试上有关于q个的讨? http://advdbg.org/forums/2142/ShowPost.aspx

但是我的分析是基于ntoskrnl的,没办法,只好强制指定pȝ加蝲ntosknrl?得做以下几个工作

1 把vm的processor调成单核单处理器
2 开windows虚拟? Z告诉pȝ现在是单核单处理器模?得跑一下这个命?nbsp;
rundll32.exe setupapi,InstallHinfSection ACPIAPIC_UP_HAL 131 %windir%\inf\hal.inf
参? 如何修改Windows XPpȝ的内核类?nbsp; http://blog.sina.com.cn/s/blog_5918846401000bik.html
3 当然,q里q没l束,如果处理器支持PAE 那么pȝ会加载ntoskrnla  所以还得禁用PAE. ?span style="font-family: arial, 宋体, sans-serif; font-size: 14px; line-height: 24px; text-indent: 30px; background-color: #ffffff;">BOOT.INI里面, 启动讄中如果有/noexecute=optin替换改?execute,没有的话加?/span>/execute

完了后就会加载ntoskrnl?效果?

指定其它pȝҎcM,在此仅抛砖引?nbsp;





__ay 2013-04-06 13:16 发表评论
]]>
WINDBG的堆调试&mdash;full page heap的堆破坏原?/title><link>http://www.shnenglu.com/ay19880703/archive/2012/01/05/163604.html</link><dc:creator>__ay</dc:creator><author>__ay</author><pubDate>Thu, 05 Jan 2012 01:17:00 GMT</pubDate><guid>http://www.shnenglu.com/ay19880703/archive/2012/01/05/163604.html</guid><wfw:comment>http://www.shnenglu.com/ay19880703/comments/163604.html</wfw:comment><comments>http://www.shnenglu.com/ay19880703/archive/2012/01/05/163604.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/ay19880703/comments/commentRss/163604.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ay19880703/services/trackbacks/163604.html</trackback:ping><description><![CDATA[<p>@作? ay @文章出处: <a >cnss-ay的博?/a>@Notice: <font color="#ff0000">转蝲h明出处!若文章显CZ完整Q可以到文章出处阅读?/font></p> <p><font color="#ff0000"></font> </p> <p><font size="3">此文会涉及到一些普通堆的知识,q些内容可以参见我之前的文章 </font><a href="http://www.shnenglu.com/ay19880703/archive/2011/10/30/159364.html" target="_blank"><font size="3">WINDBG的堆调试--了解HEAPl织</font></a></p> <h1>堆破?/h1> <p><font size="2">所谓的堆破坏,是说没控制好自己的指针,把不属于你分配的那块内存l写覆盖了。这块内存可能是你程序的数据Q也可能是堆的管理结构。那么这个会D怎样的后果呢Q可能的情况我们来yy?/font></p> <ol> <li><font size="2">把程序里的计结果覆盖了Q这也许会让你重复看了Nơ代码,校验了Nơ计逻辑也搞不明白ؓ何计结果还是有问题 </font> <li><font size="2">堆管理结构被破坏了,new/deleteQ或者malloc/free操作p| </font> <li><font size="2">{等{等~</font></li></ol> <p><font size="2">堆破坏较为理想的情况是被修改的数据会马上DE序crashQ最差的情况是你的堆数据莫名其妙在今天被改了Q但明天才crash。这个时候在d析crashQ就如我们的警察叔叔现在接手一?0q前的案子一?---无从下手。老外UC为heap corruption是很贴切的,有时候咱堆数据被意外改是无声无息的Q你也许没法从界面甚x志文件中看到它被改的一点迹象,当到某一个时刻,q种错误会暴露出来,然而这个时候查看堆信息也许会是毫无头A。所以对于堆破坏Q咱的策略是早发现我们的堆被篡改了Q最好能够在堆数据被意外改的那一时刻诱发一个异常来提醒我们----兄弟Q你的堆被腐蚀了?/font></p> <p><font size="2">微Y提供了一些方案,来帮助我们诊断堆破坏。一般来_堆破坏往往都是写数据越界造成的(yy的第二种情况Q如果是W一U情况其实还单,下个内存断点好Q,所以微软在堆分配上Q给E序员门额外提供?U堆分配模式--完全堆Qfull page heapQ,准页?normal page heap)Q用来检堆被写界的情c?/font></p> <h1></h1> <h1>完全堆Qfull page heapQ?/h1> <h2>原?/h2> <p><font size="2">完全堆的检基本思\是通过分配盔R的一个页Qƈ其设ؓ不可讉K属性,然后用户数据块会被分配到内存늚最末端Q从而实现越界访问的。当我们对堆中分配的内存d界后便会访问到那个不可ȝ,pȝ捕获到改ơ异常后会试图中断执行ƈ该异常上报ldebuggerQ或者崩溃。具体的内存l织l构如下?/font></p> <p><a href="http://www.shnenglu.com/images/cppblog_com/ay19880703/Windows-Live-Writer/918bb68ffcd3_1405A/image_15.png"><font size="2"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.shnenglu.com/images/cppblog_com/ay19880703/Windows-Live-Writer/918bb68ffcd3_1405A/image_thumb_5.png" width="468" height="184"></font></a></p> <p><font size="2">摘自《Y件调试?/font></p> <p><font size="2"> </font></p> <p><font size="2">与普通堆不同的是Q内存块前面的HEAP_ENTRYl构被DPH_BLOCK_INFORMATIONl构取代Q这个结构内部记录了堆模式下这个内存块的一些基本信息。如果用h据区前面的数据,也就是DPH_BLOCK_INFORMATIONl构被破坏了Q那么在释放内存块的时候系l会报错Q如果编E者对q块内存块读写越界了Q当Ӟq里界有几U情况:</font></p> <ol> <li><font size="2">读越界,但只是访问了块尾填充部分数据Q那么系l不会报?</font> <li><font size="2">写越界,但只改了图中块֡充的部分Q那么在堆块释放的时候会报错 </font> <li><font size="2">读越界,且超q了块尾填充的部分,讉KC栅栏,那么pȝ会立xZ个异常ƈ中断执行 </font> <li><font size="2">写越界,且超q了块尾填充部分Q写C栅栏,那么pȝ会立xZ个异常ƈ中断执行</font></li></ol> <p><font size="3"><font size="2">q里需要注意的q是<font color="#ff0000">块尾填充不一定存?/font>Q块֡充是因ؓ要满_内存的最分配粒度,如果本n内存块的分配_度已l是最分配粒度的倍数了,那么块尾填充׃存在了,比如堆内存分配粒度是? bytesQ那么如果申请了14 bytes的话会有2 bytes的大徐小的块֡充块Q如果申请了24bytesQ那么就没有块尾填充了,因ؓ24正好?的倍数</font>?/font></p> <p> </p> <h2>CZ</h2> <p><font size="2">开启全堆Q用windbg目录下的gflags或者装一个appverifier都可以开启)Q通过自己写的一个heap.exe来看一下如何用全堆堆破坏情况heap.exe代码如下Q?/font></p> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:e8007c4f-f636-4682-8278-4037e4c00777" class="wlWriterSmartContent"><pre class="c" name="code">#include "windows.h" int 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 < 138 ; ++i ) { *(temp++) = 'a' ; } HeapFree(heap_handle, 0 , buffer ) ; HeapFree(heap_handle, 0 , buffer1 ) ; HeapDestroy( heap_handle) ; return 0 ; }</pre></div> <p><font size="2">在第14行向buffer写入138字节Q这昄界了,然后在用windbg启动heap.exeQ直接运行,会发现报错如?/font></p> <p><font size="2">0:000> g<br>(1f50.1f54): Access violation - code c0000005 (first chance)<br>First chance exceptions are reported before any exception handling.<br>This exception may be expected and handled.<br>eax=00000080 ebx=00000000 ecx=02596000 edx=02596000 esi=00000001 edi=00193374<br>eip=00191068 esp=0016fdc8 ebp=0016fddc iopl=0         nv up ei ng nz ac pe cy<br>cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010297<br>heap!main+0x68:<br>00191068 c60161          mov     byte ptr [ecx],61h         ds:0023:02596000=??<br></font></p> <p><font size="2">报了一个内存访问错误,然后看一下调用堆?/font></p> <p><font size="2">0:000> kb<br>ChildEBP RetAddr  Args to Child              <br>0016fddc 0019120f 00000001 023fbfd0 0239df48 heap!main+0x68 [d:\projects\heap\<font color="#ff0000">main.cpp @ 14</font>]<br>0016fe20 765b1114 7ffd3000 0016fe6c 778eb429 heap!__tmainCRTStartup+0x10f [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 582]<br>0016fe2c 778eb429 7ffd3000 757369d8 00000000 kernel32!BaseThreadInitThunk+0xe<br>0016fe6c 778eb3fc 00191357 7ffd3000 00000000 ntdll!__RtlUserThreadStart+0x70<br>0016fe84 00000000 00191357 7ffd3000 00000000 ntdll!_RtlUserThreadStart+0x1b</font></p> <p><font size="2">可以看到是第14行报的错Q但?4行的代码q行了那么多ơ,我们再看一下这个时候变量i的值是多少</font></p> <p><font size="2">0:000> dv i<br>              i = 0n128</font></p> <p><font size="2">昄Q在填充W?28字节的时候,我们的temp指针讉KC栅栏,从而报Z一个内存违规的异常?/font></p> <p><font size="2">q里带看一下如果我们分配的内存不是8 bytes的情况(一般堆内存分配_度? bytesQ所以申?28 bytes的内存时是不会有块尾填充部分的)</font></p> <p><font size="2">那我们接下来看另外一D代?/font></p> <p><font color="#ff0000" size="2">我们把第10行的temp = bufferҎtemp = buffer1</font></p> <p><font color="#000000" size="2">因ؓbuffer1甌?21 bytesQ也是说它? bytes的填充字?/font></p> <p><font size="2">0:000> g<br>(1ba0.1ba4): Access violation - code c0000005 (first chance)<br>First chance exceptions are reported before any exception handling.<br>This exception may be expected and handled.<br>eax=00000080 ebx=00000000 ecx=024c8000 edx=024c8000 esi=00000001 edi=00033374<br>eip=00031068 esp=002cfb80 ebp=002cfb94 iopl=0         nv up ei ng nz ac pe cy<br>cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010297<br>heap!main+0x68:<br>00031068 c60161          mov     byte ptr [ecx],61h         ds:0023:024c8000=??<br>0:000> dv i<br><font color="#ff0000">              i = 0n128</font></font></p> <p><font size="2">可以看到变量iq是128Q也是说我们还是在讉K到第128字节后才引发讉K异常Q而不是我们期望的121字节后就引发异常?/font></p> <p><font size="2">q里也就是说<font color="#ff0000">如果我们的代码中对申L堆内存写界了,写数据覆盖块֡充部分的时候ƈ不会引发异常Q?/font></font></p> <p><font size="2">但是Q这q不代表我们的写界问题不会被发现。块֡充部分是会被填充上固定数据的Q系l在适合的时机(比如销毁堆的时候)会校验块֡充块Q如果发现块֡充块数据有变Q那么便会报一个verifier异常Q比如我们把代码中的for循环ơ数改ؓ124</font></p> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:14cc24bb-3177-401d-b544-0ea75212c964" class="wlWriterSmartContent"><pre class="c" name="code"> for( int i = 0 ; i < 124 ; ++i )</pre></div> <p><font size="2">那么windbg会中断在W?9?/font></p> <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:812469c5-0cb0-4c63-8c15-c81123a09de7:27622578-bab1-454b-bf14-dc22531576c3" class="wlWriterSmartContent"><pre class="c" name="code"> HeapDestroy( heap_handle) ;</pre></div> <p><font size="2">提示内容如下<br>=======================================<br><font color="#0000ff">VERIFIER STOP 0000000F</font>: pid 0x1E3C: Corrupted suffix pattern for heap block. </font></p> <p><font size="2">    <font color="#ff0000">025A1000</font> : Heap handle used in the call.<br>    <font color="#ff0000">025A7F80</font> : Heap block involved in the operation.<br>    00000079 : Size of the heap block.<br>    <font color="#ff0000">025A7FF9</font> : Corruption address.</font></p> <p><br><font size="2">=======================================<br>This verifier stop is not continuable. Process will be terminated <br>when you use the `go' debugger command.</font></p> <p><font size="2">=======================================</font></p> <p><font size="2">(1e3c.143c): Break instruction exception - code 80000003 (first chance)<br>eax=6c75e994 ebx=6c75cf58 ecx=00000002 edx=002bf461 esi=00000000 edi=000001ff<br>eip=6c753c38 esp=002bf6b4 ebp=002bf8b8 iopl=0         nv up ei pl nz na po nc<br>cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202<br>vrfcore!VerifierStopMessageEx+0x543:<br>6c753c38 cc              int     3</font></p> <p><font size="2">提示说的很清楚了Qappverifier指出了堆和具体的内存块,我们q个时候查看buffer1的值是<font color="#ff0000">0x025a7f80</font> Q正好就是出问题的堆块,出问题的地址?x025a7ff79Q正好就是buffer1内存块的边界Q错误原因是Corrupted suffix pattern for heap blockQ也是说咱块尾填充部分Qsuffix pattern for heap blockQ被破坏QcorruptedQ了</font></p> <p><font size="2">l论Q只要写界Q系l都能够出来,只不q如果写界写到了栅栏页会理解触发异怸断,而写界只写了块֡充部分,那么pȝ在适当时机Q比如堆被销毁,或者这块内存被重新分配{时机)会对块尾填充部分做完整性检,如果发现被破坏了Q就会报错。当Ӟ你可以根据错误号Q?font color="#0000ff">蓝色字体部分</font>Q信息去appverifier的帮助文档中查找更详l的错误说明?/font></p> <h2>l构详解</h2> <p><font size="2">q次咱来倒叙Q先从最基本的内存堆块结构DPH_BLOCK_INFORMATION开始介l,DPH_BLOCK_INFORMATIONl构微Y也有对应文档介绍</font></p> <p><font size="2"><img alt="ms220938.Local_-1265171613_fphbs(en-US,VS.80).gif" src="http://i.msdn.microsoft.com/dynimg/IC171455.gif" width="505" height="190"></font></p> <h2><font size="2">Q摘自MSDNQ?/font></h2> <p><font size="2"></font> </p> <p><font size="2">其中prefix start magic和prefix end magic是校验块Q用来检DPH_BLOCK_INFORMATION是否被破坏,q些部分属于DPH_BLOCK_INFORMATIONl构。我们先来用windbg探究下DPH_BLOCK_INFORMATIONq个最基本的结?再一?我们打开windbg调试heap.exe.q行到第10?q个时候变量的值是</font></p> <p><font size="2">0:000> dv heap_handle<br>    heap_handle = <font color="#ff0000">0x024a0000</font><br>0:000> dv buffer<br>         buffer = <font color="#ff0000">0x024a5f80</font> "???"<br>0:000> dv buffer1<br>        buffer1 = <font color="#ff0000">0x024a7f80</font> "???"</font></p> <p><font size="2">q里可以看到一个很有趣的现?buffer1和buffer的地址正好相差8K,也就是两个页的大?q当然是因ؓ堆的原因啦,其实q两块内存分配是盔R着?虚拟内存l构如下图所C?/font></p> <table border="1" cellspacing="0" cellpadding="2" width="801"> <tbody> <tr> <td valign="top" width="200"><font size="2">buffer内存块(4KQ?/font></td> <td valign="top" width="200"><font size="2">栅栏(4KQ?/font></td> <td valign="top" width="200"><font size="2">buffer1内存?4K)</font></td> <td valign="top" width="199"><font size="2">栅栏?4K)</font></td></tr></tbody></table> <p><font size="2"></font> </p> <p><font size="2">׃buffer和buffer1分配的大是一LQbuffer1加上N填充块和buffer的大相同)Q所以这两块内存正好相差8K</font></p> <p><font size="2">而DPH_BLOCK_INFORMATION在我们甌的内存块指针的前0x20字节处,用dt命o看的l果如下:</font></p> <p><font size="2">0:000> dt _DPH_BLOCK_INFORMATION 0x024a5f80-0x20<br>verifier!_DPH_BLOCK_INFORMATION<br>   +0x000 StartStamp       : 0xabcdbbbb<br>   +0x004 Heap             : 0x024a1000 Void<br>   +0x008 RequestedSize    : 0x80<br>   +0x00c ActualSize       : 0x1000<br>   +0x010 Internal         : _DPH_BLOCK_INTERNAL_INFORMATION<br>   +0x018 StackTrace       : 0x003d9854 Void<br>   +0x01c EndStamp         : 0xdcbabbbb</font></p> <p><font size="2"></font> </p> <p><font size="2">0x024a5f80-0x20是DPH_BLOCK_INFORMATIONl构的地址。DPH_BLOCK_INFORMATIONl构在已分配和已释放的状态下QStartStamp和EndStampQ也是MSDN图中的prefix start magic和prefix end magicQ是不同的,昄dt输出的结果看来,q个内存块是已分配状态。StackTrace记录了分配这个内存块时的调用栈,可以用dds来看一下这个内存块被分配时候的调用?/font></p> <p><font size="2">0:000> dds 0x003d9854 <br>003d9854  00000000<br>003d9858  00004001<br>003d985c  00090000<br>003d9860  5b3b8e89 verifier!AVrfDebugPageHeapAllocate+0x229<br>003d9864  776d5c4e ntdll!RtlDebugAllocateHeap+0x30<br>003d9868  77697e5e ntdll!RtlpAllocateHeap+0xc4<br>003d986c  776634df ntdll!RtlAllocateHeap+0x23a<br>003d9870  003b1030 heap!main+0x30 [d:\projects\heap\main.cpp @ 8]<br>003d9874  003b120c heap!__tmainCRTStartup+0x10f [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 582]<br>003d9878  76451114 kernel32!BaseThreadInitThunk+0xe<br>003d987c  7766b429 ntdll!__RtlUserThreadStart+0x70<br>003d9880  7766b3fc ntdll!_RtlUserThreadStart+0x1b</font></p> <p><font size="2">输出l果我们可以看到q个内存块是在main.cpp,也就是我们的CZ代码的第8行分配的Q第8行是char *buffer = (char*)HeapAlloc(heap_handle , NULL , 128) 正好是分配buffer内存的那条语句。这个结构的其它字段Q顾名思义QActualSize指明了实际分配字节数Q?x1000 bytes也就?K大小QInternalq个字段保存了个内部l构Q用windbg也看不出q个l构信息?/font></p> <p><font size="2">当然Z防止内存块前面的数据被冲hQ除了DPH_BLOCK_INFORMATION外,pȝq通过DPH_HEAP_BLOCK保存了所分配内存块的信息Q?/font></p> <p><font size="2">通过!heap –p –h [address] 可以查看到页堆的信息</font></p> <p><font size="2">0:000> !heap -p -h <font color="#ff0000">0x024a0000                            //heap_handle的?/font><br>    _DPH_HEAP_ROOT @ 24a1000<br>    Freed and decommitted blocks<br>      DPH_HEAP_BLOCK : VirtAddr VirtSize<br>    Busy allocations<br>      DPH_HEAP_BLOCK : UserAddr  UserSize - VirtAddr VirtSize<br>        <font color="#0000ff">024a1f6c</font> : 024a5f80 00000080 - 024a5000 00002000<br>        024a1f38 : 024a7f80 00000079 - 024a7000 00002000<br></font></p> <p><br><font size="2">可以看到Qbuffer内存块对应的DPH_HEAP_BLOCKl构地址?font color="#0000ff">024a1f6c</font></font></p> <p><font size="2">0:000> dt _DPH_HEAP_BLOCK 024a1f6c<br>verifier!_DPH_HEAP_BLOCK<br>   +0x000 NextFullPageHeapDelayedNode : 0x024a1020 _DPH_HEAP_BLOCK<br>   +0x004 DelayQueueEntry  : _DPH_DELAY_FREE_QUEUE_ENTRY<br>   +0x000 LookasideEntry   : _LIST_ENTRY [ 0x24a1020 - 0x0 ]<br>   +0x000 UnusedListEntry  : _LIST_ENTRY [ 0x24a1020 - 0x0 ]<br>   +0x000 VirtualListEntry : _LIST_ENTRY [ 0x24a1020 - 0x0 ]<br>   +0x000 FreeListEntry    : _LIST_ENTRY [ 0x24a1020 - 0x0 ]<br>   +0x000 TableLinks       : _RTL_BALANCED_LINKS<br>   +0x010 pUserAllocation  : 0x024a5f80  "???"<br>   +0x014 pVirtualBlock    : 0x024a5000  "???"<br>   +0x018 nVirtualBlockSize : 0x2000<br>   +0x01c Flags            : _DPH_HEAP_BLOCK_FLAGS<br>   +0x020 nUserRequestedSize : 0x80<br>   +0x024 AdjacencyEntry   : _LIST_ENTRY [ 0x24a1f5c - 0x24a1fc4 ]<br>   +0x02c ThreadId         : 0x3f4<br>   +0x030 StackTrace       : 0x003d9854 Void</font></p> <p><font size="2">从dt的数据看来,q个l构大小?x34Qbuffer和buffer1的DPH_HEAP_BLOCKl构首地址正好也是相差0x34Q说明这两个l构是紧挨着的,下一步在让我们来看看DPH_HEAP_BLOCKl构是如何组l的?/font></p> <p><a href="http://www.shnenglu.com/images/cppblog_com/ay19880703/Windows-Live-Writer/918bb68ffcd3_1405A/image_3.png"><font size="2"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.shnenglu.com/images/cppblog_com/ay19880703/Windows-Live-Writer/918bb68ffcd3_1405A/image_thumb.png" width="555" height="218"></font></a></p> <p><font size="2">摘自《Y件调试?/font></p> <p><font size="2"></font> </p> <p><font size="2">q个是整个的堆l构图,我们先来说说DPH_HEAP_BLOCK的组l吧Q在图中0x16d00000是页堆的首地址Q也是堆的句柄,我们调试器中Q页堆首地址则是<font color="#ff0000">0x024a0000</font>Qؓ了数据统一Q我q是?x024a0000作ؓ堆句柄来讲解。我们的DPH_HEAP_BLOCK其实在堆块节点池里边,我们可以q似把这个节Ҏ看成一个大型的DPH_HEAP_BLOCK数组Q但有个地方在Y件调试中没有提到Q就是在win7下,q行时这些DPH_HEAP_BLOCKl构都是以二叉^衡数的结构来l织的,q个树的l构的入口正是在TableLinks字段内,q么做的原因也大概是因ؓ能够在分配时更快的烦。我们再看看DPH_HEAP_ROOTl构Q这个结构储存了整个堆的必要信息,它就相当于普通堆的_HEAPl构?/font></p> <p><font size="2">0:000> dt _dph_heap_root 24a1000<br>verifier!_DPH_HEAP_ROOT<br>   +0x000 Signature        : 0xffeeddcc<br>   +0x004 HeapFlags        : 0x1002<br>   +0x008 HeapCritSect     : 0x024a16cc _RTL_CRITICAL_SECTION<br>   +0x00c NodesCount       : 0x2c<br>   +0x010 VirtualStorageList : _LIST_ENTRY [ 0x24a1fa0 - 0x24a1fa0 ]<br>   +0x018 VirtualStorageCount : 1<br>   +0x01c PoolReservedLimit : 0x024a5000 Void<br>   +0x020 <font color="#0000ff">BusyNodesTable</font>   : _RTL_AVL_TABLE<br>   +0x058 NodeToAllocate   : (null) <br>   +0x05c nBusyAllocations : 2<br>   +0x060 nBusyAllocationBytesCommitted : 0x4000<br>   +0x064 pFreeAllocationListHead : (null) <br>   +0x068 FullPageHeapDelayedListTail : (null) <br>   +0x06c DelayFreeQueueHead : (null) <br>   +0x070 DelayFreeQueueTail : (null) <br>   +0x074 DelayFreeCount   : 0<br>   +0x078 LookasideList    : _LIST_ENTRY [ 0x24a1078 - 0x24a1078 ]<br>   +0x080 LookasideCount   : 0<br>   +0x084 UnusedNodeList   : _LIST_ENTRY [ 0x24a1ed0 - 0x24a16e4 ]<br>   +0x08c UnusedNodeCount  : 0x28<br>   +0x090 nBusyAllocationBytesAccessible : 0x2000<br>   +0x094 GeneralizedFreeList : _LIST_ENTRY [ 0x24a1f04 - 0x24a1f04 ]<br>   +0x09c FreeCount        : 1<br>   +0x0a0 PoolCommitLimit  : 0x024a2000 Void<br>   +0x0a4 NextHeap         : _LIST_ENTRY [ 0x5b3e9a58 - 0x23a10a4 ]<br>   +0x0ac ExtraFlags       : 3<br>   +0x0b0 Seed             : 0xfed6f13a<br>   +0x0b4 NormalHeap       : 0x027d0000 Void<br>   +0x0b8 CreateStackTrace : 0x003d9824 _RTL_TRACE_BLOCK<br>   +0x0bc ThreadInHeap     : (null) <br>   +0x0c0 BusyListHead     : _LIST_ENTRY [ 0x24a10c0 - 0x24a10c0 ]<br>   +0x0c8 SpecializedFreeList : [64] _LIST_ENTRY [ 0x24a10c8 - 0x24a10c8 ]<br>   +0x2c8 DelayFreeListLookup : [257] (null) <br>   +0x6cc HeapCritSectionStorage : _RTL_CRITICAL_SECTION<br></p></font> <p><font size="2">q里边维护了很多q行时信息,比如说DPH_BLOCK_INFORMATION中的那个二叉树入口其实就是保存在<font color="#0000ff">BusyNodesTable </font>字段Q这里面记录了所有被分配了的内存块所对应的DPH_BLOCK_INFORMATION。当Ӟq里面一些信息Y件调试里面都有介l,很多看名字也能够猜到大概意思,看名字猜不到啥意思的字段Q其实我也猜不到。。?_-|||在创建页堆后Q所有内存分配都分配在页堆中Q通过分配的地址也能看得出来Q我们分配的内存都是024a打头Q,而非普通页堆中Q普通页堆也仅仅只是保存一些系l内部用的数据。一般来_堆块节点池加上DPH_HEAP_ROOTl构大小正好?个内存页Q也是16K?/font></p> <h2>优缺?/h2> <h3>~点Q消耗大量虚拟内存,每块内存的分配粒度是2个页Q?KQ,</h3> <h3>优点Q能够立x莯界读写操作,通过调用栈就可以q溯到问题源头。能够快速定位问题代码?/h3> <h3>使用Q?2位下不适宜跑配|文件结构比较复杂的软gQ让我们来假设一个xml配置文g下有3000个节点,每个节点?个字W串描述属性,如果把这些配|文件信息{化ؓstll构来保存,那么每个节点则需要ؓ此分?*8K的空_3000w|则需?000*5*8K=117MB虚拟内存Q如果每个节点信息再多一些呢Q这样会D虚拟内存耗尽从而出Cpd内存问题Q比如,newp|Q。当?4位就不存在这U问题了7T的虚拟内存空_现在看来应该是够用了?/h3> <p> </p> <p><font color="#ff0000">对于调试堆破坏来_其实我们只要了解DPH_BLOCK_INFORMATIONl构和DPH_HEAP_BLOCK中的基本字段差不多了,q样更方便我们定位出错源头。比如在appverifier报错后(或者你E序自己莫名其妙崩溃或者数据被改后,要知道appverifierq不L可信的)Q我们可以自己手动调试出错的堆块l构QDPH_BLOCK_INFORMATIONQDPH_HEAP_BLOCK和DPH_HEAP_ROOTQ,以下这些点Q?/font></p> <ol> <li><font color="#ff0000">堆块管理结构的校验字段是否完整</font> <li><font color="#ff0000">是否块尾填充部分有被修改q?/font> <li><font color="#ff0000">到未释放或者重复释攑֠资源Ӟ查看问题的堆块被分配时的调用?/font></li></ol> <p><font color="#ff0000">其实堆q好Q它有较强的实时性,所以ƈ不需要太多手工调试的操作Q越界读写都会立卌发异常ƈ且中断,所以从q点看来Q它是一些Y件用来检堆资源是否正确使用的必备良药~ 但是相对于页堆,准页堆的调试则需要更好的M解准堆工作原理了,因ؓ它提供的堆块不是实时的Q所以发现问题后Q需要咱“精湛的调试内功“去扑և源头Q关于准堆的东西,下回再说吧,敬请期待~</font></p><img src ="http://www.shnenglu.com/ay19880703/aggbug/163604.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ay19880703/" target="_blank">__ay</a> 2012-01-05 09:17 <a href="http://www.shnenglu.com/ay19880703/archive/2012/01/05/163604.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谁动了我的指?--Cơwindbg内存断点的?/title><link>http://www.shnenglu.com/ay19880703/archive/2012/01/03/163486.html</link><dc:creator>__ay</dc:creator><author>__ay</author><pubDate>Tue, 03 Jan 2012 07:07:00 GMT</pubDate><guid>http://www.shnenglu.com/ay19880703/archive/2012/01/03/163486.html</guid><wfw:comment>http://www.shnenglu.com/ay19880703/comments/163486.html</wfw:comment><comments>http://www.shnenglu.com/ay19880703/archive/2012/01/03/163486.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.shnenglu.com/ay19880703/comments/commentRss/163486.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/ay19880703/services/trackbacks/163486.html</trackback:ping><description><![CDATA[<p> </p> <p>写驱动的时候有个地方老是蓝屏,看了dump发现数据被非法篡改了.</p> <p>数据初始化如?/p> <p> </p> <p>if(record_set_ptr != NULL )<br>{<br>    record_set_ptr->look_aside_pool_ptr = g_user_control_context.look_aside_pools[type] ;<br>    record_set_ptr->type = type ;<br>    record_set_ptr->buffer_size = notify_count * unit_size_of ;<br>    record_set_ptr->units_count = notify_count ;<br>    record_set_ptr->complete_count = 0 ;<br>}<br></p> <p>然后在调用ExFreeToNPagedLookasideList传入record_set_ptr->look_aside_pool_ptr 的时候挂?发现record_set_ptr->look_aside_pool_ptr已经被改?</p> <p> </p> <p>Z跟踪数据在哪里被修改?先在数据初始化的地方下断,然后Crecord_set_ptr->look_aside_pool_ptr 的地址:0x85c16018</p> <p>对这个内存下个断?</p> <p>1: kd> ba w4 85c16018</p> <p>w表示在写入时断下,4表示监控范围,单位是字?nbsp; </p> <p>整个命o的意思就是让调试器在pȝ写入内存85c16018-85c1601bq个地址范围的时候中?</p> <p>OK,命o下完,F5一下就立马断下来了</p> <p>1: kd> g<br>Breakpoint 3 hit<br>nt!memcpy+0x33:<br>8053b583 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]</p> <p><br>此时edi的? <font color="#ff0000">0x85c16018</font></p> <p><font color="#000000"></font> </p> <p><font color="#000000">最后看一下函数堆?发现是字W串拯界覆盖了后面的数据....  </font></p> <p>后面又想?出错时record_set_ptr->look_aside_pool_ptr 的值是<font color="#ff0000">0x005c0065</font></p> <p><font color="#000000">q么明显的字W串特征竟然没意识到....一看出错值就应该知道是字W串覆盖造成?....</font></p><img src ="http://www.shnenglu.com/ay19880703/aggbug/163486.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/ay19880703/" target="_blank">__ay</a> 2012-01-03 15:07 <a href="http://www.shnenglu.com/ay19880703/archive/2012/01/03/163486.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WINDBG的堆调试--了解HEAPl织http://www.shnenglu.com/ay19880703/archive/2011/10/30/159364.html__ay__aySun, 30 Oct 2011 11:05:00 GMThttp://www.shnenglu.com/ay19880703/archive/2011/10/30/159364.htmlhttp://www.shnenglu.com/ay19880703/comments/159364.htmlhttp://www.shnenglu.com/ay19880703/archive/2011/10/30/159364.html#Feedback1http://www.shnenglu.com/ay19880703/comments/commentRss/159364.htmlhttp://www.shnenglu.com/ay19880703/services/trackbacks/159364.html@作? ay @文章出处: cnss-ay的博?/a>@Notice: 转蝲h明出处!若文章显CZ完整Q可以到文章出处阅读?/font>

HEAP的概?/h1>

堆栈堆栈Q在操作pȝ内存中有两种存储I间Q一个是堆,一个是栈。堆主要用于存储用户动态分配的变量Q而栈呢,则是存储我们E序q程中的临时变量。当然栈的作用远不止用作存储变量Q但q不是我们这文章的讨论内容?/p>

 

堆(HEAPQ的分配Q用,回收都是通过微Y的API来管理的Q最常见的API是malloc和new。在往底层C点呢Q这两个函数都会调用HeapAllocQRtlAllocateHeapQ。同L相关函数q有HeapFree用来释放堆,HeapCreate用来创徏自己的私有堆。下面是q些函数的调用链Q?/p>

HeapCreate->RtlCreateHeap->ZwAllocateVirtualMemory  (q里会直接申请一大片内存,至于甌多大内存,pEPEBl构中的字段觉得QHeapSegmentReserve字段指出要申请多大的虚拟内存QHeapSegmentCommit指明要提交多大内存,对虚拟内存的甌和提交概念不清楚的童鞋,请参见windows核心~程相关内容~)

HeapAlloc->RtlAllocateHeapQ至于这里申L内存Q由于HeapCreate已经甌了一大片内存Q堆理器这片内存中划分一块出来以满甌的需要。这一步申h作是堆管理器自己l护的,仅当甌内存不够的时候才会再ơ调用ZwAllocateVirtualMemory Q?/p>

HeapFree->RtlFreeHeap Q对于释攄内存Q堆理器只是简单的把这块内存标志位已释放让后加入到I闲列表中,仅当I闲的内存达C定阀值的时候会调用ZwFreeVirtualMeMory Q?/p>

HeapDestroy->RtlDestroyHeap->ZwFreeVirtualMeMory   Q销毁我们申L堆)

如何扑ֈ我们的HEAP信息Q?/h1>

WINDBG观察?/p>

源码Q?/p>

#include "windows.h"

int 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.exeQ然后用windbg调试q个E序Q在main函数下断Q紧接着执行W五行语句,执行l果如下

0:000> p
eax=002e1ca0 ebx=00000000 ecx=6d29b6f0 edx=00000000 esi=00000001 edi=01033374
eip=01031012 esp=0022fe8c ebp=0022feac iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
heap!main+0x12:
01031012 ff150c200301    call    dword ptr [heap!_imp__HeapCreate (0103200c)] ds:0023:0103200c={kernel32!HeapCreateStub (769a29d7)}

0:000> p
eax=002c0000 ebx=00000000 ecx=77429897 edx=77498500 esi=00000001 edi=01033374
eip=01031018 esp=0022fe98 ebp=0022feac iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
heap!main+0x18:
01031018 8945fc          mov     dword ptr [ebp-4],eax ss:0023:0022fea8=6d222201
0:000> !heap
Index   Address  Name      Debugging options enabled
  1:   00300000               
  2:   00010000               
  3:   00020000               
  4:   002e0000               
  5:   002c0000      

HeapCreate执行的返回值存攑֜eax处,q个函数q回了一个堆句柄Q?x002c0000。用!heap命o查看可以看到W五个堆是我们创徏的堆句柄了?/p>

每个q程都存在多个堆Q我们也可以通过PEBl构来得到进E中存在的堆Q结果和!heap命o昄的内Ҏ一L?/p>

heap!_PEB
   +0x018 ProcessHeap      : 0x00300000 Void         ; q程的默认堆
   +0x068 NtGlobalFlag     : 0                                       ; q个标志位记录了当前堆调试模?0为普通调试模?br />   +0x078 HeapSegmentReserve : 0x100000          ; q程在新建堆的时候默认申L虚拟内存大小
   +0x07c HeapSegmentCommit : 0x2000               ; q程在每ơ申h交的虚拟内存大小Q在提交的内存用完后Q进E会又在一ơ提交HeapSegmentCommit中指定的内存大小
   +0x080 HeapDeCommitTotalFreeThreshold : 0x10000    ; 当释攄内存大小大于q个阀|p行内存解除提交操?br />   +0x084 HeapDeCommitFreeBlockThreshold : 0x1000     ;  当一ơ性释攄块大超q这个阀|p行内存解除提交操作,只有当满两个条g时才会调用ZwFreeVirtualMeMory 释放物理内存
   +0x088 NumberOfHeaps    : 5                                               ; 当前q程的堆数目,q个数目对应着!heap命o的堆昄个数
   +0x08c MaximumNumberOfHeaps : 0x10                          ; q程所能运行的最大堆数目,若堆数目过q个g计HeapCreate失败了?br />   +0x090 ProcessHeaps     : 0x77498500  -> 0x00300000 Void ;存储堆句柄的数组,q里我们可以得到q程的所有堆句柄

我们可以输入如下命o来查看现有的堆句?/p>

0:000> dd 0x77498500 
77498500  00300000 00010000 00020000 002e0000
77498510  002c0000 00000000 00000000 00000000
77498520  00000000 00000000 00000000 00000000
77498530  00000000 00000000 00000000 00000000
77498540  00000000 77498340 7749bb08 77498220
77498550  00000000 00000000 00000000 00000000
77498560  77498220 00317bd0 00000000 00000000
77498570  00000000 00000000 00000000 00000000

可以看得到这里面的内容和!heap命o的输出结果是一L

而堆句柄的存放范?从MaximumNumberOfHeaps 上来?是77498500-77498540q?x40个字节,因ؓ每个堆句柄占4个字节,0x10个堆句柄的存攄间就?x40?/p>

HEAP的组l结?/h1>

堆的理Q我们可以理解ؓ一个内存池Q它甌一大块I间Q然后负责接应用程序的甌释放{请求。只有在创徏堆,释放堆(注意Q是释放堆,不是堆中的空_Q在q之前,我们需要对堆有关的数据l构做一些解?/p>

我这里观察到的HEAPl构QHEAP_SEGMENTl构和HEAP_ENTRYl构都和软g调试里面描述的不一P当年奎哥写Y件调试的时候估计还没用上WIN7吧。。。我的演C系l是WIN7

HeapCreate函数q回的堆句柄其实是一个指向堆理l构的指针,每个堆都会涉及到q样三个l构QHEAP,HEAP_SEGMENT,HEAP_ENTRY

HEAP_ENTRYl构Q?/p>

在堆理中,每一块申请下来的内存都会有下面所C的固定模式Q?/p>

HEAP_ENTRYQ? bytesQ?/p>

我们new或malloc分配的空?/p>

固定填充I间

q个l构用来记录所分配的空间的信息Q包括用LLI间Q填充的I间Q所在的D号{等信息。所以我们new或者malloc的地址减去8指向该l构。第三部分的固定填充I间是ؓ了内存对齐而生成的Q当然这部分I间q有一部分是用来额外记录这块内存的其它信息Q这里就不详l做介绍了?/p>

HEAP_SEGMENTl构Q?/p>

我们可以q么认ؓQ堆甌内存的大是以段为单位的Q当新徏一个堆的时候,pȝ会默认ؓq个堆分配一个段?hQ通过刚开始的new和malloc分配的空间都是在q个D上分配的,当这个段用完的时候,如果当初创徏堆的时候指明了HEAP_GROWABLEq个标志Q那么系l会个堆在再分配一个段Q这个时候新分配的段q?h了,以下以此cL。每个段的开始初便是HEAP_SEGMENTl构的首地址Q由于这个结构也是申L一块内存,所以它前面也会有个HEAP_ENTRYl构Q?/p>

HEAP_ENTRYQ? bytesQ?/p>

HEAP_SEGMENT

HEAP_ENTRYQ? bytesQ?/p>

我们new或malloc分配的空?/p>

固定填充I间

HEAP_SEGMENTl构会记录段的一些基本信息,该段甌的大,已经提交内存的大,W一个HEAP_ENTRYl构的入口点。(我观察看貌似D는L内存q不会一ơ性全部提交,而是每次提交一个页的大,比如一个段大小2个页Q那么它会先提交一个页内存Q若用完了再提交一个页的内存,若内存还用完了那新Z个段Q这个新建的D也会是先提交一个页内存。)但是0h很特别,q个D늚起始地址是堆句柄指针指向的|也就是说Q?/font>HeapCreateq回的堆句柄L指向0hQؓ什么呢Q因为HEAPl构是HEAP_ENTRY,HEAP_SEGMENT的合体加长版~

HEAPl构Q?/p>

HEAPl构则是记录了这个堆的信息,q个l构可以扑ֈHEAP_SEGMENT链表入口Q空闲内存链表的入口Q内存分配粒度等{信息。HEAP的首地址便是堆句柄的|但是堆句柄的值又?h的首地址也是堆句柄,何解Q其实很单,0h的HEAP_SEGMENT在HEAPl构里面QHEAPl构cd义如q样Q?/p>

struct _HEAP

{

_HEAP_ENTRY Entry ; //HEAP_ENTRYl构Q用来描q存储HEAP内存块大等信息?

_HEAP_SEGMENT Segment ;  //0h的首地址

…? //对于该HEAP的描qC?

} ;

在我们看来,内存l织l构应该如下所C:

HEAP_ENTRYQ? bytesQ?/p>

HEAP_SEGMENT

HEAP

更确切的_HEAPl构中本w就包含了HEAP_ENTRY和HEAP_SEGMENTQHEAP_ENTRYl构是HEAP的第一个数据成员,HEAP_SEGMENT是它W二个数据成员。而对于HEAP_SEGMENT,它的W一个数据成员便是HEAP_ENTRY。这里ؓ了方便理解,才在内存l织l构中把它们拆开展示。(注:q里是win7的情况,和Y件调试这本书中所描述的有一些差异,也属正常现象Q毕竟这部分l构微Yq未公开Q?/p>

用WINDBG观察HEAPl构

在之前已l演CZ如何从PEBl构中找到所有的堆句柄,可以看到002c0000便是我们创徏的句柄。然后我们执CZE序的第7行代码。执行完后结果如下:

0:000> p
eax=002c0000 ebx=00000000 ecx=77429897 edx=77498500 esi=00000001 edi=01033374
eip=01031026 esp=0022fe8c ebp=0022feac iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
heap!main+0x26:
01031026 ff1500200301    call    dword ptr [heap!_imp__HeapAlloc (01032000)] ds:0023:01032000={ntdll!RtlAllocateHeap (774120b5)}
0:000> p
eax=002c0590 ebx=00000000 ecx=774134b4 edx=002c0180 esi=00000001 edi=01033374
eip=0103102c esp=0022fe98 ebp=0022feac iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
heap!main+0x2c:
0103102c 8945f0          mov     dword ptr [ebp-10h],eax ss:0023:0022fe9c={heap!envp (0103301c)}

可以看到EAX保存的返回gؓ002c0590。我们通过两种途径来观察我们申L内存Q通过!heap命o观察和通过dt命o观察

通过!heap命o观察

输入命o!heap –a 2c0590得到的结果如下:

0:000> !heap -a 2c0000
Index   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.

q个命o分别提炼ZHEAPQ绿色区域),HEAP_SEGMENTQ红色区域)和HEAP_ENTRYQ灰色区域)l构中的信息。虽然在灰色区域中,我们找不?c0590Q但是找C一?c0588Q这个正?c0590-8的结果,也就是说最双的地址是每个HEAP_ENTRY的首地址Q接着00588q个字段表示了前面一个HEAP_ENTRY所占用的大,后面?088表示q个内存块的d,x们申L内存+HEAP_ENTRYQ?28+8=0x80+0x8=0x88Q,[101]是这块内存的标志位,最双一位ؓ1表示该内存块被占用。然后busyQ?0Q就是解释说q块内存是被占用的(非空闲的Q,它申L内存?x80Q{化成十进制正好就是我们申L128字节大小?/p>

但是q里用dt _HEAP_ENTRY 2c0588命o却没办法查看对应的结构信息,真是怪哉Q有博文也提到win2008中HEAP相关l构也有变,看来到NT6后,HEAPl构变得不小Qv码windbg中直接dt HEAP_ENTRY是无法原始数据的了,貌似对HEAP_ENTRY做了~码?/font>

通过dt命o观察

同样的,已知HEAP的首地址Q那么先从HEAP下手好了Qdt _HEAP 002c0000可以昄HEAP的数据结?/p>

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
如本文前面所q的Q第一个字D|HEAP_ENTRYl构Q接着应该是HEAP_SEGMENTQ这里只不过把HEAP_SEGMENTl构的字D展开了,可以dt _HEAP_SEGMENT来观察下q个l构的字D?/p>

0:000> dt _heap_segment
ntdll!_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

可以看到HEAPl构中灰色部分是和HEAP_SEGMENTl构中的字段是重复的Q也是说灰色部分字D便是HEAP_SEGMENTl构。在HEAP_SEGMENTl构中,我们可以扑ֈFirstEntry字段Q这里指的便是我们的分配的内存,不过HEAP_ENTRYl构无法观察Q这里便没办法枚丑և所有的HEAP_ENTRYl构了,但是说一下思\Q?/p>

每个HEAP_ENTRY和它对应的内存我们可以称Z个内存块Q计下一个内存块需要用到现有内存块中的2个字D,Size和UnsedBytesQSize的g上粒度(是0:000> !heap -a 2c0000命o昄的信息中的Granularity: 8 bytes字段Q这里是8字节Q,下一个内存块地址是 本内存块地址+Size*8+UnsedBytes。当然这里的_度可以通过HEAP字段中的AlignMask 字段出来?/p>

HEAP的分配粒?/h1>

在HEAPl构中指明了分配_度Q这个分配粒度是说每ơ堆分配的时候,都以q个_度为最单位,q里看到_度?字节。所以这里就有了W二ơ分配内存的实验Q我们让E序执行W?行,然后?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.

q里可以看出多出了一个占用块Q大是0x79Q?21Q?bytesQ但是实际分配的大小q是0x 88 Q?28QbytesQ这是因为系l是? bytes为粒度分配的Q所以ؓq块121 bytes的内存自动填充了7个字节,可见甌121 bytes和申?28 bytes所使用的空间是一L?/font>

HEAP的释攑֒销?/h1>

执行?1行和12行的代码后,堆中的内容分别如下:

执行11行代码的堆情?/strong>

FreeList[ 00 ] at 002c00c4: 002c06a0 . 002c0590 
    002c0588: 00588 . 00088 [100] ?free   Q空闲列表中多出了一块内?/font>
    002c0698: 00088 . 00948 [100] ?free   Q空闲内存,I闲I间?48

Heap entries for Segment00 in Heap 002c0000
002c0000: 00000 . 00588 [101] - busy (587)
002c0588: 00588 . 00088 [100]   Q原先的q块内存释放掉了
002c0610: 00088 . 00088 [101] - busy (79)
002c0698: 00088 . 00948 [100]    ; I闲内存
002c0fe0: 00948 . 00020 [111] - busy (1d)
002c1000: 00001000 - uncommitted bytes.

执行12行代码的堆情?/strong>

FreeList[ 00 ] at 005c00c4: 005c0590 . 005c0590 
    005c0588: 00588 . 00a58 [100] ?free Q回收了buffer1的内存后Q由于由于空闲内存是q箋的,所以直接合q成一块内存。可以看C前内存freeI间?48Q现在合q了以后便是948+88+88=a58,也就是当前内存大?/font>

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.

最后执?4行代?对堆q行释放,释放后我们通过!heap也可以看到只?个堆?我们甌的堆被释放了.

0:000> !heap
Index Address Name Debugging options enabled
1: 00300000
2: 00010000
3: 00020000
4: 002e0000

 

至于HEAP_ENTRYl构的问?有时间在调试看看是怎么回事吧~另外Q这里说明下Qnew和malloc内部都会调用HeapAlloc来申请内存,但是堆句柄从哪来呢?它会_crtheap变量是否为空Q若不ؓI则拿_crtheap变量来作q堆句柄去调用HeapAlloc

参考:

软g调试    张奎?/p>

MSDN   

React OS



__ay 2011-10-30 19:05 发表评论
]]> ˾þþƷ| ƷþĻ| Ӱһþҹײ| 18պҹþó| 69ۺϾþþƷ| þþþþavѿƬ| 69Ʒþþþùۿ| ƷŮþþþþ2018| ˾þ| Ʒþһ| þùƷ| 97þþƷˬ| ҰAVþһ| ݾƷŮ˾þþþþ| 99þþƷѿһ | þСƵ| þ°Ҳȥ| þþƷһ| þþƷAvӰƬ| þþƷר| ޺ݺۺϾþ| һֻƴƬ99þ| þþþAVۺ| þ97㽶| ϵרþ| þþþþþۺձ| þ޾Ʒavվ| þþAVҰ| ޹ŷۺϾþ| þۺϾþۺ| þһҹ| һɫþۺ޾Ʒ| ޹˾þۺһ77| ޹þþþƷ| þøƬ| ھƷŷþþƷ| ŷƷ99þ| ˾Ʒþ޸岻 | ˾þô߽AVһ | þֹƷۺ| ޹Ʒþþ|