漫谈兼容内核之二十五:Windows的结构化异常处理(二)3_replicon日记

导读:



万一真的发生了嵌套异常,则CPU照样还是通过上述的路线经RtlDispatchException()进入
RtlpExecuteHandlerForException(),并且有了一个新的异常纪录块。但是实际的调用参数却不同了:首先异常纪录块是新的,
并且此时的RegistrationFrame指向临时的保护节点,而保护节点的处理函数RegistrationFrame->handler则
指向RtlpExceptionProtector()、而不再是_SEHFrameHandler()。在深入到普通节点的处理函数之前,我们不妨先看
一下RtlpExceptionProtector()的代码:



[_KiTrap14() > KiPageFaultHandler() > KiKernelTrapHandler() > KiDispatchException()

> RtlDispatchException() > RtlpExecuteHandlerForException() > _RtlpExecuteHandler2()

> RtlpExceptionProtector()]



_RtlpExceptionProtector:

    /* Assume we'll continue */

    mov eax, ExceptionContinueSearch

    /* Put the exception record in ECX and check the Flags */

    mov ecx, [esp+4]     /* 指向异常纪录块 */

    test dword ptr [ecx+EXCEPTION_RECORD_EXCEPTION_FLAGS],

                                                EXCEPTION_UNWIND

    jnz return



    /* Save the frame in ECX and Context in EDX */

    mov ecx, [esp+8]     /* 使ECX指向当前节点,这是一个保护节点 */

    mov edx, [esp+16]     /* 使EDX指向第四个参数DispatcherContext */

    /* Get the nested frame */

    mov eax, [ecx+8]     /* 当前节点中的第三个指针,指向所保护的节点 */

    /* Set it as the dispatcher context */

    mov [edx], eax     /* 通过第四个参数返回指向所保护节点的指针 */

    /* Return nested exception */

    mov eax, ExceptionNestedException

return:

    ret 16



    这里的常数EXCEPTION_UNWIND定义为

(EXCEPTION_UNWINDING + EXCEPTION_EXIT_UNWIND)。

    所以,代码中的test指令所测试的是两个标志位,只要其中有任何一个为1就执行转移指令,从而返回常数
ExceptionContinueSearch。否则就返回ExceptionNestedException,并将第二个参数、即指针
RegistrationFrame、复制到第四个参数DispatcherContext所指的地方。

   
前面RtlDispatchException()中的while循环显然在扫描ExceptionList,这是为了替新发生的异常寻找能够认领、处理
本次异常的节点。凡是新发生的异常,其异常纪录块中的上述两个标志位都是0,所以RtlpExceptionProtector()会返回
ExceptionNestedException,这就表明发生了嵌套异常。那么在什么时候会返回ExceptionContinueSearch呢?
后面读者将看到,在执行长程跳转之前,还有个“展开”的过程,在此过程中又要扫描ExceptionList中位于目标节点之前的那些节点,以调用它们的
善后函数。到那时候,就至少要把异常纪录块中的标志位EXCEPTION_UNWINDING设成1,于是这个函数就返回
ExceptionContinueSearch了,因为保护节点本身是不具备认领和处理一次异常的能力的。

    所以,真正解决问题还得靠代表着SEH框架的普通节点,而普通节点的处理函数是_SEHFrameHandler():



[_KiTrap14() > KiPageFaultHandler() > KiKernelTrapHandler() > KiDispatchException()

> RtlDispatchException() > RtlpExecuteHandlerForException() > _RtlpExecuteHandler2()

> _SEHFrameHandler()]



static int __cdecl

_SEHFrameHandler(struct _EXCEPTION_RECORD * ExceptionRecord,

      void * EstablisherFrame, struct _CONTEXT * ContextRecord, void * DispatcherContext)

{

_SEHPortableFrame_t * frame;



_SEHCleanHandlerEnvironment();

frame = EstablisherFrame;



if(ExceptionRecord->ExceptionFlags & (4 | 2)) /* Unwinding */

    _SEHLocalUnwind(frame, NULL);

else /* Handling */

{

    int ret;

    _SEHPortableTryLevel_t * trylevel;



    if(ExceptionRecord->ExceptionCode)

      frame->SPF_Code = ExceptionRecord->ExceptionCode;

    else

      frame->SPF_Code = 0xC0000001;



    for (trylevel = frame->SPF_TopTryLevel; trylevel != NULL; trylevel = trylevel->SPT_Next)

    {

      _SEHFilter_t pfnFilter = trylevel->SPT_Handlers->SH_Filter;



      switch((UINT_PTR)pfnFilter)

      {

        case (UINT_PTR)_SEH_STATIC_FILTER(_SEH_EXECUTE_HANDLER):

        case (UINT_PTR)_SEH_STATIC_FILTER(_SEH_CONTINUE_SEARCH):

        case (UINT_PTR)_SEH_STATIC_FILTER(_SEH_CONTINUE_EXECUTION):

        {

           ret = (int)((UINT_PTR)pfnFilter) - 2;

           break;

        }



        default:

        {

           if(trylevel->SPT_Handlers->SH_Filter)

           {

             EXCEPTION_POINTERS ep;

             ep.ExceptionRecord = ExceptionRecord;

             ep.ContextRecord = ContextRecord;

             ret = pfnFilter(&ep, frame);

           }

           else

             ret = _SEH_CONTINUE_SEARCH;

           break;

        }

      }



      if(ret < 0) /* _SEH_CONTINUE_EXECUTION */

        return ExceptionContinueExecution;

      else if(ret > 0) /* _SEH_EXECUTE_HANDLER */

        _SEHCallHandler(frame, trylevel);

      else /* _SEH_CONTINUE_SEARCH */

        continue;

    } /* end for */

    /* FALLTHROUGH */

} /* end if */



return ExceptionContinueSearch;

}



   
这个函数实际上把本来是两个函数的代码合在了一起,成为一个if语句,这也跟在异常处理的整个过程中要先后两次扫描ExceptionList有关。如果
异常纪录块中的标志位EXCEPTION_UNWINDING或EXCEPTION_EXIT_UNWIND为1,就说明这个SEH框架已经在“展开”的
过程中,这是为展开而调用本节点的这个函数,所以调用_SEHLocalUnwind()。否则便是在搜寻能够认领和处理本次异常的SEH框架的过程中,
是从RtlpDispatchException()中经由RtlpExecuteHandlerForException()调用下来的。所处的阶段不
同,调用的路线也就不同,这里的操作也就随之而不同。此刻我们是在后一种情景中。

   
所谓搜寻SEH框架,就是依次执行节点中各局部SEH框架的过滤函数,看看是否应该执行这个SEH框架的长程跳转,如果是就加以实施,不是就加以拒绝。如
果节点中的所有SEH框架都拒绝,就退回RtlpDispatchException()继续考察链表中的下一个节点,如果不再有下一个节点,搜寻就失败
了。

   
过滤函数根据什么信息确定是否应该执行实施函数进行长程跳转呢?我们在前面看到,作为参
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值