vc++6对windows SEH扩展分析 一文拾遗

前一篇文章vc++6对windows SEH扩展分析 尚有遗漏,本篇加以补齐。

其实本文参考csdn上一篇名为<Win32结构化异常处理(SEH)--异常处理程序(__try/except)>,同时提出了一些质疑。

作者罗列了vc++6.0扩展的SEH节点的结构如下:

struct _EXCEPTION_REGISTRATION
   {
     struct _EXCEPTION_REGISTRATION *prev;
     void (*handler)(PEXCEPTION_RECORD,
           PEXCEPTION_REGISTRATION,
           PCONTEXT,
           PEXCEPTION_RECORD);
     struct scopetable_entry *scopetable;
     int trylevel;
     int _ebp;
     PEXCEPTION_POINTERS xpointers;
   };

如前一篇<vc++6对windows SEH扩展分析 >所述,这个结构是在进入函数时按push ebp/push 0xff一系列操作在堆栈上形成一个_EXCEPTION_REGISTRATION节点。一般而言push ebp是进入函数的第一条语句,那么,在栈中地址高于int _ebp的指针变量xpointers是谁压入的?一般而言,堆栈中[ebp+4]是函数返回地址,因此可以认为作者可能笔误写错了PEXCEPTION_POINTERS xpointers;在整个结构中的位置。那么,这个域在哪?本文通过调试代码,回答这个问题。

产生异常的代码如下:

#include <windows.h>

int filter1(EXCEPTION_POINTERS* excpInfo)
{
	DWORD accAddr = excpInfo->ExceptionRecord->ExceptionInformation[1];
	excpInfo->ContextRecord;
	return EXCEPTION_EXECUTE_HANDLER;
}

int main()
{
	int* a = NULL;
	__try
	{
		(*a)=0xcc;
	}
	__except(filter1(GetExceptionInformation()))
	{
		MessageBox(NULL,"","",MB_OK);
	}
	return 0;

}

程序触发异常后传入4个参数然后进入except_handler3,原型如下:

int __except_handler3(
 struct _EXCEPTION_RECORD * pExceptionRecord,
 struct EXCEPTION_REGISTRATION * pRegistrationFrame,
 struct _CONTEXT *pContextRecord,
 void * pDispatcherContext );

except_handler3源码没有,忍忍看看反汇编的结果:

__except_handler3:
00401234   push        ebp
00401235   mov         ebp,esp
00401237   sub         esp,8
0040123A   push        ebx
0040123B   push        esi
0040123C   push        edi
0040123D   push        ebp
0040123E   cld
0040123F   mov         ebx,dword ptr [ebp+0Ch] ;ebx指向第二个参数
00401242   mov         eax,dword ptr [ebp+8] ;[ebp+8]指向第一个参数
00401245   test        dword ptr [eax+4],6
0040124C   jne         __except_handler3+0A0h (004012d4)
00401252   mov         dword ptr [ebp-8],eax
00401255   mov         eax,dword ptr [ebp+10h] ;[ebp+10h]指向第三个参数
00401258   mov         dword ptr [ebp-4],eax
0040125B   lea         eax,[ebp-8]
0040125E   mov         dword ptr [ebx-4],eax

注释说了,ebx指向第二个参数,这个其实是发生异常前main函数中堆栈中压入的struct _EXCEPTION_REGISTRATION结构。这段代码中有这么几句:

00401242   mov         eax,dword ptr [ebp+8] ;[ebp+8]指向第一个参数
<pre name="code" class="cpp">00401252   mov         dword ptr [ebp-8],eax
...
00401255   mov         eax,dword ptr [ebp+10h] ;[ebp+10h]指向第三个参数
00401258   mov         dword ptr [ebp-4],eax
...
<pre name="code" class="cpp">0040125E   mov         dword ptr [ebx-4],eax ;把结构的起始地址放入地址[pRegistrationFrame-4]


这段代码,把参数1 3放入某个结构中,然后把这个结构的地址传给[pRegistrationFrame-4],按照<Win32结构化异常处理(SEH)--异常处理程序(__try/except)>一文的作者描述,这个结构就是PEXCEPTION_POINTERS
xpointer。前面多次说过pRegistrationFrame是ide在堆栈上形成的结构,pRegistrationFrame-4相当于往堆栈上又压入一个4字节变量,因此,可以确定vc++6.0扩展的结构的原型应该是:

<pre name="code" class="cpp"><pre name="code" class="cpp">PEXCEPTION_POINTERS xpointers;


struct _EXCEPTION_REGISTRATION   {

    struct _EXCEPTION_REGISTRATION *prev;     void (*handler)(PEXCEPTION_RECORD,           PEXCEPTION_REGISTRATION,           PCONTEXT,           PEXCEPTION_RECORD);     struct scopetable_entry *scopetable;     int trylevel;     int _ebp;   };

两个变量紧挨在一起


当然也可以查看内存值:

触发异常之前,ebp=0x12ff48,trylevel=0x00,handler=0x403118,prev=0x12ff78;而0x12ff34,即xpointer处为0x83;

触发异常执行到

<pre name="code" class="cpp"><pre name="code" class="cpp">mov         dword ptr [ebx-4],eax ;把结构的起始地址放入地址[pRegistrationFrame-4]


语句之后

0x12FF34处值为0x12FAEC。而地址0012FAEC处的内存为:

0012FAE4  00 00 00 00 00 00 00 00  ........
0012FAEC  E0 FB 12 00 FC FB 12 00  帑....
0012FAF4  18 FB 12 00 79 71 85 77  ....yq厀

明眼人一看0x12FBE0和0x12FBFC就知道是堆栈值。可以在按ebp的值查看函数参数和返回地址:

0012FAEC  25 E1 82 77 DD AE 6C 00  %醾w莓l.
0012FAF4  18 FB 12 00 79 71 85 77  ....yq厀
0012FAFC  E0 FB 12 00 38 FF 12 00  帑..8...
0012FB04  FC FB 12 00 B4 FB 12 00

这两个值果然是except_handler3的两个参数。

由此至少可以说明

<pre name="code" class="cpp"><pre name="code" class="cpp"><pre name="code" class="cpp">PEXCEPTION_POINTERS xpointers;

struct _EXCEPTION_REGISTRATION   {

    struct _EXCEPTION_REGISTRATION *prev;

void (*handler)(PEXCEPTION_RECORD,PEXCEPTION_REGISTRATION,PCONTEXT,PEXCEPTION_RECORD);

struct scopetable_entry *scopetable;

int trylevel;

int _ebp;

};



这个结构才是vc6扩展的SEH节点。到此本文可以完结了,但是调试代码时发现一个有趣的地方,就是从except_handler3跳去过滤函数执行时,except_handler3为了使得过滤函数中能使用异常函数中定义的栈变量,用了save/load ebp大法:

00401273   push        ebp
00401274   lea         ebp,[ebx+10h]
00401277   call        dword ptr [edi+ecx*4+4]

call指令将调用scopetable数组中定义的过滤函数,然而在此之前except_handler3从[ebx+10h]处恢复ebp的值,那么[ebx+10h]是啥?首先可以确定ebx还是进入except_handler3时设置的ebx,其指向_EXCEPTION_REGISTRATION。_EXCEPTION_REGISTRATION+10H不就是_EXCEPTION_REGISTRATION!ebp吗?这个ebp是进入main函数时保存的函数帧,因此可以通过相对于ebp的偏移取得main函数中的变量。

至于GetExceptionInformation()函数,就是取except_handler3设置的xpointer地址:

18:       __except(filter1(GetExceptionInformation()))
004010B5   mov         ecx,dword ptr [ebp-14h]
004010B8   push        ecx
004010B9   call        @ILT+5(filter1) (0040100a)
004010BE   add         esp,4

在正式进入过滤函数后,由于有一次push ebp修改了函数栈帧,因此不能在过滤函数用调用GetExceptionInformation


版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-18 17:30:37

vc++6对windows SEH扩展分析 一文拾遗的相关文章

windows客户端崩溃分析和调试

本文介绍windows上崩溃分析的一些手段,顺便提多进程调试.死锁等. 1.崩溃分析过程 1.1 确认错误码 无论是用windbg还是用vs,首先应该注意的是错误码,而90%以上的崩溃都是非法访问. 在非法访问时,可以看一下访问的目标地址.地址是0,或者离0很近(0x00000008或0xfffffffc), 一般和空指针相关.如果是一个貌似正常的地址,一般是对象已析构后访问其数据,或者堆破坏. 1.2确认崩溃对应的C++操作 什么是确认崩溃对应的C++操作: 比如非法访问,通常得有个mov指令

Windows系统内存分析工具的介绍

? Windows系统内存分析工具的介绍(进程管理器,资源管理器,性能监视器, VMMap, RamMap,PoolMon) 微软官方提供多种工具来分析Windows 的内存使用情况,除了系统自带的任务管理器(Task Manager), 资源监视器(Resource Manager), 性能监视器(Performance Monitor), 还有SysInternals工具, ?RamMap, PoolMon用以分析内存问题.本文简单介绍上述工具的快速使用方法,如果需要了解深入了解,请参考微软

windows下扩展yaf,并生成yaf框架文件

YAF中文文档:http://www.laruence.com/manual/index.html 1 YAF框架是用C开发的,属于PHP的扩展框架: 2 YAF的性能相对于源生PHP,性能只降低不到10%: 下面直接切入正题,如何在windows下扩展yaf并生成yaf框架文件(linux下就不赘述了,有很多资源讲解) 本机环境 PHP5.6.24 Apache 2.0 Handler YAF框架配置 要使用YAF首先要开启PHP的yaf扩展,由于集成环境不会自带所以我们得自己去下载: yaf

MiinCMP SAE版修正windows平台下无法获取文档问题

原因:在本地测试时,出现文件路径错误,windows下路径为\ROOT\WEB-INF\,统一转换为/ROOT/WEB-INF/ @Override     public String readFile(File file,String encode){                 String pathweb=parentWebDir;// +"/"+  path;       File f=new File(pathweb);              String path=

iOS Crash 分析(文一)- 开始

iOS Crash 分析(文一)- 开始 1. 名词解释 1. UUID 一个字符串,在iOS上每个可执行文件或库文件都包含至少一个UUID.目的是为了唯一识别这个文件. 2. dwarfdump 苹果提供的命令行工具,其中一些功能就是查看可执行文件件或库文件的UUID 3. symbolicatecrash 一个苹果提供的脚本.可以将crash日志符号化为可读的堆栈信息. 4. atosl 苹果提供的命令行工具,可以将crash的base_address和load_address转化为可读的堆

iOS Crash 分析(文二)-崩溃日志组成

iOS Crash 分析(文二)-崩溃日志组成 现在我们看一个淘宝iOS主客崩溃的例子: ### 1.进程信息 ### Incident Identifier: E4201F10-6F5F-40F9-B938-BB3DA8ED7D50 CrashReporter Key: TODO Hardware Model: iPhone4,1 Process: Taobao4iPhone [3538] Path: /var/mobile/Applications/E3B51E77-D44D-4B3E-87

iOS Crash 分析(文三)- 符号化崩溃日志

iOS Crash 分析(文三)- 符号化崩溃日志 未符号化的崩溃日志就象一本天书,看不懂,更别谈分析崩溃原因了.所以我们在分析日志之前,要把日志翻译成我们可以看得懂的文字.这一步我们称之为符号化. 在iOS Crash分析(文一)中已经提到过符号化的两种方式: 1.利用Xcode符号化 2.利用symbolicatecrash脚本符号化 其实这两种分析方式都使用了同一个工具符号化:***atos***. atos是苹果提供的符号化工具,在Mac OS系统下默认安装. 使用***atos***符

Visual Studio 实用扩展推荐   Visual Studio 拥有非常不错的可扩展性,在之前的文章中,我也给大家示范了如何进行编辑器的扩展(详见文末参考资源)。在本篇文章中,我将介绍几款非常实用的扩展,从而帮助我们提高开发效率。 C# outline   Visual Studio 默认的大纲方案只允许在方法级别及以上进行代码的折叠,无法对一个if、while的区块进行折叠

Visual Studio 实用扩展推荐 Visual Studio 拥有非常不错的可扩展性,在之前的文章中,我也给大家示范了如何进行编辑器的扩展(详见文末参考资源).在本篇文章中,我将介绍几款非常实用的扩展,从而帮助我们提高开发效率. C# outline Visual Studio 默认的大纲方案只允许在方法级别及以上进行代码的折叠,无法对一个if.while的区块进行折叠,而这款工具则正好弥补了这个问题. highlight all occurrences of selected word

Windows SEH学习 x86

windows 提供的异常处理机制实际上只是一个简单的框架.咱通常所用的异常处理(比如 C++ 的 throw.try.catch)都是编译器在系统提供的异常处理机制上进行加工了的增强版本.这里先抛开增强版的不提,先说原始版本.     原始版本的机制很简单:谁都可以触发异常,谁都可以处理异常(只要它能看得见).但是不管是触发还是处理都得先注册.系统把这些注册信息保存在一个链表里,并且这个链表保存在线程的数据结构里.也就是说,异常所涉及的一些行为都是线程相关的.比如,线程 T1 触发的异常就只能