• C++ EH Exception(0xe06d7363)----抛出过程


    C++ EH Exception是Windows系统VC++里对c++语言的throw的分类和定义,它的代码就是0xe06d7363。在VC++里其本质也是SEH结构化异常机制。在我们分析用户崩溃的例子中经常会遇到它。一般情况下,遇到它,是我们代码里用throw抛出异常后没有处理导致程序崩溃。下面分析一下它的原理。

    我们借助一段代码来跟踪和分析

    class MyException
    {
    public:
        int nErr;
        char *szMessage;
    public:
        MyException(void)
            :nErr(0)
            , szMessage(NULL)
        {
    
        }
    
        MyException(int nerr,char *szMess)
            :nErr(nerr)
            , szMessage(szMess)
        {
    
        }
    
        ~MyException(void)
        {
    
        }
    };
    int _tmain(int argc, _TCHAR* argv[])
    {
        try
        {
            MyException me(1, "test exception");
            throw me;
        }
        catch (MyException me1)
        {
            printf("err=%s ",me1.szMessage);
        } }

    将上述代码在VS2013里编译调试,转到汇编

    91:     try
        92:     {
    01361797  mov         dword ptr [ebp-4],0  
        93:         MyException me(1, "test exception");
    0136179E  push        1367858h  
    013617A3  push        1  
    013617A5  lea         ecx,[ebp-1Ch]  
    013617A8  call        MyException::MyException (013610D2h)  
    013617AD  mov         dword ptr [ebp-104h],eax  
    013617B3  mov         byte ptr [ebp-4],1  
        94:         throw me;
    013617B7  mov         eax,dword ptr [ebp-1Ch]  
    013617BA  mov         dword ptr [ebp-0FCh],eax  
    013617C0  mov         ecx,dword ptr [ebp-18h]  
    013617C3  mov         dword ptr [ebp-0F8h],ecx  
    013617C9  push        1369084h  
    013617CE  lea         edx,[ebp-0FCh]  
    013617D4  push        edx  
    013617D5  call        __CxxThrowException@8 (0136111Dh)  
        95:     }

    我们可以看到,throw 首先通过下面的代码

    013617B7  mov         eax,dword ptr [ebp-1Ch]  
    013617BA  mov         dword ptr [ebp-0FCh],eax  
    013617C0  mov         ecx,dword ptr [ebp-18h]  
    013617C3  mov         dword ptr [ebp-0F8h],ecx  

    复制了一份异常对象MyException me,然后取了这份拷贝的地址作为参数传给了__CxxThrowException函数,同时将异常类型信息也传递了过去

    013617CE  lea         edx,[ebp-0FCh]  
    013617D4  push        edx  
    

    接着调用了__CxxThrowException函数,我们进入看看

    下面是改函数的代码

    /////////////////////////////////////////////////////////////////////////////
    //
    // _CxxThrowException - implementation of 'throw'
    //
    // Description:
    //      Builds the NT Exception record, and calls the NT runtime to initiate
    //      exception processing.
    //
    //      Why is pThrowInfo defined as _ThrowInfo?  Because _ThrowInfo is secretly
    //      snuck into the compiler, as is the prototype for _CxxThrowException, so
    //      we have to use the same type to keep the compiler happy.
    //
    //      Another result of this is that _CRTIMP can't be used here.  Instead, we
    //      synthesisze the -export directive below.
    //
    // Returns:
    //      NEVER.  (until we implement resumable exceptions, that is)
    //
    
    // We want double underscore for CxxThrowException for ARM CE only
    __declspec(noreturn) extern "C" void __stdcall
    #if !defined(_M_ARM) || defined(_M_ARM_NT)
    _CxxThrowException(
    #else
    __CxxThrowException(
    #endif
            void*           pExceptionObject,   // The object thrown
            _ThrowInfo*     pThrowInfo          // Everything we need to know about it
    ) {
            EHTRACE_ENTER_FMT1("Throwing object @ 0x%p", pExceptionObject);
    
            static const EHExceptionRecord ExceptionTemplate = { // A generic exception record
                EH_EXCEPTION_NUMBER,            // Exception number
                EXCEPTION_NONCONTINUABLE,       // Exception flags (we don't do resume)
                NULL,                           // Additional record (none)
                NULL,                           // Address of exception (OS fills in)
                EH_EXCEPTION_PARAMETERS,        // Number of parameters
                {   EH_MAGIC_NUMBER1,           // Our version control magic number
                    NULL,                       // pExceptionObject
                    NULL,
    #if _EH_RELATIVE_OFFSETS
                    NULL                        // Image base of thrown object
    #endif
                }                      // pThrowInfo
            };
            EHExceptionRecord ThisException = ExceptionTemplate;    // This exception
    
            ThrowInfo* pTI = (ThrowInfo*)pThrowInfo;
            if (pTI && (THROW_ISWINRT( (*pTI) ) ) )
            {
                ULONG_PTR *exceptionInfoPointer = *reinterpret_cast<ULONG_PTR**>(pExceptionObject);
                exceptionInfoPointer--; // The pointer to the ExceptionInfo structure is stored sizeof(void*) infront of each WinRT Exception Info.
    
                WINRTEXCEPTIONINFO** ppWei = reinterpret_cast<WINRTEXCEPTIONINFO**>(exceptionInfoPointer);
                pTI = (*ppWei)->throwInfo;
    
                (*ppWei)->PrepareThrow( ppWei );
            }
    
            //
            // Fill in the blanks:
            //
            ThisException.params.pExceptionObject = pExceptionObject;
            ThisException.params.pThrowInfo = pTI;
    #if _EH_RELATIVE_OFFSETS
            PVOID ThrowImageBase = RtlPcToFileHeader((PVOID)pTI, &ThrowImageBase);
            ThisException.params.pThrowImageBase = ThrowImageBase;
    #endif
    
            //
            // If the throw info indicates this throw is from a pure region,
            // set the magic number to the Pure one, so only a pure-region
            // catch will see it.
            //
            // Also use the Pure magic number on Win64 if we were unable to
            // determine an image base, since that was the old way to determine
            // a pure throw, before the TI_IsPure bit was added to the FuncInfo
            // attributes field.
            //
            if (pTI != NULL)
            {
                if (THROW_ISPURE(*pTI))
                {
                    ThisException.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
                }
    #if _EH_RELATIVE_OFFSETS
                else if (ThrowImageBase == NULL)
                {
                    ThisException.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
                }
    #endif
            }
    
            //
            // Hand it off to the OS:
            //
    
            EHTRACE_EXIT;
    #if defined(_M_X64) && defined(_NTSUBSET_)
            RtlRaiseException( (PEXCEPTION_RECORD) &ThisException );
    #else
            RaiseException( ThisException.ExceptionCode,
                            ThisException.ExceptionFlags,
                            ThisException.NumberParameters,
                            (PULONG_PTR)&ThisException.params );
    #endif
    }

    可以看到,这个函数首先是创建了一个EHExceptionRecord 对象,其实对应的就是 SEH里的结构EXCEPTION_RECORD,并且给这个结构成员赋值。

    在这里通过如下代码,0xe06d7363就赋值给ThisException.ExceptionCode

    还有就是将ThrowInfo赋值给EHExceptionRecordThisException.params.pThrowInfo。_ThrowInfo 结构体定义如下:

    typedef const struct _s__ThrowInfo
    {
    unsigned int attributes;
    _PMFN pmfnUnwind;
    int (__cdecl*pForwardCompat)(...);
    _CatchableTypeArray *pCatachableTypeArray;
    } _ThrowInfo;

    结构体中重要的成员是_CatchableTypeArray。它包含了程序运行时抛出对象的类新信息(RTTI).
    如果你的程序运行时抛出一个my_exception类型的对象,那么抛出的数据参数pCatchableTypeArray包含了两个重要子数据信息。一个是typeid(my_exception),另外一个是typeid(std::exception)。

    在我们的例子里

    紧接着就调用RaiseException函数进入了异常的分发过程。

    综上,在C++ EH Exception 的异常里,EXCEPTION_RECORD结构填充如下:

    ExceptionAddress: 异常地址 76d018a2 (KERNELBASE!RaiseException+0x00000062)
       ExceptionCode: 异常代码 e06d7363 (C++ EH exception)
      ExceptionFlags: 标志 00000001
    NumberParameters: 3 or 4 64位时是4
       Parameter[0]: 19930520//魔数
       Parameter[1]: 00d5f690// 抛出的异常对象指针
       Parameter[2]: 00989084// 异常对象类型信息
       Parameter[3]: 0000000010000000 // 64位下模块句柄HINSTANCE

  • 相关阅读:
    500 cannot be cast to javax.xml.registry.infomodel
    mybatis
    [Nest] 02.nest之控制器
    [React] react-interview-01
    [JavaScript] es6规则总结
    [JavaScript] Date对象
    [Vue] vuex-interview
    [Vue] vue-router-interview
    [Vue] vue的一些面试题4
    [Vue] vue的一些面试题3
  • 原文地址:https://www.cnblogs.com/yilang/p/11541592.html
Copyright © 2020-2023  润新知