天天看點

windows SEH機制注釋(1) 基于ReactOS [第二次修訂]

1.SEH連結清單的布局:

FS:[0]->因函數層層調用形成嵌套的全局SEH連結清單:

_SEHFrame!__SEHRegistration* SER_Prev-函數調用形成->_SEHFrame!__SEHRegistration* SER_Prev-函數調用形成->SER_Prev ... [該方向是線程全局fs:[0]連結清單]

        |                                                                                                   |

        |                                                                                                   |

        |                                                                                                   |-[單個函數内部嵌套SEH連結清單:SEH_SEHFrame!SPF_TopTryLevel!__SEHPortableTryLevel* ]

        |

        |-[單個函數内部嵌套SEH連結清單:SEH_SEHFrame!SPF_TopTryLevel!__SEHPortableTryLevel* SPF_TopTryLevel->_SEHFrame!SPF_TopTryLevel!__SEHPortableTryLevel* SPF_TopTryLevel] ...

2.SEH組織結構:

1).如果,函數中使用了SEH機制,就會生成一個SEH節點,插入到連結清單fs:[0]中;

2).SEH内部又可以再嵌套使用SEH機制,内部的SEH又需要一個SEH節點來管理;

    WinOS在設計上引入了面向對象的思想,在SEH中也有展現:為了展現内部嵌套的SEH與函數中最外部SEH節點的從屬關系,需要将這個内部嵌套的SEH挂到外部SEH節點(已挂在fs:[0]連結清單中)的用于管理嵌套異常的連結清單頭中。

鑒于上述結論,可知一個SEH節點的歸屬,不是在fs:[0]連結清單中,就是在某個fs:[0]連結清單中SEH節點的嵌套異常連結清單中。要确定SEH的位置,首先要知道插入到fs:[0]中的哪個位置;還要知道在嵌套連結清單中的位置,SEHFrame是以誕生,用以描述SEH節點的歸屬。

3.SEH組織結構的實作:

3-1).姑且稱__SEHFrame結構為SEH管理結構,為什麼這樣稱呼他?

上面提到SEH節點不是在fs:[0]連結清單中,就是在某個SEH節點的嵌套連結清單中,而這個結構正好具有這兩個特征:

首先,__SEHFrame中__SEHRegistration域表明SEH是否處于fs:[0]所指向的連結清單中。

其次,__SEHFrame中的SPF_TopTryLevel域表明SEH節點是全局SEH連結清單下某個嵌套連結清單節點中。

下面,展開看下這個結構。

typedef struct __SEHFrame

 {

     _SEHPortableFrame_t SEH_Header;

         /*

         struct __SEHPortableFrame

        {
             _SEHRegistration_t SPF_Registration;

                 /*

                 包含單個seh架構或者局部seh架構棧(seh嵌套?)的最外層SEH節點

                 struct __SEHRegistration

                {
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                     //用以加入全局ExceptionList隊列[fs:0]

                     struct __SEHRegistration* SER_Prev;

                     //一個具體節點的處理都由這個函數實施

                    _SEHFrameHandler_t SER_Handler;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                 }

                 */

             //異常碼

             unsigned long SPF_Code;

             //_SEHHandler_t設定為_SEHCompilerSpecificHandler

             volatile _SEHHandler_t SPF_Handler;

             //SPF_TopTryLevel代表具體seh架構,可能是指函數中諾幹嵌套try{}except{}中的一個,

             //異常發生後,從系統進行中跳轉到try{}except{}中的處理,需要__SEHTryLevel結構

             //__SEHTryLevel結構再下面

             _SEHPortableTryLevel_t* volatile SPF_TopTryLevel;

                 /*

                 struct __SEHPortableTryLevel

                {
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                     //構成局部SEH架構棧中的一個節點,

                     //每個節點代表嵌套SEH結構中某個具體的SEH架構

                     //

                    struct __SEHPortableTryLevel * volatile SPT_Next;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                     //與該SEH節點相關的過濾函數和finally函數指針

                     volatile _SEHHandlers_t SPT_Handlers;

                     /*

                         typedef struct __SEHHandlers

                         {

                         _SEHFilter_t SH_Filter;

                         _SEHFinally_t SH_Finally;

                         }

                     */

                 }

                 */

             volatile int SPF_Tracing;

         }

         */

     //緩沖區,存放寄存器值

     void * volatile SEH_Locals;

 }      

3-2).SEH節點

struct __SEHTryLevel 姑且稱為SEH處理結構,為什麼這樣稱呼他?

首先這個結構跟上面的結構有所不同,沒有SEH歸屬相關的管理資訊(雖然__SEHTryLevel!__SEHPortableTryLevel!SPT_Next仍有管理從屬相關的資訊)。

另外,_SEHJmpBuf_t ST_JmpBuf域涉及異常出現時跳轉相關,跳轉的異常處理代碼中,是以,個人覺得,這個稱為SEH處理節點不為過。

struct __SEHTryLevel

 {

     _SEHPortableTryLevel_t ST_Header;

         /*

         struct __SEHPortableTryLevel

         {

             //構成局部SEH架構棧中的一個節點,

             //每個節點代表具體的SEH架構

             struct __SEHPortableTryLevel * volatile SPT_Next;

             //過濾函數和finally函數指針

             volatile _SEHHandlers_t SPT_Handlers;

             /*

                 typedef struct __SEHHandlers

                 {

                 _SEHFilter_t SH_Filter;

                 _SEHFinally_t SH_Finally;

                 }

             */

         }

         */

     //異常發生後,從系統進行中跳轉到try{}except{}中的處理,需要__SEHTryLevel!ST_JmpBuf儲存的上下文

     _SEHJmpBuf_t ST_JmpBuf;

 };

4.結構介紹完,來看個SEH宏展開:
func()
 {
     _SEH_TRY
     {
         //do sth
         __SEH_TRY
         {           //異常了吧
             _asm int 0x03;
         }
         _SEH_HANDLE
         {
             status = _SEH_GetExceptionCode();
         }
         _SEH_END
     }
     _SEH_HANDLE
     {
         status = _SEH_GetExceptionCode();
     }
     _SEH_END
 }

 func()
 {
     ///
     外層SEH
     ///
     for(;;) 
     { 
         /*
         初次進入函數時,_SEHScopeKind _SEHPortableFrame _SEHPortableTryLevel為檔案範圍的靜态變量,值分别為1 0 0
         經過邏輯判斷,指派_SEHTopTryLevel==1,意為這是頂層SEH
         */
         _SEH_INIT_CONST int _SEHTopTryLevel = (_SEHScopeKind != 0);  //(a)
         
         /*
         生成一個包含SEH節點的局部SEH架構指針(值空),_SEHCurPortableFrame ==> current frame目前架構,        上面說了靜态變量_SEHPortableFrame==NULL
        */
         _SEHPortableFrame_t* const _SEHCurPortableFrame = _SEHPortableFrame;
         
         /*
         局部SEH架構可能嵌套諾幹SEH子架構,如
             try
             {
                 try{} ...
             }
         _SEHPrevPortableTryLevel應該為目前局部SEH架構中所有嵌套SEH子架構的頂層架構,        初始時_SEHPortableTryLevel==NULL;
        */
         _SEHPortableTryLevel_t* const _SEHPrevPortableTryLevel = _SEHPortableTryLevel;
      
         { 
             //上一個_SEHScopeKind是檔案靜态變量,現在定義是本層try的局部變量_SEHScopeKind
             _SEH_INIT_CONST int _SEHScopeKind = 0;  //(5)
             register int _SEHState = 0; 
             register int _SEHHandle = 0;              //SEH管理變量,指明SEH的歸屬
             //__SEHFrame!_SEHPortableTryLevel_t* SPF_TopTryLevel是一個指針成員,指向棧上變量_SEHTryLevel_t!ST_Header
             _SEHFrame_t _SEHFrame; 
             //SEH處理結構變量
             _SEHTryLevel_t _SEHTryLevel;             
             /*此處定義的_SEHPortableFrame、_SEHPortableTryLevel指針,與SEH結構開始時的檔案靜态變量_SEHPortableFrame _SEHPortableTryLevel相呼應;當建立内部嵌套的SEH處理結構時,會用到外層定義的這幾個局部變量            */
            /*
             (6) 經過(a)處的指派,現在_SEHTopTryLevel==1
             是以下面的?:操作符的結果使得_SEHPortableFrame的值為本層局部 SEH管理變量_SEHFrame.SEH_Header位址。而很湊巧的是,_SEHFrame!SEH_Header位于結構體起始位址,這個位址又恰好又是_SEHPortableFrame!SPF_Regstration的位址,是以,這個語句意為:如果本層SEH是最外層SEH(_SEHTopTryLevel==1),則_SEHPortableFrame指向fs:[0]
             */
             _SEHPortableFrame_t* const _SEHPortableFrame = 
                 _SEHTopTryLevel ? &_SEHFrame.SEH_Header : _SEHCurPortableFrame; 
               /*             _SEHPortableTryLevel指向本層局部SEH處理變量位址 (連結清單頭)
             */  
            //(7)
             _SEHPortableTryLevel_t* const _SEHPortableTryLevel = &_SEHTryLevel.ST_Header;
     
             (void)_SEHScopeKind; 
             (void)_SEHPortableFrame; 
             (void)_SEHPortableTryLevel; 
             (void)_SEHHandle; 
     
             for(;;) 
             { 
                 //(1)
                 if(_SEHState) /*初始時_SEHState為0,進入(2)else分支_SEHSetJmp,設定長跳轉後_SEHState++;continue*/
                 { 
                     for(;;) 
                     { 
                         {
                         /*這層循環中是執行受保護的代碼,受保護代碼執行完,會遇到下面的break,跳出_SEH_TRY*/
                         /*_SEHHandle _SEHTryLevel _SEHFrame定義在_SEH_TRY*/
                             擴充前try的開始{
                             
                             
                                 ///
                                 内層SEH
                                 ///
                                 for(;;)
                                 {
                                     /*
                                     内層SEH_TRY使用的_SEHScopeKind是外層SEH_TRY定義的棧變量,初值為0,位于(5)
                                     是以_SEHTopTryLevel=0,意為接下去的SEH處理節點不是函數的頂層節點
                                     */
                                     _SEH_INIT_CONST int _SEHTopTryLevel = (_SEHScopeKind != 0);
                                     
                                     /*
                                     此處的_SEHPortableFrame是位于(6)的外層SEH定義的指針,指向外層SEH管理變量_SEHFrame.SEH_Header
                                     */
                                     _SEHPortableFrame_t * const _SEHCurPortableFrame = _SEHPortableFrame;
                                     
                                     
                                     /*
                                     同上,位于(7),_SEHPortableTryLevel是外層SEH定義的指針,該指針指向外層SEH處理結構,
                                     這麼看,倒是符合Prev這個名字
                                     */
                                     _SEHPortableTryLevel_t * const _SEHPrevPortableTryLevel = _SEHPortableTryLevel;
                                  
                                     {
                                         _SEH_INIT_CONST int _SEHScopeKind = 0;
                                         register int _SEHState = 0;
                                         register int _SEHHandle = 0;
                                         _SEHFrame_t _SEHFrame;
                                         _SEHTryLevel_t _SEHTryLevel;
                                         /*本層的SEH管理結構。
                                         _SEHTopTryLevel==0,_SEHPortableFrame=_SEHCurPortableFrame。而_SEHCurPortableFrame在進入内層SEH時                                        指向外層SEH管理變量_SEHFrame,
                                         這是在形成函數内部嵌套SEH結構的連結清單?
                                         */
                                         _SEHPortableFrame_t* const _SEHPortableFrame =
                                             _SEHTopTryLevel ? &_SEHFrame.SEH_Header : _SEHCurPortableFrame;
                                             
                                         _SEHPortableTryLevel_t * const _SEHPortableTryLevel = &_SEHTryLevel.ST_Header;
                                
                                         (void)_SEHScopeKind;
                                         (void)_SEHPortableFrame;
                                         (void)_SEHPortableTryLevel;
                                         (void)_SEHHandle;
                                
                                         for(;;)
                                         {
                                             if(_SEHState) /*初始時_SEHState為0,進入else分支_SEHSetJmp,設定長跳轉後_SEHState++;continue*/
                                             {
                                                 for(;;)
                                                 {
                                                     {
                                                     /*這層循環中是執行受保護的代碼,受保護代碼執行完,會遇到下面的break,跳出_SEH_TRY*/
                                                     /*_SEHHandle _SEHTryLevel _SEHFrame定義在_SEH_TRY*/
                                                         擴充前try的開始{
                                                        
                                                        
                                                             ///
                                                             _asm int 0x03;
                                                             ///
                                                            
                                                            
                                                         擴充前try的結束}
                                                     }
                                
                                                     break;
                                                 }
                                
                                                 break; /*沒有遇到異常,受保護代碼跳出到if(_SEHHandle){...},判斷 _SEHHandle的值*/
                                             } /*在if((_SEHHandle = _SEHSetJmp(_SEHTryLevel.ST_JmpBuf)) == 0)中被置位0*/
                                             else
                                             {
                                                 if((_SEHHandle = _SEHSetJmp(_SEHTryLevel.ST_JmpBuf)) == 0)
                                                 {
                                                     _SEHTryLevel.ST_Header.SPT_Handlers.SH_Filter = (_SEH_STATIC_FILTER(_SEH_EXECUTE_HANDLER)); //執行異常處理代碼
                                                     _SEHTryLevel.ST_Header.SPT_Handlers.SH_Finally = 0;
                                                     /*
                                                     _SEHPrevPortableTryLevel指向外層的SEH處理結構,
                                                     内層SEH處理結構的下一個節點指向外層SEH處理結構,形成函數内部嵌套SEH鍊,注意與後面外層_SEHTryLevel的差別
                                                     */
                                                     _SEHTryLevel.ST_Header.SPT_Next = _SEHPrevPortableTryLevel;/*定義于_SEH_TRY!_SEHPortableTryLevel_t * _SEHPrevPortableTryLevel*/
                                                     _SEHFrame.SEH_Header.SPF_TopTryLevel = &_SEHTryLevel.ST_Header;
                                
                                                     if(_SEHTopTryLevel) /*_SEH_TRY!_SEH_INIT_CONST int _SEHTopTryLevel = (_SEHScopeKind != 0);*/
                                                     {
                                                         if(&_SEHLocals != _SEHDummyLocals)
                                                             _SEHFrame.SEH_Locals = &_SEHLocals;
                                
                                                         _SEH_EnableTracing(_SEH_DO_DEFAULT_TRACING);
                                                         _SEHFrame.SEH_Header.SPF_Handler = _SEHCompilerSpecificHandler;
                                                         _SEHEnterFrame(&_SEHFrame.SEH_Header); /*_SEHEnterFrame是個函數,用于挂入[FS:0]*/
                                                     }
                                                     //即将進入受保護的代碼塊去執行,即if(_SEHState)塊中                                                    ++ _SEHState;
                                                     continue;
                                                 }
                                                 else
                                                 {
                                                     break;
                                                 }
                                             }
                                
                                             break;
                                         }
                                         //連結清單操作,指向外層的SEH處理結構
                                         _SEHPortableFrame->SPF_TopTryLevel = _SEHPrevPortableTryLevel;
                                
                                         if(_SEHHandle)
                                         {
                                             {
                                                 status = _SEH_GetExceptionCode();
                                             }
                                         }
                                     }
                                
                                     if(_SEHTopTryLevel)
                                         _SEHLeaveFrame();
                                
                                     break;
                                 }
                             }
                             ///
                             ///
                             ///
                                 
                                 
                             擴充前try的結束}
                         } 
     
                         break; 
                     } 
     
                     break; 
                     /*如果沒有遇到異常,受保護代碼跳出到if(_SEHHandle){...} _SEHHandle的值一直未被修改,
                     在首次if((_SEHHandle = _SEHSetJmp(_SEHTryLevel.ST_JmpBuf)) == 0)中被置位0,是以不執行
                     if(_SEHHandle){...}内的代碼
                     */
                 } 
                 //(2),承接上面(1)處的if語句
                 else 
                 { 
                     //_SEHSetJmp注釋在後面
                     /*
                     建立一個SEH處理結構_SEHTryLevel時,_SEHState為0,進入else分支調用_SEHSetJmp,設定異常出現後的跳轉目标
                     */
                     //(3)
                     if((_SEHHandle = _SEHSetJmp(_SEHTryLevel.ST_JmpBuf)) == 0) 
                     { 
                         /*
                         SEH處理結構的過濾/善後函數
                         */
                         _SEHTryLevel.ST_Header.SPT_Handlers.SH_Filter = (_SEH_STATIC_FILTER(_SEH_EXECUTE_HANDLER));
                         _SEHTryLevel.ST_Header.SPT_Handlers.SH_Finally = 0; 
                         /*
                         外層_SEHPrevPortableTryLevel指針為空,是以外層SEH處理結構_SEHTryLevel也指向空,注意與内層SEH處理結構的差別;                       另外,SEH結構是先進後出的結構,_SEHTryLevel.ST_Header.SPT_Next = NULL;使得外層SEH處理結構添加到連結清單尾部。
                        */
                         _SEHTryLevel.ST_Header.SPT_Next = _SEHPrevPortableTryLevel;/*定義于_SEH_TRY!_SEHPortableTryLevel_t * _SEHPrevPortableTryLevel*/
                         _SEHFrame.SEH_Header.SPF_TopTryLevel = &_SEHTryLevel.ST_Header;                         //對于外層SEH,_SEHTopTryLevel==1
                         if(_SEHTopTryLevel) /*_SEH_TRY!_SEH_INIT_CONST int _SEHTopTryLevel = (_SEHScopeKind != 0);*/
                         { 
                             if(&_SEHLocals != _SEHDummyLocals) 
                                 _SEHFrame.SEH_Locals = &_SEHLocals; 
     
                             _SEH_EnableTracing(_SEH_DO_DEFAULT_TRACING); 
                             _SEHFrame.SEH_Header.SPF_Handler = _SEHCompilerSpecificHandler;
                             //隻有外層SEH處理結構會挂到FS:[0]中
                             _SEHEnterFrame(&_SEHFrame.SEH_Header); /*_SEHEnterFrame是個函數,用于挂入[FS:0]*/
                         } 
                         /*
                         ++ _SEHState; 
                         continue; 
                         這兩句執行後,才有機會進入到(1)中,然後執行受保護的代碼,在這個函數中是_asm int 0x03
                         */
                         ++ _SEHState; 
                         continue; 
                     } 
                     else 
                     { 
                         break; 
                     } 
                 } 
     
                 break; 
             } 
             //連結清單操作
             _SEHPortableFrame->SPF_TopTryLevel = _SEHPrevPortableTryLevel; 
             
             //(5)發生異常後,會跳轉到(3),由_SEHSetJmp傳回1,将_SEHHandle設定成1,然後進入if(_SEHHandle) {}
             if(_SEHHandle) 
             {
                 {
                     status = _SEH_GetExceptionCode();
                 }
             } 
         } 
     
         if(_SEHTopTryLevel) 
             _SEHLeaveFrame(); 
     
         break; 
     }
 }      

繼續閱讀