windows程序员进阶系列:《软件调试》之Win32堆的调试支持

    Win32堆的调试支持

 

 

为了帮助程序员及时发现堆中的问题,堆管理器提供了以下功能来辅助调试。

 

1:堆尾检查(Heap Tail Check) HTC,在堆尾添加额外的标记信息,用于检测堆块是否溢出。

2:释放检查(Heap Free Check)在释放堆块时进行检查,防止释放同一个堆块。

3:参数检查,对传递给堆的各种参数进行更多的检查。

4:调用时验证(Heap Validate On Call)HVC,每次调用堆函数时都对整个堆进行验证和检查。

5:堆块标记(Heap Tagging)为堆块增加附加标记,以记录堆块的使用情况。

6:用户态栈回溯(User Mode Stack Trace)UST,将每次调用堆函数的函数调用信息记录到一个数据库中。

7:专门用于调试的页堆(Debug  Page Heap)DHP堆,页堆比较常用,且需要专门开启,我们会专门对其进行介绍。

 

创建堆时,堆会根据当前进程的全局标志来决定是否 启用堆的调试功能。操作系统在加载一个进程时会在注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Image File Execution Option表键下寻找以该程序命名的子键。如果存在该子键则读取下面的GlobalFlag键值。

可以使用gflags.exe来编辑系统的全局标志或某个文件的全局标志。

如果在调试器运行一个程序,但注册表中并没有设置GlobalFlag键值 ,那么操作系统的加载器会默认将全局标志置为0x70,也就是启用htc、hfc和hpc三项调试功能。

如果注册表中设置了GlobalFlag键值,则使用注册表中的设置,不再默认提供其他调试选项。

如果是附加到一个已经运行的程序上,则它的全局标志就是注册表中的值。如果注册表中不存在全局标志,则为0。

 

gflags.exe

 

gflags.exe被称为全局标志编辑器是,windows调试工具集的一部分。该程序是用于对各个全局标志选项的集中式配置工具。它有gui和控制台两种模式。

 

  1. GUI模式开启调试选项

运行gflag.exe,打开gui模式:

             

 

可以看到程序分为三个标签页:system Registry、Kernel flags 、Image File。

标签页System Registry用于设置针对整个系统的选项。设置之后需要重启系统才能生效。

Image File用于设置针对单个进程的配置。设置之后重启进程后才会生效。

Kernel Flags用于设置只对内核产生影响的选项。

在gflags中包含了针对操作系统的各个方面的配置信息,这些信息是被保存在注册表相应的位置上。

单击Image File标签页,在Image:(Tab to Refresh)编辑框内输入要设置的进程名称。如calc.exe。设置完成后点击Tab进行刷新,刷新后下面的各个控件变为可用状态。在需要设置的调试选项后点勾,确认后即可。

 

  1. 控制台方式

 

在cmd窗口输入一下命令来开启相应的调试功能。

开启堆尾检查:gflags  /i calc.exe +htc

开启释放检查:gflags /i calc.exe +hfc

开启调用时验证:gflags /I calc.exe +hvc

开启参数检查: gflags /I calc.exe +hpc

开启用户态栈回溯:gflags /I calc.exe +ust

开启页堆:gflags /p /enable  程序名 /full

或者gflags /I 程序名 +hpa

 

需要关闭时只需要将+变为-即可。

 

在windbg中输入!gflag开查看开启的调试选项。

         

注意,一旦调试之前设置页堆注册表相应位置便不再为0,默认便不会开启hfc、hpc和htc。这一点要特别注意,调试以前要通过!gflag命令查看到底开启了何种调试选项。

 

因为在调试器运行一个程序且注册表中没有设置GlobalFlag键值 时,操作系统会启用htc、hfc和hpc三项调试功能,且它们原理非常简单,因此我们此处将主要精力放在经常使用,且需要手动开启的页堆上。

 

页堆DPH

 

利用堆尾检查可以在释放堆块时或在下次分配时检查到堆结构的破坏。但是这些检查都是滞后的,我们很难知道堆是何时发生的破坏。·   今天我们介绍的页堆(Debug Page Heap DPH)可以解决这个问题。启用DPH后堆管理器会在堆块后增加用于检测溢出的栈栏页,一旦用户数据溢出触及栈栏页将立即引发异常,从而让我们在第一个时间知道堆破坏。

前面介绍的win32堆使用用户数据区前面的_HEAP_ENTRY结构来描述堆块,一旦用户数据超出分配的空间将会覆盖堆块后面的数据。有可能覆盖下一个堆块的_HEAP_ENTRY结构或是空闲堆块的_HEAP_FREE_ENTRY结构导致堆被破坏。为了防止堆块的管理信息被覆盖后使堆发生不可恢复的破坏。页堆管理器除了在堆块用户区前面存储堆块管理信息外,还会将这些管理器信息存储在节点池内。

 

启用页堆

开启页堆可以使用gflags.exe来实现。

  1. Gui方式开启DPH。

                  

 

  1. 控制台方式

gflag.exe /I calc.exe +hpa

设置完成后,使用windbg打开进程进行调试。为了验证是否开始DHP,可以执行!gflag /p命令:

 

                 

也可以查看全局变量ntdll!RtlpDebugPageHeap的值来验证是否开启。当该值为1时表示开启dhp。

 

                 

 

与普通堆相比页堆有很大的不同,每个堆块至少占用两个内存页。在存放用户数据的第一个内存页后面,堆管理器会额外多分配一个内存页。这个内存页是用来检测溢出的,被称为栅栏页。栅栏页的属性为PAEG_NOACCESS,因此一旦用户数据发生溢出触及到栅栏页便会引发异常,使调试人员第一时间发现问题,从而可以迅速定位到导致溢出的代码。

有人也许会有疑问,当堆块非常小时,难道也是占用两个内存页么?答案是肯定的。为了及时检测溢出,堆块被放到第一个内存页的末尾紧邻栅栏页,因此第一个内存页前面的大半部分有可能都是没有被使用的。由于分配粒度为8byte,堆块和栅栏页之间可能会有填充字段。对于很小的堆块也需要占用两个内存页,这是很耗费空间的。

 

测试页堆在调试中的效果

 

 

     使用下面的代码生成HeapTest.exe,并使用windbg调试。

 

[cpp] view plaincopy

 

  1. HANDLE hHeap = HeapCreate(HEAP_NO_SERIALIZE, 0, 1024*1024);  
  2.   
  3. char * p = (char*)HeapAlloc(hHeap, HEAP_NO_SERIALIZE, 1012);  
  4.   
  5. char *q = p;  
  6.   
  7. for(int i = 0 ;i < 1048; i++)  
  8.   
  9. {  
  10.   
  11.   *q++ = 0;  
  12.   
  13. }  
  14.   
  15. bool bRetVal = HeapFree(hHeap, HEAP_NO_SERIALIZE, p);  

 

 

这段代码首先创建一个私有堆然后从私有堆分配1012个字节。但却访问分配地址后的1048byte的地址,很明显发生了堆溢出。

 

第一次我们不开启任何选项,观察全局标志:

          

 

 

发现默认开启了htc、hfc、hpc。

按F5继续运行,可以看到程序抛出异常而中断。

 

                    

 

 

第一行的调试信息显示在访问堆块00420648时超过了它的大小3f4 = 1012Byte。而导致了访问违规。

查看堆栈调用:

 

           

 

 

可以发现是在对堆块释放时检测到堆块被破坏。显然这种方法不能在第一时间中断到出现为问题的地方。

 

第二次我们开启开启dph来观察:

首先开启dhp。gflags /i HeapTest.exe +hpa

   在windbg中重新开始HeapTest.exe。可以看到程序发生内存访问异常。

                  

 

最后一条指令由于访问eax所代表的地址而导致。

查看局部变量的值:

 

                 

 

可以看到i等于1016时发生了异常。细心的同学可能会发现,我们申请的空间只有1012byte,为什么访问到1016时才会导致异常。这是因为在堆中的分配粒度是8byte,即分配的空间大小必须为8的倍数。因此此处填充了4个字节。

启用页堆后我们在第一时间发现了堆溢出的问题,结合源码分析便可很容易的找到导致堆溢出的地方。

 

准页堆

 

使用页堆确实是非常方便的,但是美中不足的是页堆要为每个堆块都分配两个内存页且只利用第一个内存页得后半部分,利用率是非常低的。在调试需要使用大量内存的应用程序时有可能会导致一些问题。为此引入了准页堆。

准页堆弥补了页堆的不足,同时还具有页堆的一些功能。准页堆也被称为常规页堆,页堆也被称为完全页堆。

准页堆不再为每个堆块分配栅栏页,只是在堆块的前后添加一些类似于安全Cookie的附加标记。当释放堆块时,堆管理器会检测这些标记的完好性。一旦检测到这些标记被破坏,便会产生异常。这种机制与释放时检查hfc类似。

由于是在释放时检测,因此准页堆并不具备页堆第一时间便能检测到堆破坏的优点。因此本文并不准备详细介绍。

 

启用准页堆

准页堆也需要手动开启,开启命令与页堆很像:gflags /p /enable calc.exe

 

 

本文介绍了win32堆的调试支持,重点介绍了页堆,其他调试功能调试器默认是开启的,仅以非常小的篇幅介绍。对于一些非常复杂的堆破坏问题,使用其他方式很难发现问题。即使有错误报告,错误报告处往往距离问题发生地十万八千里。而使用页堆却能很好的解决这个问题。仅仅使用一个命令打开页堆的调试功能便可以使困扰很久的问题迎刃而解,这也是本文之所以如此推崇页堆的原因。

下一篇文章将会介绍CRT堆。

 

from:http://blog.csdn.net/ithzhang/article/details/12786393

时间: 2025-01-15 18:49:20

windows程序员进阶系列:《软件调试》之Win32堆的调试支持的相关文章

windows程序员进阶系列:《软件调试》之Win32堆

  win32堆及内部结构   Windows在创建一个新的进程时会为该进程创建第一个堆,被称为进程的默认堆.默认堆的句柄会被保存在进程环境块_PEB的ProcessHeap字段中. 要获得_PEB的地址,可以通过$peb伪寄存器来获得,dt _PEB @$peb.也可以通过.process获得.                        如上图ProcessHeap字段即为进程默认堆.其上的HeapSegmentReserve是进程堆的预订(默认为1MB)大小.HeapSegmentCom

为什么没人比程序员更讨厌软件

几个月前,我们买了一台新的数码相机,为的是更好地记录我们家小宝贝的成长过程.采购的事情由我太太负责.她小心地打开相机的包装盒,给相机装上电 池,然后就开始了首次试拍.像很多电子产品一样,随相机附送的还有一张软件光盘.于是,她不假思索地打开DVD光驱,然后把光盘放了进去. 我眼角的余光碰巧注意到了这一切.说时迟,那时快,我疯了似的从房间的另一头冲过去,同时大叫:"不--要--啊--"--我不顾一切地试图阻止她从那张光盘上安装软件(为了更好地理解当时的情景,你可以以夸张的慢动作来想象一下整

转一个程序员对一个软件的总结

问题描述 一个程序员开发一个简单要求的图形软件可能比较容易,但要做到专业水准就比较困难了,主要是时间不够,而VisualGraph经过近十年长期的积累,功能全面.例如,多页面.多图层.表格图形混排.透明度.自由旋转.动画效果.自定义网纹.自定义线条.自定义箭头.自定义提示信息窗口.自定义光标.图形保护等等.不仅做到了功能全,而且每个细节都做到极致.这不是一件容易的事. VisualGraph在每一个细节方面都做到能够应付未来用户可能提出的种种要求.为了解决这问题,创建了独立的脚本语言.在这里你甚

活动的可访问兼容型应用给程序员带来使用软件的新工具

摘要Active Accessibility推出的目标是方便身患残疾的人士使用电脑--可 用于放大器.屏幕阅读器,以及触觉型鼠标.它还可用来开发驱动其它软件的应用程序,最 后,其模拟用户输入的能力尤其适合测试软件的开发. 本文从Active Accessibility 的 基本概念出发,带你领略一个测试应用程序软件的开发过程.你将会看到这个测试程序是如 何与常用的控件以及其它UI元素交互,并处理随后的WinEvents的. Microsoft Active Accessibility是一种相对较

好的程序员和优秀软件设计师距离有多远?

你不能认为一个程序员还不错,就可以把他推到系统分析师.软件设计师甚至是软件架构师的位置上. 如果你要想在团队或公司里寻找一个能够胜任软件架构师或者是设计师这样重要位置的人时,首先,出现在你的脑子里的想法,我想通常都是在程序员中选一个最好的.不过我觉得你最好别这么干.这样的位置不是随意的找个不错的程序员就可以胜任的.就算把你最资深的程序员晋升到这个位置也未必合适. 也许刚听见你会感觉很荒诞.为什么我不能让一个程序员去做系统设计呢?毕竟,他们都是设计程序的,不是吗?的确,是的,没错.但是,你要明白一

为什么你招聘不到程序员,以及软件如何定义现实世界

文/霍炬 2015 年,互联网创业从火热到"寒冷",但有一件事全年都没有变化,就是大家都觉得招聘不到程序员. 我没有确切的数字知道 2015 年这一年程序员的薪资增长的比例,但是我确切的知道,在 2015 年,一个有3~4 年工作经验的年轻程序员,在北京和上海能拿到的薪水已经接近 2010 年盛大创新院给我的薪水了.要知道,2010 年盛大为了创建创新院,给出的是业界顶级的薪水,而且那时候我已经有 8 年的工作经验了.这不过是 5 年前的事,今天,3 年工作经验的程序员薪水已经到了这个

程序员恶意编写软件病毒

法院表示今后对植入病毒行为将严加处理 本报讯 (通讯员吴艳燕 记者徐亢美)昨天,上海市第二中级人民法院终审判决了一起"在软件上恶意添加病毒案",认定该行为构成破坏计算机信息系统罪,处以行为人浦加志有期徒刑2年6个月. 浦加志是上海一家软件公司的程序员.2007年7月至9月,其在参与公司两款软件的编程工作中,在一款算量软件内植入了一个含有恶意代码的函数,该函数可以在一定时间内删除C盘至H盘内所有文件.此外,浦加志在另一款报表软件内也恶意添加了病毒. 当年9月,浦加志从公司辞职.与此同时,

一个程序员在卖软件服务中学到的销售经验

干了将近7年的软件开发,我开发实现了很多有趣的东西.最近,我开始投身销售,研究营销技术--为了我的新应用. 我感到发现客户并理解他们的消费行为是一件非常有挑战性的事情,同时也有很多的乐趣.程序员对销售的典型态度要么认为它不重要--这是最好的情况,最坏的 情况是根本不知道何为销售.在这里我要讲的是非常不同的另一面,希望能带来一些能让大家兴奋的建议.如果你喜欢这些建议,我将会再写一篇. 下面的这些忠告都是来自我经营一个B2B服务软件的经验.也许并不是每个人都能接受,但至少从趣味性和知识性方面还是值得

请问下17岁的程序员能在软件公司上班吗

问题描述 我之前在青鸟学了一年,感觉已经有能力在企业上班了,现在准备趁这时候去找份工作,我离满18还差4个月,我想问问,一般企业会招我这种情况的人吗? 解决方案 解决方案二:正规企业不会招的.解决方案三:你可以签个实习合同解决方案四:去小公司看看吧.解决方案五:好年轻,这个时候我连汇编都不会呢.解决方案六: 解决方案七:可以,封装一下解决方案八:不会招.才4个月你急个啥..