Windows程序异常捕获及定位方法

文档序号:6595898阅读:597来源:国知局

专利名称::Windows程序异常捕获及定位方法
技术领域
:本发明涉及程序异常处理
技术领域
,特别是一种Windows环境下的程序异常捕获及定位方法。
背景技术
:目前各类编程语言都提供了程序异常处理机制,如C++语言中的try…catch语句。try关键字引导着一块可能会引起意外的正常处理代码,catch关键字引导着一块意外处理代码。一旦try这个区中引发了一个意外,对程序流程的控制就切换到相应的catch区域。但这种程序异常处理的方式必须知道可能发生的程序异常类型,而且会降低程序运行的性能,所以该种程序异常处理机制一般只用于局部的已知异常处理,如微软基础类库(MFC)中的CFile类在文件操作失败时会抛出文件异常(CFileException)。CFile是MFC提供的一个类,它提供了访问文件的接口。CFile类用来处理正常文件的输入/输出操作,它直接提供磁盘输入/输出服务,并且通过其派生类间接支持文本文件和内存文件。在使用CFile的成员函数进行文件操作时,可使用try…catch语句来捕获CFileException类型的异常。而对于程序未处理的未知程序异常的捕获,特别是针对程序异常的定位目前还没有一个通用的处理方式。即使通过常规方式进行程序异常捕获,也无法对程序异常的发生位置进行定位。例如,由于程序编码错误导致访问非法的内存,这种程序异常是在编码时无法预测的,因此,也就无法使用现有的程序异常处理机制来进行异常捕获,更谈不上在程序异常发生时对程序异常进行定位了。当程序中使用了多个动态连接库时,如果动态连接库中发生程序异常,现有的程序异常处理技术更是无法确定程序异常发生在哪个动态连接库中,使程序异常的定位非常困难。
发明内容有鉴于此,本发明的目的在于提供一种Windows程序异常捕获及定位方法,利用一种通用的方法来捕获和定位Windows环境下程序未处理的未知异常,以提高系统自身定位错误的能力。为了达到上述目的,本发明提供了一种Windows程序异常捕获及定位方法,其特征在于包含以下步骤A、通过系统设置的Windows异常过滤钩子捕获Windows环境下程序未处理的异常,交异常过滤钩子函数处理;B、由异常过滤钩子函数记录每层函数完成调用后返回时要执行的指令地址,定位出异常发生前程序的函数调用顺序;C、通过程序的函数调用顺序,得到所调用的每一模块的起始地址,根据起始地址查询到模块的名称,完整地重现程序异常发生的过程。步骤A包括所述的Windows异常过滤钩子是通过系统函数设置筛选器异常函数(SetUnhandleExceptionFilter)实现的。步骤B,对于记录栈顶地址的函数进一步包括B11、异常过滤钩子函数读取当前基址寄存器中的内存地址;B12、系统函数判断读取到的内存地址是否为合法地址,如果为合法地址,则异常过滤钩子函数读取的内存地址为当前函数进入栈顶时的栈顶地址,如果为非法地址,则停止函数调用顺序的搜索;B13、异常过滤钩子函数读取当前基址寄存器中内存地址的上一个连续内存地址中存放的内容,该内容为上层调用函数完成调用后返回时要执行的指令地址,记录该指令地址用于重现函数调用顺序;B14、异常过滤钩子函数读取当前基址寄存器内存地址中存放的内容,该内容为上层调用函数进入栈时的栈顶地址,返回执行步骤B12。步骤B,对于不记录栈顶地址的函数进一步包括B21、异常过滤钩子函数逐一读取每一个内存地址;B22、系统函数判断读取到的内存地址是否为合法地址,如果为合法地址,继续执行,如果为非法地址,则停止函数调用顺序的搜索;B23、异常过滤钩子函数判断该内存地址中存放的指令内容的上一条指令是否为函数调用call指令,如果是call指令则认为是函数调用关系;B24、异常过滤钩子函数继续读取上一个连续内存地址,返回执行步骤B22。步骤B12进一步包括异常过滤钩子函数第一次读取的内存地址为合法地址时,该内存地址为发生程序异常的当前函数进入栈顶时的栈顶地址。判断读取到的内存地址是否为合法地址是采用系统函数确定读指针合法性(IsBadReadPtr)实现的。步骤C进一步包括程序的函数调用关系发生在动态连接库中,通过程序的函数调用顺序,得到所调用的动态连接库中文件的起始地址,根据起始地址查询到动态连接库的名称。步骤C进一步包括根据记录的每层函数完成调用后返回时要执行的指令地址查询虚拟地址,得到每一条指令地址与其所属模块在内存中的起始地址的偏移量。本发明通过系统设置的Windows异常过滤钩子捕获程序异常,并将程序异常交由异常过滤钩子函数处理,异常过滤钩子函数详细记录程序异常的各种信息,完整重现程序异常发生过程,以实现对程序异常的定位。本发明很好地解决了对Windows程序各种未知异常的捕获和程序异常的定位,为程序的未知异常的捕获和定位提供了一种通用的方法。通过对程序异常的捕获和内存数据的分析得到程序异常发生的位置,提高了系统对程序异常定位的能力,提高了对产品问题定位的能力,为程序异常的处理提供了更加有效的途径。图1示出了异常过滤钩子函数进行函数调用顺序的搜索流程框图;图2示出了依据本发明重现函数调用顺序的搜索步骤示意图。具体实施例方式为了使本发明的目的、技术方案和优点更加清楚,下面结合附图对本发明作进一步地详细描述。程序异常是指程序运行时出现的非正常状况,代表了程序的某种反常状态,即程序异常状况。这种程序中出现的异常会产生某些错误。为了更好的处理程序异常,本发明提供了Windows程序异常捕获及定位方法。本发明主要包含两个方面的内容一是对Windows环境下程序未处理的异常的捕获;二是对程序异常发生位置的定位。要定位程序异常,首先要捕获程序异常。对于程序异常的捕获是通过设置Windows异常过滤钩子来实现的。Windows异常过滤钩子的设置可通过调用系统函数设置筛选器异常函数(SetUnhandleExceptionFilter)来完成。在Windows程序发生异常而程序本身又没有处理的情况下,Windows异常过滤钩子将程序异常交由异常过滤钩子函数处理。异常过滤钩子函数将记录程序异常发生时系统内各种寄存器的值、程序异常发生时的代码地址、程序异常的类型和程序异常标志。同时异常过滤钩子函数还将记录当前程序的栈内容,通过分析栈内容搜索出程序异常发生之前函数的调用顺序,并得到所调用的每一个模块在内存中的起始地址,依据起始地址查询到模块的名称或动态连接库中文件的名称,从而重现程序异常发生的过程。函数调用call指令会将完成调用后返回时要执行的下一条指令的地址压入栈中。栈是内存中的一个连续存储空间,栈的底部是一个固定地址。通常栈是由内存的高地址端向低地址端增长。栈中有两个重要的指针寄存器栈指针寄存器(ESP)是寻址栈的存储区,确定当前栈顶的位置,用于访问栈段的栈顶单元;基址寄存器(EBP)用来存放一个内存地址,指向栈段中的一个存储单元,用于访问栈段中的任意单元。在函数调用顺序的搜索过程中,根据当前EBP寄存器来定位函数进入栈时的栈顶地址,得到call指令压入的指令地址,从而得到当前函数的上层调用函数进入栈时的栈顶地址,进而重现函数的调用顺序。高级语言在编译有参数的程序时,会在栈中记录调用函数进入栈时的栈顶地址,而当前函数的栈顶地址则保存在EBP寄存器中。当程序中发生函数调用时,计算机进行如下操作首先把参数压入栈;然后保存函数调用完成返回后要执行的指令地址,作为返回地址;第三个放入栈的是EBP指针,随后把当前的栈顶指针拷贝到EBP中,作为新的基地址;最后将栈指针减去适当的数值,作为存储本地变量的预留空间。下面就程序异常定位的过程进行描述。图1示出了异常过滤钩子函数进行函数调用顺序的搜索流程框图,如图1所示步骤101~步骤102Windows异常过滤钩子捕获到程序发生异常,将程序异常从异常过滤钩子函数入口交由异常过滤钩子函数处理。异常过滤钩子函数读取当前EBP寄存器中的内存地址,即为当前栈顶的栈顶地址。步骤103系统函数确定读指针合法性函数(IsBadReadPtr)判断步骤102读取到的内存地址是否为合法地址。如果为合法地址,执行步骤104;如果为非法地址,执行步骤109。在系统中应用程序使用的内存空间是有限的,每个系统会对应用程序使用的内存空间进行限定,即内存地址应在某个范围中,当内存地址超出这个内存地址范围即为非法地址。重现函数调用顺序时,异常过滤钩子函数会不断地向上读取内存地址,判断该内存地址是否在设定的内存地址范围内,如果在设定的范围内即为合法地址;当内存地址越来越大,超出了设定的内存地址范围,即为非法地址。步骤104~步骤106步骤102读取到的内存地址为程序异常所在的当前函数进入栈时的栈顶地址;异常过滤钩子函数读取步骤102读取到的内存地址的上一个连续内存地址中存放的内容,该内容为上层调用函数完成调用当前函数后,返回时要执行的指令地址,记录该指令地址用于重现函数调用顺序。步骤107~步骤108异常过滤钩子函数读取步骤102读取到的内存地址中存放的内容,该内容为上层调用函数进入栈时的栈顶地址,读取到该内容后执行步骤103。步骤109搜索结束。读取到的内存地址为非法地址时,异常过滤钩子函数停止搜索。为了使图1的描述更加清晰,下面举例说明图1的过程。假定函数的调用顺序为函数FunA调用函数FunB,函数FunB调用函数FunC。在函数FunC中,异常过滤钩子捕获到程序异常。函数FunB进入栈时的栈顶地址为0012F6B8;函数FunB完成调用函数FunC后,返回时要执行的指令地址为00401C9D。函数FunC进入栈时的栈顶地址为0012F660。函数FunB调用函数FunC时,将调用函数FunC完成后,返回时要执行的指令地址00401C9D压入栈,然后将函数FunB进入栈时的栈顶地址0012F6B8压入栈,并将ESP寄存器中当前的栈顶地址0012F660赋值给EBP寄存器,作为函数FunC的基地址,该值即为函数FunC进入栈时的栈顶地址。为了使图1的步骤更加清晰,下面结合图2做进一步的说明。栈地址及栈地址中存放的内容均为内存地址。图2示出了依据本发明重现函数调用顺序的搜索步骤示意图,函数调用顺序的搜索步骤如图2所示异常过滤钩子函数读取当前EBP寄存器中的栈地址(内存地址)0012F660,系统函数确定读指针合法性函数(IsBadReadPtr)判断内存地址0012F660是否为合法地址。如果内存地址0012F660为合法地址,则该内存地址0012F660即为当前函数FunC进入栈时的栈顶地址,以上过程与图1的步骤102、103、104相对应。异常过滤钩子函数读取内存地址0012F660的上一个连续内存地址0012F664中存放的内容(内存地址)00401C9D。内存地址00401C9D为上层调用函数FunB完成调用函数FunC后,返回时要执行的指令地址。记录指令地址00401C9D用于重现函数调用顺序。以上过程与图1的步骤105、106相对应。异常过滤钩子函数读取内存地址0012F660中存放的内容(内存地址)0012F6B8。系统函数确定读指针合法性函数(IsBadReadPtr)判断内存地址0012F6B8是否为合法地址。如果内存地址0012F6B8为合法地址,栈地址(内存地址)0012F660中存放的内容(内存地址)0012F6B8为上层调用函数FunB调用当前函数FunC时被压入栈的基地址,内存地址0012F6B8即为上层调用函数FunB进入栈时的栈顶地址。以上过程与图1的步骤107、103、104相对应。异常过滤钩子函数读取内存地址0012F68的上一个连续内存地址0012F6BC中存放的内容(内存地址)00401A5D。内存地址00401A5D为上层调用函数FunA完成调用函数FunB后,返回时要执行的指令地址。记录指令地址00401A5D用于重现函数调用顺序。以上过程与图1的步骤105、106相对应。异常过滤钩子函数读取内存地址0012F6B8中存放的内容(内存地址)ebp。系统函数确定读指针合法性函数(IsBadReadPtr)判断内存地址ebp的合法性。如果内存地址ebp为合法地址,栈地址(内存地址)0012F6B8中存放的内容(内存地址)ebp为上层调用函数FunA调用函数FunA时被压入栈的基地址,内存地址ebp即为上层调用函数FunA进入栈时的栈顶地址。以上过程与图1的步骤107、103、104相对应。如此重复,根据EBP寄存器得到再上层调用函数进入栈时的栈顶地址和完成调用后返回时要执行的指令地址,直到读取到的内存地址为非法地址,使函数的调用顺序得以完整重现。对于一些不记录栈顶地址的函数,可以通过逐一分析内容,找到call指令压入栈的指令地址,从而确定出函数的调用顺序。这里首先假设栈中压入的内容全部为call指令压入的指令地址,然后逐一读取每一个内存地址,系统函数确定读指针合法性函数(IsBadReadPtr)判断读取到的内存地址是否为合法地址。如果为合法地址,则判断该内存地址中存放的指令内容的上一条指令是否为call指令,如果是call指令则认为是函数调用关系。依次向上搜索,判断是否为函数调用关系,直至读取到的内存地址为非法地址。在得到了函数调用关系的层次后,根据程序异常定位过程中记录的每层函数完成调用后返回时要执行的指令地址来查询虚拟地址,得到每一指令地址所属模块在内存中的起始地址。操作系统让系统看上去有比实际内存大得多的内存空间。虚拟内存可以是系统中实际物理空间的许多倍。虚拟内存系统中的所有地址都是虚拟地址而不是物理地址。每个进程都有自己的唯一虚拟地址空间。内存映射技术将映象文件和数据文件直接映射到进程的地址空间。在内存映射中,文件的内容被直接连接到进程的虚拟地址空间上。每个段都有一个定位自身起始位置的虚拟地址。因此需要完成虚拟地址到物理地址的转换。由于内存中的起始地址是内存中的实际地址,实际地址就是内存单元的物理地址。模块在每次启动时所处内存中的位置并不固定,因此得到的实际地址会有所不同。偏移地址是存储器单元所在位置到段基址的距离,在程序运行中偏移地址是固定的。所以需要得到指令地址所在的段和段内偏移地址。有了各模块的起始地址,就可以根据可移植执行体(PE-PortableExecutable)文件格式分析出模块内指令地址所在的段和段内偏移地址。PE文档是微软设计用于其所有Win32位操作系统(WindowsNT、Windows95、Win32s)的可执行文档。这样就可以得到固定的相对位置。通过模块起始地址查询模块名称的方法,获取该起始地址所在模块的模块名称。当函数调用是发生在不同的动态连接库中时,这里得到的就是动态连接库中文件的名称。有了函数调用顺序和各函数所在模块的模块名称,就能很清楚的看到在程序异常发生之前是哪些模块逐一调用了哪些函数,最终导致了程序异常的发生,这也就使程序异常的发生过程得以完整地重现。总之,以上所述仅为本发明的较佳实施例而已,并非用于限定本发明的保护范围。权利要求1.一种Windows程序异常捕获及定位方法,其特征在于包含以下步骤A、通过系统设置的Windows异常过滤钩子捕获Windows环境下程序未处理的异常,交异常过滤钩子函数处理;B、由异常过滤钩子函数记录每层函数完成调用后返回时要执行的指令地址,定位出异常发生前程序的函数调用顺序;C、通过程序的函数调用顺序,得到所调用的每一模块的起始地址,根据起始地址查询到模块的名称,完整地重现程序异常发生的过程。2.根据权利要求1所述的Windows程序异常捕获及定位方法,其特征在于所述的步骤A包括所述的Windows异常过滤钩子是通过系统函数设置筛选器异常函数(SetUnhandleExceptionFilter)实现的。3.根据权利要求1所述的Windows程序异常捕获及定位方法,其特征在于所述的步骤B,对于记录栈顶地址的函数进一步包括B11、异常过滤钩子函数读取当前基址寄存器中的内存地址;B12、系统函数判断读取到的内存地址是否为合法地址,如果为合法地址,则异常过滤钩子函数读取的内存地址为当前函数进入栈顶时的栈顶地址,如果为非法地址,则停止函数调用顺序的搜索;B13、异常过滤钩子函数读取当前基址寄存器中内存地址的上一个连续内存地址中存放的内容,该内容为上层调用函数完成调用后返回时要执行的指令地址,记录该指令地址用于重现函数调用顺序;B14、异常过滤钩子函数读取当前基址寄存器内存地址中存放的内容,该内容为上层调用函数进入栈时的栈顶地址,返回执行步骤B12。4.根据权利要求1所述的Windows程序异常捕获及定位方法,其特征在于所述的步骤B,对于不记录栈顶地址的函数进一步包括B21、异常过滤钩子函数逐一读取每一个内存地址;B22、系统函数判断读取到的内存地址是否为合法地址,如果为合法地址,继续执行,如果为非法地址,则停止函数调用顺序的搜索;B23、异常过滤钩子函数判断该内存地址中存放的指令内容的上一条指令是否为函数调用call指令,如果是call指令则认为是函数调用关系;B24、异常过滤钩子函数继续读取上一个连续内存地址,返回执行步骤B22。5.根据权利要求3所述的步骤B12进一步包括异常过滤钩子函数第一次读取的内存地址为合法地址时,该内存地址为发生程序异常的当前函数进入栈顶时的栈顶地址。6.根据权利要求3或4所述的Windows程序异常捕获及定位方法,其特征在于所述的判断读取到的内存地址是否为合法地址是采用系统函数确定读指针合法性(IsBadReadPtr)实现的。7.根据权利要求1所述的Windows程序异常捕获及定位方法,其特征在于所述的步骤C进一步包括程序的函数调用关系发生在动态连接库中,通过程序的函数调用顺序,得到所调用的动态连接库中文件的起始地址,根据起始地址查询到动态连接库的名称。8.根据权利要求1或7所述的Windows程序异常捕获及定位方法,其特征在于所述的步骤C进一步包括根据记录的每层函数完成调用后返回时要执行的指令地址查询虚拟地址,得到每一条指令地址与其所属模块在内存中的起始地址的偏移量。全文摘要本发明公开了一种Windows程序异常捕获及定位方法,涉及Windows环境下程序异常处理领域,包括通过系统设置的Windows异常过滤钩子捕获Windows环境下程序未处理的异常,并将程序异常交由异常过滤钩子函数处理,由异常过滤钩子函数定位出程序异常发生前函数的调用顺序;然后异常过滤钩子函数依据函数的调用顺序搜索到所调用的每一个模块在内存中的起始地址,再通过起始地址查询到模块的名称,使程序异常的发生过程得以完整地重现,完成对程序异常的捕获及定位,从而提高了系统对异常定位的能力,提高了对产品问题定位的能力,为异常的处理提供了更加有效的途径。文档编号G06F9/42GK1492320SQ0214596公开日2004年4月28日申请日期2002年10月25日优先权日2002年10月25日发明者黄勇,黄勇申请人:华为技术有限公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1