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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

编译器扩展SEH(2)

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

文章目錄

  • 問題
  • 自動掛入的異常處理函數
    • __try __except嵌套,重復
    • 原始的`_EXCEPTION_REGISTRATION_RECORD`結構體
    • 編譯器拓展的_EXCEPTION_REGISTRATION_RECORD結構體
    • scopetable_entry結構體
    • scopetable_entry舉例子:
    • 第一個:
    • 第二個:
    • 第三個:
    • 第四個:
    • _except_handler4執行過程
    • 解釋:
    • trylevel

問題

我們在制作SEH鏈的時候,每增加一個,就得進行一次如下代碼

__asm{ mov eax,FS:[0] mov temp,eax lea ecx,myException mov FS:[0],ecx -------------->修改FS:[0]掛入鏈表 }

那么當編譯進行自行生成時,會不會也是每增加一個try,except,然后就壓入一個_EXCEPTION_REGISTRATION_RECORD結構體呢?接下來我們來看看如下代碼會壓入多少_EXCEPTION_REGISTRATION_RECORD結構體

#include<Windows.h> #include<iostream>int ExceptFilter(LPEXCEPTION_POINTERS pExceptionInfo) {pExceptionInfo->ContextRecord->Ecx = 1;return EXCEPTION_CONTINUE_EXECUTION; }void TestException() {__try { __try {}__except (EXCEPTION_EXECUTE_HANDLER) {printf("異常處理函數Y");}}__except (EXCEPTION_EXECUTE_HANDLER) {printf("異常處理函數X");}__try {__try { }__except (GetExceptionCode() == 0xC0000094 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {printf("異常處理函數Y");}}__except (ExceptFilter(GetExceptionInformation())) {printf("異常處理函數Z");}}int main() {TestException(); }

反匯編代碼如下:



看了一下,就只有一個_EXCEPTION_REGISTRATION_RECORD結構體,只不過這個結構體跟我們手動掛的原始_EXCEPTION_REGISTRATION_RECORD結構體不太一樣,接下來我們來解釋一下。

自動掛入的異常處理函數

_except_handler4(編譯器固定,那么異常處理函數名也就固定;隨著編譯器改變而改變)

__try __except嵌套,重復

每個使用__try __except的函數,不管其內部嵌套或反復使用多少__try, __except,都只注冊一遍,即只將一個_EXCEPTION_REGISTRATION_RECORD掛入當前線程的異常鏈表中(對于遞歸函數,每一次調用都會創建一個_EXCEPTION_REGISTRATION_RECORD,并掛入線程的異常鏈表中)。

原始的_EXCEPTION_REGISTRATION_RECORD結構體

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

然而如果編譯器的_EXCEPTION_REGISTRATION_RECORD結構體是這樣的話,那么壓入一個_EXCEPTION_REGISTRATION_RECORD結構體就能辦到__try __except重復嵌套的話,那是根本不可能的,所以需要自行拓展

編譯器拓展的_EXCEPTION_REGISTRATION_RECORD結構體

typedef struct _EXCEPTION_REGISTRATION_RECORD{ struct _EXCEPTION_REGISTRATION_RECORD *Next; void (*void)(PEXCEPTION_RECORD,PEXCEPTION_REGISTRATION, PCONTEXT,PEXCEPTION_RECORD); struct scopetable_entry *scopetable; int trylevel; int _ebp; }

這里trylevel暫時等于-2,scopetable地址等于 0x1990F0h,接下來看看這兩值有何作用

scopetable_entry結構體

scopetable_entry成員分析struct scopetable_entry{ DWORD previousTryLevel //上一個try{}結構編號 PDWRD IpfnFilter //過濾函數的起始地址(小括號里面的常量或者表達式或者函數) PDWRD IpfnHandler //異常處理程序的地址 }

scopetable[0].previousTryLevel=-2
scopetable[0]. IpfnFilter=過濾函數1
scopetable[0]. IpfnHandler=異常函數1

scopetable[1].previousTryLevel=0
scopetable[1]. IpfnFilter=過濾函數2
scopetable[1]. IpfnHandler=異常函數2

scopetable[2].previousTryLevel=-1
scopetable[2]. IpfnFilter=過濾函數3
scopetable[2]. IpfnHandler=異常函數3

scopetable[3].previousTryLevel=2
scopetable[3]. IpfnFilter=過濾函數4
scopetable[3]. IpfnHandler=異常函數4

scopetable_entry舉例子:


隨便找出一個來舉個例子:

第一個:


上一個try結構編號是-2,過濾函數的起始地址是0x19532F,異常處理程序的地址:0x195335

它把過濾函數當成一個代碼來執行,然后執行完后直接返回相應的返回結果。

緊接著,進入了異常處理程序代碼

第二個:


上一個try結構編號是0,過濾函數的起始地址是0x195309,異常處理程序的地址:0x19530f

它把過濾函數當成一個代碼來執行,然后執行完后直接返回相應的返回結果。

緊接著,進入了異常處理程序代碼

第三個:


上一個try結構編號是-1,過濾函數的起始地址是0x1953b9,異常處理程序的地址:0x1953c6

過濾函數中有一個函數,進行跳轉之后,再次進行返回,然后執行完后直接返回相應的返回結果。

緊接著,進入了異常處理程序代碼

第四個:


上一個try結構編號是2(編號為2,即上一個try位于第三個try),過濾函數的起始地址是0x195363,異常處理程序的地址:0x195399

還是把過濾函數中的當成幾段代碼,執行完之后再返回,最后進行返回值判斷,根據返回值判斷下一步如何進行

緊接著,進入了異常處理程序代碼

_except_handler4執行過程

1.CPU檢測異常 查中斷表執行處理函數 CommonDispatchException----->
KiDispatchException---->KiUserExceptionDispatcher------->RtlDispatchException—
—>VEH---->SEH

解釋:

CPU發現異常,根據異常類型來查找中斷表,查中斷表找到具體的異常處理函數,執行處理函數之后,處理函數會調用CommonDispatchException,CommonDispatchException把出異常的那些相關信息存儲起來(哪里出異常,什么類型的異常等),然后調用KiDispatchException(異常分發處理函數),這個函數會判斷這個異常是0環異常還是3環異常,如果是3環異常,那么會修正EIP,把Trap_Frame指向KiUserExceptionDispatcher,然后當線程回到三環時,會從KiUserExceptionDispatcher開始執行,這個函數第一件事就是查RtlDispatchException,來查找異常處理函數在哪里?先找VEH,然后再找SEH(fs:[0]))

當如果我們自己拋出的異常,執行流程除了前三步(CPU檢測異常 查中斷表執行處理函數 CommonDispatchException),后面的一模一樣

2.執行_except_handler4函數

  • 根據trylevel選擇scopetable數組
  • 調用scopetable數組中對應的IpfnFilter函數
    EXCEPTION_EXECUTE_HANDLER(1)執行except代碼
    EXCEPTION_CONTINUE_SEARCH(0)尋找下一個
    EXCEPTION_CONTINUE_EXECUTION(-1)重新執行
  • 如果IpfnFilter函數返回0,向上遍歷,直到previousTryLevel=-1
  • trylevel

    trylevel表示代碼正在執行在第幾個try中(它是時刻正在變化的,進入一個try或者走出一個try, trylevel都會被改變)


    trylevel初始化為-1,此時的話也就代表未處于try中

    當后面開始進入try時

    進入第一個try,trylevel 值被賦為0;進入第二個try,trylevel 值被賦為1;退出第二個try,trylevel 值被賦為0


    進入第三個try,trylevel 值被賦為2;進入第四個try,trylevel 值被賦為3;退出第四個try,trylevel 值被賦為2


    當退出所有的try時,trylevel值變為-1

    這樣的話trylevel結合scopetable數組就可以準確找到出錯位置。

    注意:
    如果出異常后,就不會往下執行了,然后異常就被CPU捕獲了,通過各種判斷,最終轉到_except_handler4,然后

  • 根據trylevel選擇scopetable數組
  • 調用scopetable數組中對應的IpfnFilter函數
    1.EXCEPTION_EXECUTE_HANDLER(1)執行except代碼
    2.EXCEPTION_CONTINUE_SEARCH(0)尋找下一個
    3.EXCEPTION_CONTINUE_EXECUTION(-1)重新執行
  • 總結

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

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