SEH:结构化异常处理
结构化异常处理机制提供了一个操作系统,用于优化结构的方案,为客户提供更强大的程序执行环境。试想一下,你写程序不用考虑内存访问错误,那里是空指针错误,一直在按照程序的逻辑结构来写,而无需检查功能是否成功,那将是怎样愉快的事情(但SEH宣传的字,并不意味着我的角度来看,这里是没有义务的语言之际)。
结构化异常处理--- SEH是一个操作系统级别,操作系统(windows平台为每个线程的基本单元,系统调度线程),以维持一个链表异常处理,异常发生时,控制权转移到手中的操作系统,操作系统根据一定的方式遍历列表中找到相应的处理函数,进行处理,堆栈展开
用户模式的线程中运行的操作系统FS寄存器指向线程环境块(TEB),TEB是一个用户模式访问的数据结构,的所谓NT_TIB结构嵌入TIB(线程信息块)在开幕式上,这的TIB里面保存SEH使用链表。
struct_TEB
{
NT_TIBNtTib的;
......
}
structNT_TIB
{
EXCEPTION_REGISTRATION_RECORD * ExceptionList中;
.....
}
structEXCEPTION_REGISTRATION_RECORD
{
EXCEPTION_REGISTRATION_RECORD *下一步;
enum_EXCEPTION_DISPOSITION(*处理器)(_EXCEPTION_RECORD * ExceptionRecord,无效* EstablisherFrame,_context * ContextRecord无效* DispatcherContext);
FS段线程运行点TEB结构可以看出,在下面的汇编代码里面。
是否发生异常时特定的操作系统,他们做了什么,先说说。 首先,你要了解什么是异常,这个名字以为意,异常是不寻常的地方( - ),CPU遇到异常,将触发一个中断,操作系统将取得的(控制具体的,我不能在这里详细说明),经过一系列必要的措施,以节省现成的,现成的操作系统通过FS指数TEB,工商局之间在ExceptionList然后访问,处理函数指针,给他打电话的功能,如果函数的返回,检查函数的返回值,返回值意味着他不能处理这个异常,那么对指数下指针到下一条记录,重复,链表结束,还是没有人可以处理自动杀这个线程。
处理程序是在哪里呢?在执行的时候安装的应用程序,你可能已经知道,通常指向一个处理程序称为功能_except_handler3,从上面看到,这个功能是整个SEH的关键,下面详细介绍此功能。
里面的c语言,其语法是__尝试SEH ... 。 __except .... __finally构造(具体语法,这里就不详细介绍),大家都知道,c语言编译成机器语言,然后由CPU点__尝试,这样的结构将被改造成什么样子像机器语言?和他同行汇编语言是它看起来像它? SEH的,它涉及到地面太多,尤其是内存的布局是非常重要的,所以在这里谈论这个转换的过程。
编译器遇到__试着结构,他应该知道SEH代码生成,是EXCEPTION_REGISTRATION_RECORD完成上面的链接,
push_except_handler3;这个纪录
moveax堆栈结构以上,FS:[0];原始记录
pusheax /> movfs的:[0],ESP
此代码执行完毕,堆栈又算得了什么呢? (低地址旁边的地址)
原来的记录指针| FS:[0]点这里
|现在hanlder |
只是构成了一个记录结构,也发生被连接到一起的原始清单只是为了满足操作系统的要求。
了解编译器如何安排记录,我们来看看真正的处理程序,相对来说,每个处理程序,做不同的事情,每次尝试生成的加工处理程序,这将是很麻烦,VC + +实现的,因此该处理程序点相同的功能,但是这一次,处理程序本身设有一个复杂的领域,因为他必须分开当前异常的尝试,功能,必须建立相应的数据结构去该信息处理程序正确处理。
VC保持一个叫每个函数的数据结构SCOPETABLE的,他记录功能里面一试。
typedefstruct_SCOPETABLE
{
DWORDpreviousTryLevel ;/ /尝试列表指针
DWORDlpfnFilter ;/ / __除括号内的代码地址后立即
DWORDlpfnHandler的;/ / __除外,以下括号内的代码地址
} SCOPETABLE的,PSCOPETABLE的;
VC在生成的代码为每个函数生成一个数组的SCOPETABLE,建立sehrecord此表指针也添加到堆栈中,而当前trylevel的也把这样的,这里面__except_handler3的叠加将能够访问这些数据,就能够妥善处理异常。
首先解释一下什么是trylevel trylevel标志,标记当前代码执行的位置,他居然表示尝试里面。此/>英地= 0 ;/ / trylevel = -1。
__尝试
{
= 1 ;/ /此代码,让trylevel = 0
__尝试
{
= 2 ;/ /此代码,让trylevel = 1
}
__惟(EXCEPTION_EXECUTE_HANDLER)
{
= 3;}
__尝试
{
I = 4 ;/ /执行这段代码,让trylevel = 2
}
__除外(EXCEPTION_EXECUTE_HANDLER)
{
= 5;
}
}
__除外(EXCEPTION_EXECUTE_HANDLER)
{
= 6;
}
请忽略变量i,它是内容存在的代码里面。
trylevel这是用来标记当前代码的尝试里面,这个值作为下标的索引里面SCOPETABLE,SCOPETABLE里面记录的电流对应的try __除表达,除了处理代码地址。 SCOPETABLE有prevtrylevel成员,它tryblock联系起来的搜索进程句柄,如上面的代码,如果i = 2里面的异常,一试的第一个视图对应的__除了这trylevel的索引,如果SCOPETABLE不处理,你应该尝试以外的对应,那就是,I = 6,检查,但你怎么知道其中尝试SCOPETABLE的(因为处理函数和过滤函数地址都记录在表里面)表只是里面的prevtrylevel的的prevtrylevel使用= 0,指数第一尝试SCOPETABLE,正是我们正在寻找你马上会想到I = 4对应也等于0里面prevtrylevel的的SCOPETABLE,是的,youareright只要你明白这个事实的一部分,其余的要容易得多。
接下来看看如何实际生成的汇编代码在功能代码的开头这样
pushebp
movebp的ESP
push0ffffffffh;这里是trylevel了
pushxxxx是,这是SCOPETABLE数组指针
pushfs:[0] /> movfs的:[0],ESP
subebp,20H,这里是不一定图中,使用的局部变量的函数关系
/ / try语句后相遇,
MOV [EBP-4],1,也许两个,也许是三个,你应该明白的地方该值是用来做什么的
正如你可以看到,在除了以外的集trylevel SCOPETABLE指针的处理,因为这应该是用于内部处理程序。你可能要怪的处理程序里面如何获取指针trylevel SCOPETABLE?要看看在内存布局。
[EBP-0] = prevebp
[EBP-4] = trylevel
[EBP-8] = scopetablepointer
[EBP-0C] =处理程序 [EBP-10] = prevregistrationrecord
啊......如果我们有记录指针向前的访问将是能够访问它们trylevel啊,是的,记录指针作为参数传递给你,这确实是访问变量的trylevel等方式。
最后一件事,然后进入处理函数的主体,你应该知道GetExceptionInformation GetExceptionCode()函数,你可能会奇怪,MSDN中,他提到,他们只能用在某些的地方,为什么呢?因为他们的代码实现,很奇怪的实施
的GetExceptionInformation代码
moveax,[EBP-14]
RET
你应该知道eax中保存的返回函数值,这意味着这个函数只是返回值[EBP-14],它不设置EBP(EBP是函数framepointer的价值,你应该知道EBP-XX多少的情况下是一个函数的局部变量),这意味着它返回一个局部变量的值,调用者。 VC建筑几乎trylevel的代码保留这样的空间,事实上,实行动态中的处理程序将此值设置为指向相应的地址。
ok了,该处理的身体,看看它的几个参数,首先不用说,操作系统将帮助您填写的价值观,你可以使用GetExceptionInformation获取此信息,第二个是一个void *的参数,其实时,操作系统通过当前registerationrecord地址的话,这是一个非常关键的指针的第三不用说,它是一种架构的上下文中的关系。事实上,有时指向SCOPETABLE最后一个参数,但这个参数没有使用。
这里都是伪代码的处理程序,在此之前,让我们看看在处理程序应
处理程序的主要任务是找到合适的__ except语句,检查它的返回值是HANDLER EXCEPTION_EXECUTE_。 (和当然continueexecute的)要执行除了后面的代码,否则在继续搜索,如何去上一试,上面已经很清楚
处理程序处理的情况,开展放松操作系统的处理函数被调用两次,在第一个参数是一个成员,在里面告诉你这样做是要找到治疗或放松
/ /对比上述布局的起源想想这种结构
struct_EXCEPTION_REGISTRATION
{,
struct_EXCEPTION_REGISTRATION *上一页;,
无效的(处理器)(PEXCEPTION_RECORD,PEXCEPTION_REGISTRATION,IMessageSource接口16.3.1。 PEXCEPTION_RECORD);
structscopetable_entry * SCOPETABLE;
inttrylevel的;
int_ebp;
明白任务的处理程序,请参阅是实际的代码。
int__except_handler3(_EXCEPTION_RECORD * pExceptionRecord,EXCEPTION_REGISTRATION * pRegistrationFrame _context
* pContextRecord,无效* pDispatcherContext)
{
LONGtrylevel LONGfilterFuncRet
EXCEPTION_POINTERSexceptPtrs
PSCOPETABLEpScopeTable
CLD / / Clearthedirectionflag的(makenoassumptions!),这是C语言编译器的默认操作方式
/ / ifneithertheEXCEPTION_UNWINDINGnorEXCEPTION_EXIT_UNWINDbit。
/ /使用isset ... Thisistruethefirsttimethroughthehandler(
/ /非unwindingcase)
/ /检查是不是要放松
如果(!(pExceptionRecord ExceptionFlags(EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))) BR /> {
/ /套[EBP-14]的值,记住上面所说的,[EBP-14]把它,这里ExceptionRecord的内部处理程序堆栈 / /所以它的寿命是有限的,处理函数返回时,此不存在,[EBP-14],指针会指向一个未知的领域,MSDN里面限制
/ /功能调用GetExceptionXXX的位置,明白了吗?
/ / BuildtheEXCEPTION_POINTERSstructureonthestack的,
exceptPtrs.ExceptionRecord = pExceptionRecord;,
exceptPtrs.ContextRecord = pContextRecord;
/ / PutthepointertotheEXCEPTION_POINTERS4bytesbelowthe / / establisherframe.SeeASMcodeforGetExceptionInformation的
/ /想想吧,-4点,到了什么地方?
*(PDWORD),的((PBYTE),的pRegistrationFrame-4)= &exceptPtrs;
/ / Getinitial“trylevel”的价值,看布局,然后看看该结构定义
> trylevel = pRegistrationFrame> trylevel
/ / Getapointertothescopetablearray,
SCOPETABLE = pRegistrationFrame - > SCOPETABLE;
search_for_handler:
( != / TRYLEVEL_NONE * -1 pRegistrationFrame> trylevel * /)
{
/ /如果它是空的,这意味着这是一个finally语句终于用于放松
(pRegistrationFrame的, -> SCOPETABLE [trylevel] lpfnFilter)
{。
PUSHEBP / / SavethisframeEBP
/ /! veryimportant! SwitchtooriginalEBP.Thisis
/ / whatallowsalllocalsintheframetohavethesame。的
/ / valueasbeforetheexceptionoccurred的的
/ / EBP是一个函数,对于执行该功能是非常重要的,在这里是来执行滤镜(后面括号 / /里面的语句),你必须恢复EBP EBP的值是如何恢复的?上面的代码可以看到一个movebp,ESP,ESP
/ /这是什么?根据上述的内存布局,一个很好的经验句话的含义相反,看看在前面的地址字符。
EBP。 =&pRegistrationFrame> _ebp
/ / Callthefilterfunction的声明,除括号内的检查返回值被称为
filterFuncRet的= SCOPETABLE [trylevel]。 ;
POPEBP / / RestorehandlerframeEBP的
(filterFuncRet! = EXCEPTION_CONTINUE_SEARCH)
{
(filterFuncRet <0)/ / EXCEPTION_CONTINUE_EXECUTION
> returnExceptionContinueExecution,;/ /依靠经营系统完整的continueexecution的
/ / Ifwegethere。,EXCEPTION_EXECUTE_HANDLERwasspecified
SCOPETABLE的== pRegistrationFrame - > SCOPETABLE
/ / DoestheactualOScleanupofregistrationframes
> / / Causesthisfunctiontorecurse的
/ /进行放松,操作系统将是以前的处理程序可变registrationrecord的逐一,然后断开这些记录链
__ global_unwind2 pRegistrationFrame;
BR /> / / Oncewegethere。everythingisallcleanedup,除了
/ / forthelastframe,wherewe'llcontinueexecution
EBP =&pRegistrationFrame> _ebp
/ /操作系统来帮助我们完成以前放松,目前战绩放松做<br / __ local_unwind2的的(pRegistrationFrame trylevel);
/ /调用setjmp / longjmp的支持的代码
/ /前起落架==“非本地转到(setjmp的/ longjmpstuff)
__ NLG_Notify(1);/ / EAX == SCOPETABLE的 - > lpfnHandler
/> / / SetthecurrenttryleveltowhateverSCOPETABLEentry的
/ / wasbeingusedwhenahandlerwasfound中的
/ /当前trylevel prevtrylevel显然,从目前tryblock出自然的。上tryblock是
pRegistrationFrame> trylevel = SCOPETABLE-> previousTryLevel
/ / Callthe_except。的{} block.Neverreturns,的。 BR /> / / gotoexcept声明,不返回,因为编译器不产生一个RET代码
pRegistrationFrame> SCOPETABLE的[trylevel] except语句的最后lpfnHandler();
}
}
SCOPETABLE = pRegistrationFrame> SCOPETABLE的;
trylevel = SCOPETABLE-> previousTryLevel
gotosearch_for_handler;
}
其他/ / trylevel的== {TRYLEVEL_NONE
retvalue == DISPOSITION_CONTINUE_SEARCH;
}
}
}
其他/ / EXCEPTION_UNWINDINGorEXCEPTION_EXIT_UNWINDflagsareset。
{
/ /进行出放松(触发由__ global_unwind2功能)
PUSHEBP / / SaveEBP的
EBP =与pRegistrationFrame-_ebp / /
__ local_unwind2(pRegistrationFrame TRYLEVEL_NONE)
BR /> POPEBP / / RestoreEBP。
retvalue的== DISPOSITION_CONTINUE_SEARCH;
}
}
在这里不能不提到的编译器来生成你的代码看起来
__尝试
{
= 0;
}
__除外(EXCEPTION_EXECUTE_HANDLER) { BR /> = 1;
}
据推测,在省略EBP-20的地方FS:[0]设置
__尝试: MOV [EBP -4],0; trylevel = 0
MOV [EBP-18H],ESP ESP保存
MOV [EBP-20H],0;执行i = 0
> jmp__finish; try语句
__ except_filter:
moveax EXCEPTION_EXECUTE_HANDLER返回,
RET
__ except_body:
movesp,[EBP-18H];第一恢复ESP的值,它是一个运行时堆栈
MOV [EBP-20H],1;执行i = 1;
__完成:
MOV [EBP-4] 0FFFFFFFFH trylevel = -1
几乎一致在这里我想说,到最后的,更详细的参考,我已多次提到MSJ里面的文章。
msj/0197/exception / exception.aspx的“目标=”_blank“>
http://www.microsoft.com/msj/0197/exception/exception.aspx 如果VC生成的代码更感兴趣的是,您可以使用IDA + SoftICE的动态静态跟进看到
★家★★★★位★★★如何★学★★★?