VC++中 __try,____except( EXCEPTION_EXECUTE_HANDLER ) 结构中,except的参数是什么,求详解!

如题!

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的动态静态跟进看到

★家★★★★位★★★如何★学★★★?
温馨提示:答案为网友推荐,仅供参考
第1个回答  推荐于2018-03-07
在__excep后面的()中是一个表达式,值可以是:
EXCEPTION_CONTINUE_EXECUTION (–1) 异常被忽略,控制流将在异常出现的点之后,继续恢复运行。
EXCEPTION_CONTINUE_SEARCH (0) 异常不被识别,也即当前的这个__except模块不是这个异常错误所对应的正确的异常处理模块。系统将继续到上一层的try-except域中继续查找一个恰当的__except模块。
EXCEPTION_EXECUTE_HANDLER (1) 异常已经被识别,也即当前的这个异常错误,系统已经找到了并能够确认,这个__except模块就是正确的异常处理模块。控制流将进入到__except模块中。本回答被提问者和网友采纳