If you're feeling a bit overwhelmed at this point by things like EXCEPTION_REGISTRATIONs, scopetables, trylevels, filter-expressions, and unwinding, so was I at first. The subject of compiler-level structured exception handling does not lend itself to learning incrementally. Much of it doesn't make sense unless you understand the whole ball of wax. When confronted with a lot of theory, my natural inclination is to write code that applies the concepts I'm learning. If the program works, I know that my understanding is (usually) correct.
前面有一大堆名词: EXCEPTION_REGISTRATIONs ( 异常注册 ), scopetables( 异常范围表 ), trylevels(try 块顺序索引 ), filter-expressions( 过滤表达式 ), unwinding( 回退 ) 。你可能会感到疑惑。而编译器生成的异常处理代码更是如此,你必须了解整个细节才能对此有所感觉。我通过一些可以工作的代码来说明我的理解。
Figure 10 is the source code for ShowSEHFrames.EXE. It uses _try/_except blocks to set up a list of several Visual C++ SEH frames. Afterwards, it displays information about each frame, as well as the scopetables that Visual C++ builds for each frame. The program doesn't generate or expect any exceptions. Rather, I included all the _try blocks to force Visual C++ to generate multiple EXCEPTION_ REGISTRATION frames, with multiple scopetable entries per frame.
下面一段代码生成了一系列的 SEH 帧并打印出来。 ( 注:这段代码在 VS2005 下需要做很多修改,因为 VS2005 编译器生成的代码和数据结构有了很多变化 )
The important functions in ShowSEHFrames are WalkSEHFrames and ShowSEHFrame. WalkSEHFrames first prints out the address of __except_handler3, the reason for which will be clear in a moment. Next, the function obtains a pointer to the head of the exception list from FS:[0] and walks each node in the list. Each node is of type VC_EXCEPTION_REGISTRATION, which is a structure that I defined to describe a Visual C++ exception handling frame. For each node in the list, WalkSEHFrames passes a pointer to the node to the ShowSEHFrame function.
ShowSEHFrames 首先打印出 __except_handler3 的地址,然后通过 FS:[0] 获取异常链表的头指针,遍历链表并打印信息。
ShowSEHFrame starts by printing the address of the exception frame, the handler callback address, the address of the previous exception frame, and a pointer to the scopetable. Next, for each scopetable entry, the code prints out the previous trylevel, the filter-expression address, and the _except block address. How do I know how many entries are in a scopetable? I don't really. Rather, I assume that the current trylevel in the VC_EXCEPTION_REGISTRATION structure is one less than the total number of scopetable entries.
ShowSEHFrame 可以获取 scopetable 并打印里面信息。
不过这段代码获取 scopetable 的地方在 VS2005 下不能工作,因为 VS2005 是这样保存 scopetable 的:
004020E5 push offset ___rtc_tzz+11Ch (403870h) <== Scope table
004020FC mov eax,dword ptr [___security_cookie (405004h)]
00402101 xor dword ptr [ebp-8],eax <== Scope table
也就是它会将 scopetable 的地址值与 ___security_cookie 做个异或,所以取得时候也得做些处理。
同样 scopetable 中有四个 int 空间保存了其他信息,从第五个 int 开始才是 scopetable 内的原素值。
You may be wondering why there are three exception frames using __except_handler3 as their callback since ShowSEHFrames plainly has only two functions that use SEH. The third frame comes from the Visual C++ runtime library. The code in CRT0.C from the Visual C++ runtime library sources shows that the call to main or WinMain is wrapped in an _try/_except block. The filter-expression code for this _try block is found in the WINXFLTR.C file.
你运行代码后可能会奇怪打印出来的信息怎么多了一个 SEH 。最后一个来自 VC 运行库,再 CRT0.C 中在调用 main/WinMain 时使用了一个 __try/__except 块包含,其相关 filter-expression 代码在 WINXFLTR.C 中。