1.SEH链表的布局:
FS:[0]->因函数层层调用形成嵌套的全局SEH链表:
_SEHFrame!__SEHRegistration* SER_Prev-函数调用形成->_SEHFrame!__SEHRegistration* SER_Prev-函数调用形成->SER_Prev ... [该方向是线程全局fs:[0]链表]
| |
| |-[单个函数内部嵌套SEH链表:SEH_SEHFrame!SPF_TopTryLevel!__SEHPortableTryLevel* ]
|
|-[单个函数内部嵌套SEH链表:SEH_SEHFrame!SPF_TopTryLevel!__SEHPortableTryLevel* SPF_TopTryLevel->_SEHFrame!SPF_TopTryLevel!__SEHPortableTryLevel* SPF_TopTryLevel] ...
2.SEH组织结构:
1).如果,函数中使用了SEH机制,就会生成一个SEH节点,插入到链表fs:[0]中;
2).SEH内部又可以再嵌套使用SEH机制,内部的SEH又需要一个SEH节点来管理;
WinOS在设计上引入了面向对象的思想,在SEH中也有体现:为了体现内部嵌套的SEH与函数中最外部SEH节点的从属关系,需要将这个内部嵌套的SEH挂到外部SEH节点(已挂在fs:[0]链表中)的用于管理嵌套异常的链表头中。
鉴于上述结论,可知一个SEH节点的归属,不是在fs:[0]链表中,就是在某个fs:[0]链表中SEH节点的嵌套异常链表中。要确定SEH的位置,首先要知道插入到fs:[0]中的哪个位置;还要知道在嵌套链表中的位置,SEHFrame因此诞生,用以描述SEH节点的归属。
3.SEH组织结构的实现:
3-1).姑且称__SEHFrame结构为SEH管理结构,为什么这样称呼他?
上面提到SEH节点不是在fs:[0]链表中,就是在某个SEH节点的嵌套链表中,而这个结构正好具有这两个特征:
首先,__SEHFrame中__SEHRegistration域表明SEH是否处于fs:[0]所指向的链表中。
其次,__SEHFrame中的SPF_TopTryLevel域表明SEH节点是全局SEH链表下某个嵌套链表节点中。
下面,展开看下这个结构。
{
_SEHPortableFrame_t SEH_Header;
/*
struct __SEHPortableFrame
{
_SEHRegistration_t SPF_Registration;/*
包含单个seh框架或者局部seh框架栈(seh嵌套?)的最外层SEH节点
struct __SEHRegistration
{
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
struct __SEHRegistration* SER_Prev;
//一个具体节点的处理都由这个函数实施
_SEHFrameHandler_t SER_Handler;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/
//异常码
unsigned long SPF_Code;
//_SEHHandler_t设置为_SEHCompilerSpecificHandler
volatile _SEHHandler_t SPF_Handler;
//SPF_TopTryLevel代表具体seh框架,可能是指函数中诺干嵌套try{}except{}中的一个,
//异常发生后,从系统处理中跳转到try{}except{}中的处理,需要__SEHTryLevel结构
//__SEHTryLevel结构再下面
_SEHPortableTryLevel_t* volatile SPF_TopTryLevel;
/*
struct __SEHPortableTryLevel
{
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//每个节点代表嵌套SEH结构中某个具体的SEH框架
//
struct __SEHPortableTryLevel * volatile SPT_Next;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
volatile _SEHHandlers_t SPT_Handlers;
/*
typedef struct __SEHHandlers
{
_SEHFilter_t SH_Filter;
_SEHFinally_t SH_Finally;
}
*/
}
*/
volatile int SPF_Tracing;
}
*/
//缓冲区,存放寄存器值
void * volatile SEH_Locals;
}
3-2).SEH节点
struct __SEHTryLevel 姑且称为SEH处理结构,为什么这样称呼他?
首先这个结构跟上面的结构有所不同,没有SEH归属相关的管理信息(虽然__SEHTryLevel!__SEHPortableTryLevel!SPT_Next仍有管理从属相关的信息)。
另外,_SEHJmpBuf_t ST_JmpBuf域涉及异常出现时跳转相关,跳转的异常处理代码中,因此,个人觉得,这个称为SEH处理节点不为过。
{
_SEHPortableTryLevel_t ST_Header;
/*
struct __SEHPortableTryLevel
{
//构成局部SEH框架栈中的一个节点,
//每个节点代表具体的SEH框架
struct __SEHPortableTryLevel * volatile SPT_Next;
//过滤函数和finally函数指针
volatile _SEHHandlers_t SPT_Handlers;
/*
typedef struct __SEHHandlers
{
_SEHFilter_t SH_Filter;
_SEHFinally_t SH_Finally;
}
*/
}
*/
//异常发生后,从系统处理中跳转到try{}except{}中的处理,需要__SEHTryLevel!ST_JmpBuf保存的上下文
_SEHJmpBuf_t ST_JmpBuf;
};
4.结构介绍完,来看个SEH宏展开:
func()
{
_SEH_TRY
{
//do sth
__SEH_TRY
{
//异常了吧
_asm int 0x03;
}
_SEH_HANDLE
{
status = _SEH_GetExceptionCode();
}
_SEH_END
}
_SEH_HANDLE
{
status = _SEH_GetExceptionCode();
}
_SEH_END
}
func()
{
///
外层SEH
///
for(;;)
{
/*
初次进入函数时,_SEHScopeKind _SEHPortableFrame _SEHPortableTryLevel为文件范围的静态变量,值分别为1 0 0
经过逻辑判断,赋值_SEHTopTryLevel==1,意为这是顶层SEH
*/
_SEH_INIT_CONST int _SEHTopTryLevel = (_SEHScopeKind != 0); //(a)
/*
生成一个包含SEH节点的局部SEH框架指针(值空),_SEHCurPortableFrame ==> current frame当前框架,
上面说了静态变量_SEHPortableFrame==NULL
*/
_SEHPortableFrame_t* const _SEHCurPortableFrame = _SEHPortableFrame;
/*
局部SEH框架可能嵌套诺干SEH子框架,如
try
{
try{} ...
}
_SEHPrevPortableTryLevel应该为当前局部SEH框架中所有嵌套SEH子框架的顶层框架,
初始时_SEHPortableTryLevel==NULL;
*/
_SEHPortableTryLevel_t* const _SEHPrevPortableTryLevel = _SEHPortableTryLevel;
{
//上一个_SEHScopeKind是文件静态变量,现在定义是本层try的局部变量_SEHScopeKind
_SEH_INIT_CONST int _SEHScopeKind = 0; //(5)
register int _SEHState = 0;
register int _SEHHandle = 0;
//SEH管理变量,指明SEH的归属
//__SEHFrame!_SEHPortableTryLevel_t* SPF_TopTryLevel是一个指针成员,指向栈上变量_SEHTryLevel_t!ST_Header
_SEHFrame_t _SEHFrame;
//SEH处理结构变量
_SEHTryLevel_t _SEHTryLevel;
/*此处定义的_SEHPortableFrame、_SEHPortableTryLevel指针,与SEH结构开始时的文件静态变量_SEHPortableFrame _SEHPortableTryLevel相呼应;当创建内部嵌套的SEH处理结构时,会用到外层定义的这几个局部变量
*/
/*
(6) 经过(a)处的赋值,现在_SEHTopTryLevel==1
因此下面的?:操作符的结果使得_SEHPortableFrame的值为本层局部 SEH管理变量_SEHFrame.SEH_Header地址。而很凑巧的是,_SEHFrame!SEH_Header位于结构体起始地址,这个地址又恰好又是_SEHPortableFrame!SPF_Regstration的地址,因此,这个语句意为:如果本层SEH是最外层SEH(_SEHTopTryLevel==1),则_SEHPortableFrame指向fs:[0]
*/
_SEHPortableFrame_t* const _SEHPortableFrame =
_SEHTopTryLevel ? &_SEHFrame.SEH_Header : _SEHCurPortableFrame;
/*
_SEHPortableTryLevel指向本层局部SEH处理变量地址 (链表头)
*/
//(7)
_SEHPortableTryLevel_t* const _SEHPortableTryLevel = &_SEHTryLevel.ST_Header;
(void)_SEHScopeKind;
(void)_SEHPortableFrame;
(void)_SEHPortableTryLevel;
(void)_SEHHandle;
for(;;)
{
//(1)
if(_SEHState) /*初始时_SEHState为0,进入(2)else分支_SEHSetJmp,设置长跳转后_SEHState++;continue*/
{
for(;;)
{
{
/*这层循环中是执行受保护的代码,受保护代码执行完,会遇到下面的break,跳出_SEH_TRY*/
/*_SEHHandle _SEHTryLevel _SEHFrame定义在_SEH_TRY*/
扩展前try的开始{
///
内层SEH
///
for(;;)
{
/*
内层SEH_TRY使用的_SEHScopeKind是外层SEH_TRY定义的栈变量,初值为0,位于(5)
因此_SEHTopTryLevel=0,意为接下去的SEH处理节点不是函数的顶层节点
*/
_SEH_INIT_CONST int _SEHTopTryLevel = (_SEHScopeKind != 0);
/*
此处的_SEHPortableFrame是位于(6)的外层SEH定义的指针,指向外层SEH管理变量_SEHFrame.SEH_Header
*/
_SEHPortableFrame_t * const _SEHCurPortableFrame = _SEHPortableFrame;
/*
同上,位于(7),_SEHPortableTryLevel是外层SEH定义的指针,该指针指向外层SEH处理结构,
这么看,倒是符合Prev这个名字
*/
_SEHPortableTryLevel_t * const _SEHPrevPortableTryLevel = _SEHPortableTryLevel;
{
_SEH_INIT_CONST int _SEHScopeKind = 0;
register int _SEHState = 0;
register int _SEHHandle = 0;
_SEHFrame_t _SEHFrame;
_SEHTryLevel_t _SEHTryLevel;
/*本层的SEH管理结构。
_SEHTopTryLevel==0,_SEHPortableFrame=_SEHCurPortableFrame。而_SEHCurPortableFrame在进入内层SEH时
指向外层SEH管理变量_SEHFrame,
这是在形成函数内部嵌套SEH结构的链表?
*/
_SEHPortableFrame_t* const _SEHPortableFrame =
_SEHTopTryLevel ? &_SEHFrame.SEH_Header : _SEHCurPortableFrame;
_SEHPortableTryLevel_t * const _SEHPortableTryLevel = &_SEHTryLevel.ST_Header;
(void)_SEHScopeKind;
(void)_SEHPortableFrame;
(void)_SEHPortableTryLevel;
(void)_SEHHandle;
for(;;)
{
if(_SEHState) /*初始时_SEHState为0,进入else分支_SEHSetJmp,设置长跳转后_SEHState++;continue*/
{
for(;;)
{
{
/*这层循环中是执行受保护的代码,受保护代码执行完,会遇到下面的break,跳出_SEH_TRY*/
/*_SEHHandle _SEHTryLevel _SEHFrame定义在_SEH_TRY*/
扩展前try的开始{
///
_asm int 0x03;
///
扩展前try的结束}
}
break;
}
break; /*没有遇到异常,受保护代码跳出到if(_SEHHandle){...},判断 _SEHHandle的值*/
} /*在if((_SEHHandle = _SEHSetJmp(_SEHTryLevel.ST_JmpBuf)) == 0)中被置位0*/
else
{
if((_SEHHandle = _SEHSetJmp(_SEHTryLevel.ST_JmpBuf)) == 0)
{
_SEHTryLevel.ST_Header.SPT_Handlers.SH_Filter = (_SEH_STATIC_FILTER(_SEH_EXECUTE_HANDLER)); //执行异常处理代码
_SEHTryLevel.ST_Header.SPT_Handlers.SH_Finally = 0;
/*
_SEHPrevPortableTryLevel指向外层的SEH处理结构,
内层SEH处理结构的下一个节点指向外层SEH处理结构,形成函数内部嵌套SEH链,注意与后面外层_SEHTryLevel的区别
*/
_SEHTryLevel.ST_Header.SPT_Next = _SEHPrevPortableTryLevel;/*定义于_SEH_TRY!_SEHPortableTryLevel_t * _SEHPrevPortableTryLevel*/
_SEHFrame.SEH_Header.SPF_TopTryLevel = &_SEHTryLevel.ST_Header;
if(_SEHTopTryLevel) /*_SEH_TRY!_SEH_INIT_CONST int _SEHTopTryLevel = (_SEHScopeKind != 0);*/
{
if(&_SEHLocals != _SEHDummyLocals)
_SEHFrame.SEH_Locals = &_SEHLocals;
_SEH_EnableTracing(_SEH_DO_DEFAULT_TRACING);
_SEHFrame.SEH_Header.SPF_Handler = _SEHCompilerSpecificHandler;
_SEHEnterFrame(&_SEHFrame.SEH_Header); /*_SEHEnterFrame是个函数,用于挂入[FS:0]*/
}
//即将进入受保护的代码块去执行,即if(_SEHState)块中
++ _SEHState;
continue;
}
else
{
break;
}
}
break;
}
//链表操作,指向外层的SEH处理结构
_SEHPortableFrame->SPF_TopTryLevel = _SEHPrevPortableTryLevel;
if(_SEHHandle)
{
{
status = _SEH_GetExceptionCode();
}
}
}
if(_SEHTopTryLevel)
_SEHLeaveFrame();
break;
}
}
///
///
///
扩展前try的结束}
}
break;
}
break;
/*如果没有遇到异常,受保护代码跳出到if(_SEHHandle){...} _SEHHandle的值一直未被修改,
在首次if((_SEHHandle = _SEHSetJmp(_SEHTryLevel.ST_JmpBuf)) == 0)中被置位0,因此不执行
if(_SEHHandle){...}内的代码
*/
}
//(2),承接上面(1)处的if语句
else
{
//_SEHSetJmp注释在后面
/*
创建一个SEH处理结构_SEHTryLevel时,_SEHState为0,进入else分支调用_SEHSetJmp,设置异常出现后的跳转目标
*/
//(3)
if((_SEHHandle = _SEHSetJmp(_SEHTryLevel.ST_JmpBuf)) == 0)
{
/*
SEH处理结构的过滤/善后函数
*/
_SEHTryLevel.ST_Header.SPT_Handlers.SH_Filter = (_SEH_STATIC_FILTER(_SEH_EXECUTE_HANDLER));
_SEHTryLevel.ST_Header.SPT_Handlers.SH_Finally = 0;
/*
外层_SEHPrevPortableTryLevel指针为空,因此外层SEH处理结构_SEHTryLevel也指向空,注意与内层SEH处理结构的区别;
另外,SEH结构是先进后出的结构,_SEHTryLevel.ST_Header.SPT_Next = NULL;使得外层SEH处理结构添加到链表尾部。
*/
_SEHTryLevel.ST_Header.SPT_Next = _SEHPrevPortableTryLevel;/*定义于_SEH_TRY!_SEHPortableTryLevel_t * _SEHPrevPortableTryLevel*/
_SEHFrame.SEH_Header.SPF_TopTryLevel = &_SEHTryLevel.ST_Header;
//对于外层SEH,_SEHTopTryLevel==1
if(_SEHTopTryLevel) /*_SEH_TRY!_SEH_INIT_CONST int _SEHTopTryLevel = (_SEHScopeKind != 0);*/
{
if(&_SEHLocals != _SEHDummyLocals)
_SEHFrame.SEH_Locals = &_SEHLocals;
_SEH_EnableTracing(_SEH_DO_DEFAULT_TRACING);
_SEHFrame.SEH_Header.SPF_Handler = _SEHCompilerSpecificHandler;
//只有外层SEH处理结构会挂到FS:[0]中
_SEHEnterFrame(&_SEHFrame.SEH_Header); /*_SEHEnterFrame是个函数,用于挂入[FS:0]*/
}
/*
++ _SEHState;
continue;
这两句执行后,才有机会进入到(1)中,然后执行受保护的代码,在这个函数中是_asm int 0x03
*/
++ _SEHState;
continue;
}
else
{
break;
}
}
break;
}
//链表操作
_SEHPortableFrame->SPF_TopTryLevel = _SEHPrevPortableTryLevel;
//(5)发生异常后,会跳转到(3),由_SEHSetJmp返回1,将_SEHHandle设置成1,然后进入if(_SEHHandle) {}
if(_SEHHandle)
{
{
status = _SEH_GetExceptionCode();
}
}
}
if(_SEHTopTryLevel)
_SEHLeaveFrame();
break;
}
}
_SEHSetJmp@4:
;[esp+0],调用_SEHSetJmp时压入的函数地址
mov eax,[esp+4] ;指向参数_SEHTryLevel.ST_JmpBuf
mov ecx,[esp+0] ;ecx保存_SEHSetJmp时压入的函数地址,应该是给_SEHHandle赋值那句
;(4)
lea edx,[esp+8] ;调用_SEHSetJmp前的堆栈指针
mov [eax+0],ebp ;ctx保存到_SEHTryLevel.ST_JmpBuf
。。。
xor eax,eax ;返回值,第一次返回值为0,因此_SEHSetJmp返回后_SEHHandle赋值为0
ret 4
后期发生异常,据说会调用_SEHLongJmp@8
_SEHLongJmp@8:
mov eax,[esp+8] ;据说此时[esp+8]==1,eax做返回值
...
;对应(4)处,edx保存的返回地址,jmp的返回地址为_SEHSetJmp,返回后,_SEHHandle赋值==1,一路跳到(5)
;执行异常处理
jmp edx