日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

编译器扩展SEH

發布時間:2025/3/21 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 编译器扩展SEH 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 編譯器擴展的SEH
      • 為什么需要編譯器擴展的SEH
      • 編譯器支持的SEH
      • 過濾表達式
    • try_except的實現細節
      • 手動掛入鏈表
      • 自動掛入鏈表
      • _try_except嵌套重復
    • 拓展的_EXCEPTION_REGISTRATION_RECORD結構體
      • scopetable成員分析
    • try_finally

編譯器擴展的SEH

為什么需要編譯器擴展的SEH

之前我們已經了解過SEH的異常處理流程,SEH的處理流程實際上就是在當前的堆棧中掛一個鏈表。

這種處理方式有一個比較麻煩的地方,如果我們想使用SEH這種方式來處理異常,那就意味著我們必須要做如下的幾件事:

  • 創建一個結構體,并把它掛到當前TEB的ExceptionList鏈表里
  • 編寫異常處理函數
  • 這些操作相對來說比較復雜,而編譯器對這一異常處理機制進行了拓展。

    編譯器支持的SEH

    _try 1) 掛入鏈表 {} _except(過濾表達式) 2) 異常過濾 {異常處理程序 3) 異常處理程序 }

    我們只需要將可能出現異常的代碼放到try塊里,編譯器就會替我們把異常的處理程序掛到Exception鏈表。

    過濾表達式

    except里的過濾表達式用于異常過濾,只能有以下三個值:

  • EXCEPTION_EXECUTE_HANDLER(1) 執行except代碼
  • EXCEPTION_CONTINUE_SEARCH(0) 尋找下一個異常處理函數
  • EXCEPTION_CONTINUE_EXECUTION(-1) 返回出錯位置重新執行
  • 過濾表達式只能有三種寫法:

  • 直接寫常量值
  • 表達式
  • 調用函數
  • 表達式寫法

    _try{_asm{xor edx,edxxor ecx,ecxmov eax,10idiv ecx //EDX = EAX /ECX}}//如果異常碼為0xC0000094返回1否則返回0 _except(GetExceptionCode() == 0xC0000094 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH){printf("如果出現異常 此處處理\n");}

    調用函數寫法:

    //參數根據需要來寫,可以不要參數 int ExceptFilter(LPEXCEPTION_POINTERS pExceptionInfo) {pExceptionInfo->ContextRecord->Ecx = 1;//異常處理return EXCEPTION_CONTINUE_EXECUTION;//返回出錯位置重新執行 }int main() {_try{_asm{xor edx,edxxor ecx,ecxmov eax,10idiv ecx //EDX = EAX /ECX}}//GetExceptionInformation獲取異常結構指針_except(ExceptFilter(GetExceptionInformation())){printf("如果出現異常 此處處理\n");}getchar(); }

    try_except的實現細節

    下面我們來了解一下編譯器是如何通過try_except將異常處理函數掛入鏈表的

    手動掛入鏈表

    _asm { mov eax,FS:[0] mov temp,eax lea ecx,myException mov FS:[0],ecx }

    自動掛入鏈表

    示例代碼如下:

    void TestException() {_try{}_except(1){} }

    將程序載入OD,分析編譯器生成的代碼。

    編譯器會自動幫我們修改FS:[0],將異常處理函數掛入SEH鏈表

    _try_except嵌套重復

    每個使用_try_except的函數,不管其內部嵌套或反復使用多少次,_try_except都只注冊一遍,即只將一個EXCEPTION_REGISTRATION_RECORD掛入當前線程的SEH鏈表中

    對于遞歸函數,每一次調用都會創建一個 _EXCEPTION_REGISTRATION_RECORD,并掛入線程的異常鏈表中

    typedef struct _EXCEPTION_REGISTRATION_RECORD {struct _EXCEPTION_REGISTRATION_RECORD *Next;PEXCEPTION_ROUTINE Handler; } EXCEPTION_REGISTRATION_RECORD;

    示例代碼如下:

    _try{_try{}_except(1){}}_except(1){}_try{}_except(1){}

    只要掛一次SEH鏈就會修改一次 FS[0]

    push ebpmov ebp,esppush 0FFhpush offset string "i386\\chkesp.c"+0FFFFFFD4h (00422020)push offset __except_handler3 (00401224)mov eax,fs:[00000000]push eaxmov dword ptr fs:[0],espadd esp,0B8hpush ebxpush esipush edimov dword ptr [ebp-18h],esplea edi,[ebp-58h]mov ecx,10hmov eax,0CCCCCCCChrep stos dword ptr [edi]_trymov dword ptr [ebp-4],0{_trymov dword ptr [ebp-4],1{}mov dword ptr [ebp-4],0jmp $L16979+0Ah (0040107c)_except(1)mov eax,1retmov esp,dword ptr [ebp-18h]{}mov dword ptr [ebp-4],0}mov dword ptr [ebp-4],0FFFFFFFFhjmp $L16975+0Ah (00401095)_except(1)mov eax,1retmov esp,dword ptr [ebp-18h]{}mov dword ptr [ebp-4],0FFFFFFFFh_trymov dword ptr [ebp-4],2{}mov dword ptr [ebp-4],0FFFFFFFFhjmp $L16983+0Ah (004010b5)_except(1)mov eax,1retmov esp,dword ptr [ebp-18h]{}mov dword ptr [ebp-4],0FFFFFFFFhmov ecx,dword ptr [ebp-10h]mov dword ptr fs:[0],ecxpop edipop esipop ebxmov esp,ebppop ebpret

    不管其內部嵌套或反復使用多少try_except,都只掛一次,指向的異常處理函數也是確定的。3個異常處理函數就應該有3個鏈,它只掛了一個,這時怎么實現的呢?

    拓展的_EXCEPTION_REGISTRATION_RECORD結構體

    struct _EXCEPTION_REGISTRATION{struct _EXCEPTION_REGISTRATION *prev;void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION, PCONTEXT, PEXCEPTION_RECORD);struct scopetable_entry *scopetable;int trylevel;int _ebp; };

    第一個成員和第二個成員和原來的EXCEPTION_REGISTRATION_RECORD含義一樣,后面多了三個成員。堆棧中的SEH鏈也發生了變化

    堆棧中的結構對應匯編代碼如圖:

    接下來分析學習的三個成員的含義,第三個成員比較好理解,就是當前堆棧的ebp,剩下的兩個成員才是關鍵

    struct _EXCEPTION_REGISTRATION{struct _EXCEPTION_REGISTRATION *prev;void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION, PCONTEXT, PEXCEPTION_RECORD);struct scopetable_entry *scopetable;int trylevel;int _ebp; };

    scopetable成員分析

    這個成員是一個結構體指針,結構體包含以下三個成員

    struct scopetable_entry {DWORD previousTryLevel //上一個try{}結構編號 PDWRD lpfnFilter //過濾函數的起始地址PDWRD lpfnHandler //異常處理程序的地址 }

    try_finally

    之前的課程我們講解了__try __except的的執行細節,但編譯器還提供了另外一組程序塊

    __try{ //可能出錯的代碼} __finally{ //一定要執行的代碼}

    總結

    以上是生活随笔為你收集整理的编译器扩展SEH的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。