1. 程式人生 > >Windows核心程式設計筆記(十八) SEH結構化異常 二

Windows核心程式設計筆記(十八) SEH結構化異常 二

23.2 編譯器層面對系統SEH機制的封裝

23.2.1 擴充套件的EXCEPTION_REGISTRATION級相關結構:VC_EXCEPTION_REGISTRATION

(1)VC_EXCEPTION_REGISTRATION結構

複製程式碼
 struct VC_EXCEPTION_REGISTRATION
{
     VC_EXCEPTION_REGISTRATION* prev;    //前一個結構體的指標
     FARPROC                    handler; //永遠指向_exception_handler4回撥函式
     scopetable_entry
* scopetable;//指向scpoetable陣列的指標 int _index; //有的書上也叫tryLevel。scopetable項的當前項 DWORD _ebp; //當前ebp值,用於訪問各成員 }
複製程式碼

(2)scopetable_entry結構體

struct scopetable_entry
{
     DWORD     prev_entryindex;//指向前一個scopetable_entry在scopetable中的索引
     FARPROC   lpfnFilter; //
對應於__except塊後小括號內的過濾函式;__finally時為NULL FARPROC lpfnHandler;//__exception或__finally後花括號{}內的程式碼地址。 }

(3)VC異常幀堆疊佈局及VC預設的異常處理

 

23.2.2 資料結構組織

 

(1)每個函式只註冊一個VC_EXCEPTION_REGISTRATION結構(也叫異常幀,如圖中的有5個Frame,即有5個函式呼叫)。可見,該SEH異常鏈從連結串列頭部到連結串列尾共有5節點,分對應於5個異常處理幀。但需注意的是,通過VC安裝的節點為VC_EXCEPTION_REGISTRATION結構

,圖中有3個,對應的回撥函式為VCSHE!_exception_handler4(0x00E0178),而系統安裝的是EXCEPTION_REGISTRATION結構的幀,位於連結串列尾部最後的兩個節點,對應的回撥函式分別為ntdll!_exception_handler4(0x77B44FF3)和0x77B50CF5

(2)VC為每個函式內的所有__try塊建立一個scopetable表,其中每個__try塊對應於scopetable中的一項。(用scopetable_entry結構體來表示這個__try項,結構裡分別用lpfnFilter和lpfnHandler來表示__except/__finally的過濾函式和處理函式,其中_finally沒有過濾函式,只有異常處理函式)。

(3)若有__try塊巢狀,則在scopetable_entry結構裡的prev_entryindex或指明,多層巢狀形成單向連結串列。

(4)對於VC的異常處理,其每個異常幀的回撥處理函式都統一設為_except_handler4。每進入一個try塊裡,編譯器會將VC_EXCEPTION_REGISTRATION中tryLevel賦值為相應的值。一旦該try塊異常發生,系統會先從VC_EXCEPTION_REGISTRATION的handler域中找到_exception_handler4函式(C執行時庫函式),然後根據當前tryLevel的值找到scopetable表中這個__try塊相應的過濾函式和處理函式對異常進行相應的處理。

(5)與_except塊不同,_finally塊的lpfnFilter為NULL,即沒有過濾函式

23.2.3 _exception_handler4函式的執行流程

 

(1)異常發生時,根據index找到scopetable項,並呼叫lfpnFilter。如果過濾函式lpfnFilter返回EXCEPTION_EXECUTE_HANDLER,則執行全域性展開之後呼叫lpfnHandler函式。如果過濾函式lpfnFilter返回EXCEPTION_CONTINUE_EXECUTION,則_except_handler4簡單地返回EXCEPTION_CONTINUE_EXECUTION,交由系統恢復執行緒的執行

(2)如果lpfnFilter返回EXCEPTION_CONTINUE_SEARCH時,此時_except_handler4檢視previndex是否是0xFFFFFFFE,若是則_except_handler4返回ExceptionContinueSearch讓系統繼續遍歷外層SEH鏈或由系統直接處理。否則_except_handler4根據previndex找到相應的過濾函式,根據其返回值重複上面的動作。直到異常被處理或previndex為0xFFFFFFFE為止。

 【VcSEH程式】演示多層巢狀try塊的呼叫

複製程式碼
/************************************************************************
Module :ExceptFrameInfo.h
Notices:Copyright(c) Microsoft System Journal,February 1997,Matt Pietrek
MSVC 2005之後的編譯器開啟/GS選項仍可能會回滾到SEH3。不過,CRT的程式碼總是使用SEH4。
************************************************************************/
#pragma  once

#include <windows.h>
#include <stdio.h>

//-------------------------------------------------------------------
// 本程式僅適用於Visual C++,它使用的資料結構是特定於Visual C++的
//-------------------------------------------------------------------
#ifndef _MSC_VER
#error Visual C++ Required (Visual C++ specific information is displayed)
#endif

/////////////////////////////////結構定義////////////////////////////////
//作業系統定義的基本異常幀

struct EXCEPTION_REGISTRATION
{
    EXCEPTION_REGISTRATION* prev;
    FARPROC     handler;
};

//VC++擴充套件異常幀指向的資料結構
struct scopetable_entry
{
    DWORD previousTryLevel;
    FARPROC  lpfnFilter;  //過濾函式
    FARPROC  lpfnHandler; //異常處理程式實體的地址
};

//VC++使用的擴充套件異常幀
struct VC_EXCEPTION_REGISTRATION :EXCEPTION_REGISTRATION
{
    scopetable_entry* scopetable;
    int  trylevel;
    int _ebp;
};

//////////////////////////////////////////////////////////////////////////
//原型宣告
//__except_handler3是Visual C++執行時庫函式,我們想打印出它的地址,但是它的原型
//並沒有出現在任何標頭檔案中,所以需要自己宣告它。
extern "C" DWORD __security_cookie;
extern "C" int _except_handler4(PEXCEPTION_RECORD,
                                EXCEPTION_REGISTRATION*,
                                PCONTEXT,
                                PEXCEPTION_RECORD);

//////////////////////////////////////////////////////////////////////////
//顯示一個異常幀及其相應的scopetable的資訊
void ShowSEHFrame(VC_EXCEPTION_REGISTRATION* pVCExcReg){
    BOOL bVcExceptionHandler4 = pVCExcReg->handler == (FARPROC)_except_handler4; //VC的_except_handler4函式
    if (bVcExceptionHandler4){ //VC的_except_handler4函式
        printf("Frame:%08X Handler:%08X prev:%08X Scopetable:%08X\n",
               pVCExcReg, pVCExcReg->handler, pVCExcReg->prev, (DWORD)pVCExcReg->scopetable^__security_cookie);
    } else{
        printf("Frame:%08X Handler:%08X prev:%08X\n",
               pVCExcReg, pVCExcReg->handler, pVCExcReg->prev);
    }


    DWORD  iAddr = (DWORD)pVCExcReg->scopetable ^ __security_cookie;
    //iAddr = 0x77090928;//在我的系統中,這個值為offset ntdll!ResCSegmentValidateHeader+0x118e (77090928)

    //Scopetable前16個位元組幾SecurityCookie相關的欄位,後面才是scopetable_entry項
    scopetable_entry* pScopeTableEntry = (scopetable_entry*)(iAddr + 16);

    for (int i = 0; i <= pVCExcReg->trylevel; i++){

        if (bVcExceptionHandler4){ //VC的_except_handler4函式

            printf("    scopetable[%u] PreTryLevel:%08X filter:%08X __except:%08X\n",
                   i, pScopeTableEntry->previousTryLevel,
                   pScopeTableEntry->lpfnFilter,
                   pScopeTableEntry->lpfnHandler);
        }

        pScopeTableEntry++;
    }
    printf("\n");
}

//////////////////////////////////////////////////////////////////////////
//遍歷異常幀的連結串列,按順序顯示它們的資訊
void WalkSEHFrames(void){
    VC_EXCEPTION_REGISTRATION* pVCExcReg;

    //打印出_except_handler4函式的位置
    printf("_except_handler4 is at address:%08X\n", _except_handler4);
    printf("\n");

    //從FS:[0]處獲取指向連結串列頭的指標
    __asm mov eax, FS:[0]
        __asm mov[pVCExcReg], EAX

    //遍歷異常幀的連結串列。0xFFFFFFFF標誌著連結串列的結尾
    while (0xFFFFFFFF != (unsigned)pVCExcReg){
        ShowSEHFrame(pVCExcReg);
        pVCExcReg = (VC_EXCEPTION_REGISTRATION*)(pVCExcReg->prev);
    }
}
複製程式碼

//VcSEH.cpp

複製程式碼
#include <windows.h>
#include <stdio.h>
#include "ExceptionFrameInfo.h"

void test(void){
    int i = 0;
    //A塊
    __try{//第1層
        __try{ //第2層
            __try{ //第3層    
                __try{ //第4層
                     i++;
                }__finally {//第4層
                }
            }__except (EXCEPTION_CONTINUE_SEARCH){//第3層
                //這裡不會被執行
            }
        }__except (EXCEPTION_CONTINUE_SEARCH){//第2層
            //這裡不會被執行
        }
    }
    __except (EXCEPTION_EXECUTE_HANDLER){ //第1層
        //該try塊及內層發生異常時,這裡被執行
    }

    //B塊
    __try{
        WalkSEHFrames();
    }__except (EXCEPTION_CONTINUE_SEARCH){ 
    }
}

int main(){
    __try{
        test();
    }__except (EXCEPTION_EXECUTE_HANDLER){

    }
    return 0;
}
複製程式碼

23.2.4 小結:異常處理流程及全域性展開

【GlobalUnwind程式】全域性展開

複製程式碼
#include <windows.h>
#include <stdio.h>

int MyExceptionFilter(int tryLevel,DWORD dwErrorCode){
    printf("第%d層的過濾函式被執行,錯誤碼:%d!\n", tryLevel, dwErrorCode);

    //最外層裡返回同意處理;否則,繼續查詢。
    return (tryLevel == 1) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH;
}

void test(void){
    int i = 0;
    //A塊
    __try{//第1層
        __try{ //第2層
            __try{ //第3層    
                __try{ //第4層
                    RaiseException(999, 0, 0, NULL); //丟擲一個異常
                }
                __finally {//第4層
                    printf("最4層的_finally塊被執行!\n");
                }
            }
            __except (MyExceptionFilter(3, GetExceptionCode())){//第3層
                //這裡不會被執行
                printf("最3層的_except塊被執行!\n");
            }
        }
        __except (MyExceptionFilter(2, GetExceptionCode())){//第2層
            //這裡不會被執行
            printf("最2層的_except塊被執行!\n");
        }
    }
    __except (MyExceptionFilter(1, GetExceptionCode())){ //第1層
        //該try塊及內層發生異常時,這裡被執行
        printf("最1層的_except塊被執行!\n");
    }

    //B塊
    __try{

    }
    __except (EXCEPTION_CONTINUE_SEARCH){
        printf("B塊中的_except被執行!\n");
    }
}

int main(){
    __try{
        test(); //test()函式內部會處理異常,所以main中的_except塊後的程式碼不會被執行!
    }
    __except (EXCEPTION_EXECUTE_HANDLER){
        printf("main中的_except被執行!\n");

    }
    system("pause");
    return 0;
}
複製程式碼

【參考文獻】

深入解析結構化異常處理(SEH)

http://www.cppblog.com/weiym/archive/2015/02/27/209884.html

http://blog.csdn.net/bad_sheep/article/details/5803649

http://blog.csdn.net/yuzl32/article/details/5383542

http://www.mouseos.com/windows/index.html

Windows系統程式設計之結構化異常處理

http://bbs.pediy.com/showthread.php?threadid=32222

《軟體加密技術內幕》,看雪學院