2 回答

TA貢獻2011條經驗 獲得超2個贊
SEH:結構化異常處理
結構化異常處理機制提供了一個操作系統,用于優化結構的方案,為客戶提供更強大的程序執行環境。試想一下,你寫程序不用考慮內存訪問錯誤,那里是空指針錯誤,一直在按照程序的邏輯結構來寫,而無需檢查功能是否成功,那將是怎樣愉快的事情(但SEH宣傳的字,并不意味著我的角度來看,這里是沒有義務的語言之際)。
結構化異常處理--- SEH是一個操作系統級別,操作系統(windows平臺為每個線程的基本單元,系統調度線程),以維持一個鏈表異常處理,異常發生時,控制權轉移到手中的操作系統,操作系統根據一定的方式遍歷列表中找到相應的處理函數,進行處理,堆棧展開
用戶模式的線程中運行的操作系統FS寄存器指向線程環境塊(TEB),TEB是一個用戶模式訪問的數據結構,的所謂NT_TIB結構嵌入TIB(線程信息塊)在開幕式上,這的TIB里面保存SEH使用鏈表。
struct_TEB
{
NT_TIBNtTib的;
......
}
structNT_TIB
{
EXCEPTION_REGISTRATION_RECORD * ExceptionList中;
.....
}
structEXCEPTION_REGISTRATION_RECORD
{
EXCEPTION_REGISTRATION_RECORD *下一步;
enum_EXCEPTION_DISPOSITION(*處理器)(_EXCEPTION_RECORD * ExceptionRecord,無效* EstablisherFrame,_context * ContextRecord無效* DispatcherContext);
FS段線程運行點TEB結構可以看出,在下面的匯編代碼里面。
是否發生異常時特定的操作系統,他們做了什么,先說說。 首先,你要了解什么是異常,這個名字以為意,異常是不尋常的地方( - ),CPU遇到異常,將觸發一個中斷,操作系統將取得的(控制具體的,我不能在這里詳細說明),經過一系列必要的措施,以節省現成的,現成的操作系統通過FS指數TEB,工商局之間在ExceptionList然后訪問,處理函數指針,給他打電話的功能,如果函數的返回,檢查函數的返回值,返回值意味著他不能處理這個異常,那么對指數下指針到下一條記錄,重復,鏈表結束,還是沒有人可以處理自動殺這個線程。
處理程序是在哪里呢?在執行的時候安裝的應用程序,你可能已經知道,通常指向一個處理程序稱為功能_except_handler3,從上面看到,這個功能是整個SEH的關鍵,下面詳細介紹此功能。
里面的c語言,其語法是__嘗試SEH ... 。 __except .... __finally構造(具體語法,這里就不詳細介紹),大家都知道,c語言編譯成機器語言,然后由CPU點__嘗試,這樣的結構將被改造成什么樣子像機器語言?和他同行匯編語言是它看起來像它? SEH的,它涉及到地面太多,尤其是內存的布局是非常重要的,所以在這里談論這個轉換的過程。
編譯器遇到__試著結構,他應該知道SEH代碼生成,是EXCEPTION_REGISTRATION_RECORD完成上面的鏈接,
push_except_handler3;這個紀錄
moveax堆棧結構以上,FS:[0];原始記錄
pusheax /> movfs的:[0],ESP
此代碼執行完畢,堆棧又算得了什么呢? (低地址旁邊的地址)
原來的記錄指針| FS:[0]點這里
|現在hanlder |
只是構成了一個記錄結構,也發生被連接到一起的原始清單只是為了滿足操作系統的要求。
了解編譯器如何安排記錄,我們來看看真正的處理程序,相對來說,每個處理程序,做不同的事情,每次嘗試生成的加工處理程序,這將是很麻煩,VC + +實現的,因此該處理程序點相同的功能,但是這一次,處理程序本身設有一個復雜的領域,因為他必須分開當前異常的嘗試,功能,必須建立相應的數據結構去該信息處理程序正確處理。
VC保持一個叫每個函數的數據結構SCOPETABLE的,他記錄功能里面一試。
typedefstruct_SCOPETABLE
{
DWORDpreviousTryLevel ;/ /嘗試列表指針
DWORDlpfnFilter ;/ / __除括號內的代碼地址后立即
DWORDlpfnHandler的;/ / __除外,以下括號內的代碼地址
} SCOPETABLE的,PSCOPETABLE的;
VC在生成的代碼為每個函數生成一個數組的SCOPETABLE,建立sehrecord此表指針也添加到堆棧中,而當前trylevel的也把這樣的,這里面__except_handler3的疊加將能夠訪問這些數據,就能夠妥善處理異常。
首先解釋一下什么是trylevel trylevel標志,標記當前代碼執行的位置,他居然表示嘗試里面。此/>英地= 0 ;/ / trylevel = -1。
__嘗試
{
= 1 ;/ /此代碼,讓trylevel = 0
__嘗試
{
= 2 ;/ /此代碼,讓trylevel = 1
}
__惟(EXCEPTION_EXECUTE_HANDLER)
{
= 3;}
__嘗試
{
I = 4 ;/ /執行這段代碼,讓trylevel = 2
}
__除外(EXCEPTION_EXECUTE_HANDLER)
{
= 5;
}
}
__除外(EXCEPTION_EXECUTE_HANDLER)
{
= 6;
}
請忽略變量i,它是內容存在的代碼里面。
trylevel這是用來標記當前代碼的嘗試里面,這個值作為下標的索引里面SCOPETABLE,SCOPETABLE里面記錄的電流對應的try __除表達,除了處理代碼地址。 SCOPETABLE有prevtrylevel成員,它tryblock聯系起來的搜索進程句柄,如上面的代碼,如果i = 2里面的異常,一試的第一個視圖對應的__除了這trylevel的索引,如果SCOPETABLE不處理,你應該嘗試以外的對應,那就是,I = 6,檢查,但你怎么知道其中嘗試SCOPETABLE的(因為處理函數和過濾函數地址都記錄在表里面)表只是里面的prevtrylevel的的prevtrylevel使用= 0,指數第一嘗試SCOPETABLE,正是我們正在尋找你馬上會想到I = 4對應也等于0里面prevtrylevel的的SCOPETABLE,是的,youareright只要你明白這個事實的一部分,其余的要容易得多。
接下來看看如何實際生成的匯編代碼在功能代碼的開頭這樣
pushebp
movebp的ESP
push0ffffffffh;這里是trylevel了
pushxxxx是,這是SCOPETABLE數組指針
pushfs:[0] /> movfs的:[0],ESP
subebp,20H,這里是不一定圖中,使用的局部變量的函數關系
/ / try語句后相遇,
MOV [EBP-4],1,也許兩個,也許是三個,你應該明白的地方該值是用來做什么的
正如你可以看到,在除了以外的集trylevel SCOPETABLE指針的處理,因為這應該是用于內部處理程序。你可能要怪的處理程序里面如何獲取指針trylevel SCOPETABLE?要看看在內存布局。
[EBP-0] = prevebp
[EBP-4] = trylevel
[EBP-8] = scopetablepointer
[EBP-0C] =處理程序 [EBP-10] = prevregistrationrecord
啊......如果我們有記錄指針向前的訪問將是能夠訪問它們trylevel啊,是的,記錄指針作為參數傳遞給你,這確實是訪問變量的trylevel等方式。
最后一件事,然后進入處理函數的主體,你應該知道GetExceptionInformation GetExceptionCode()函數,你可能會奇怪,MSDN中,他提到,他們只能用在某些的地方,為什么呢?因為他們的代碼實現,很奇怪的實施
的GetExceptionInformation代碼
moveax,[EBP-14]
RET
你應該知道eax中保存的返回函數值,這意味著這個函數只是返回值[EBP-14],它不設置EBP(EBP是函數framepointer的價值,你應該知道EBP-XX多少的情況下是一個函數的局部變量),這意味著它返回一個局部變量的值,調用者。 VC建筑幾乎trylevel的代碼保留這樣的空間,事實上,實行動態中的處理程序將此值設置為指向相應的地址。
ok了,該處理的身體,看看它的幾個參數,首先不用說,操作系統將幫助您填寫的價值觀,你可以使用GetExceptionInformation獲取此信息,第二個是一個void *的參數,其實時,操作系統通過當前registerationrecord地址的話,這是一個非常關鍵的指針的第三不用說,它是一種架構的上下文中的關系。事實上,有時指向SCOPETABLE最后一個參數,但這個參數沒有使用。
這里都是偽代碼的處理程序,在此之前,讓我們看看在處理程序應
處理程序的主要任務是找到合適的__ except語句,檢查它的返回值是HANDLER EXCEPTION_EXECUTE_。 (和當然continueexecute的)要執行除了后面的代碼,否則在繼續搜索,如何去上一試,上面已經很清楚
處理程序處理的情況,開展放松操作系統的處理函數被調用兩次,在第一個參數是一個成員,在里面告訴你這樣做是要找到治療或放松
/ /對比上述布局的起源想想這種結構
struct_EXCEPTION_REGISTRATION
{,
struct_EXCEPTION_REGISTRATION *上一頁;,
無效的(處理器)(PEXCEPTION_RECORD,PEXCEPTION_REGISTRATION,IMessageSource接口16.3.1。 PEXCEPTION_RECORD);
structscopetable_entry * SCOPETABLE;
inttrylevel的;
int_ebp;
明白任務的處理程序,請參閱是實際的代碼。
int__except_handler3(_EXCEPTION_RECORD * pExceptionRecord,EXCEPTION_REGISTRATION * pRegistrationFrame _context
* pContextRecord,無效* pDispatcherContext)
{
LONGtrylevel LONGfilterFuncRet
EXCEPTION_POINTERSexceptPtrs
PSCOPETABLEpScopeTable
CLD / / Clearthedirectionflag的(makenoassumptions!),這是C語言編譯器的默認操作方式
/ / ifneithertheEXCEPTION_UNWINDINGnorEXCEPTION_EXIT_UNWINDbit。
/ /使用isset ... Thisistruethefirsttimethroughthehandler(
/ /非unwindingcase)
/ /檢查是不是要放松
如果(!(pExceptionRecord ExceptionFlags(EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND))) BR /> {
/ /套[EBP-14]的值,記住上面所說的,[EBP-14]把它,這里ExceptionRecord的內部處理程序堆棧 / /所以它的壽命是有限的,處理函數返回時,此不存在,[EBP-14],指針會指向一個未知的領域,MSDN里面限制
/ /功能調用GetExceptionXXX的位置,明白了嗎?
/ / BuildtheEXCEPTION_POINTERSstructureonthestack的,
exceptPtrs.ExceptionRecord = pExceptionRecord;,
exceptPtrs.ContextRecord = pContextRecord;
/ / PutthepointertotheEXCEPTION_POINTERS4bytesbelowthe / / establisherframe.SeeASMcodeforGetExceptionInformation的
/ /想想吧,-4點,到了什么地方?
*(PDWORD),的((PBYTE),的pRegistrationFrame-4)= &exceptPtrs;
/ / Getinitial“trylevel”的價值,看布局,然后看看該結構定義
> trylevel = pRegistrationFrame> trylevel
/ / Getapointertothescopetablearray,
SCOPETABLE = pRegistrationFrame - > SCOPETABLE;
search_for_handler:
( != / TRYLEVEL_NONE * -1 pRegistrationFrame> trylevel * /)
{
/ /如果它是空的,這意味著這是一個finally語句終于用于放松
(pRegistrationFrame的, -> SCOPETABLE [trylevel] lpfnFilter)
{。
PUSHEBP / / SavethisframeEBP
/ /! veryimportant! SwitchtooriginalEBP.Thisis
/ / whatallowsalllocalsintheframetohavethesame。的
/ / valueasbeforetheexceptionoccurred的的
/ / EBP是一個函數,對于執行該功能是非常重要的,在這里是來執行濾鏡(后面括號 / /里面的語句),你必須恢復EBP EBP的值是如何恢復的?上面的代碼可以看到一個movebp,ESP,ESP
/ /這是什么?根據上述的內存布局,一個很好的經驗句話的含義相反,看看在前面的地址字符。
EBP。 =&pRegistrationFrame> _ebp
/ / Callthefilterfunction的聲明,除括號內的檢查返回值被稱為
filterFuncRet的= SCOPETABLE [trylevel]。 ;
POPEBP / / RestorehandlerframeEBP的
(filterFuncRet! = EXCEPTION_CONTINUE_SEARCH)
{
(filterFuncRet <0)/ / EXCEPTION_CONTINUE_EXECUTION
> returnExceptionContinueExecution,;/ /依靠經營系統完整的continueexecution的
/ / Ifwegethere。,EXCEPTION_EXECUTE_HANDLERwasspecified
SCOPETABLE的== pRegistrationFrame - > SCOPETABLE
/ / DoestheactualOScleanupofregistrationframes
> / / Causesthisfunctiontorecurse的
/ /進行放松,操作系統將是以前的處理程序可變registrationrecord的逐一,然后斷開這些記錄鏈
__ global_unwind2 pRegistrationFrame;
BR /> / / Oncewegethere。everythingisallcleanedup,除了
/ / forthelastframe,wherewe'llcontinueexecution
EBP =&pRegistrationFrame> _ebp
/ /操作系統來幫助我們完成以前放松,目前戰績放松做<br / __ local_unwind2的的(pRegistrationFrame trylevel);
/ /調用setjmp / longjmp的支持的代碼
/ /前起落架==“非本地轉到(setjmp的/ longjmpstuff)
__ NLG_Notify(1);/ / EAX == SCOPETABLE的 - > lpfnHandler
/> / / SetthecurrenttryleveltowhateverSCOPETABLEentry的
/ / wasbeingusedwhenahandlerwasfound中的
/ /當前trylevel prevtrylevel顯然,從目前tryblock出自然的。上tryblock是
pRegistrationFrame> trylevel = SCOPETABLE-> previousTryLevel
/ / Callthe_except。的{} block.Neverreturns,的。 BR /> / / gotoexcept聲明,不返回,因為編譯器不產生一個RET代碼
pRegistrationFrame> SCOPETABLE的[trylevel] except語句的最后lpfnHandler();
}
}
SCOPETABLE = pRegistrationFrame> SCOPETABLE的;
trylevel = SCOPETABLE-> previousTryLevel
gotosearch_for_handler;
}
其他/ / trylevel的== {TRYLEVEL_NONE
retvalue == DISPOSITION_CONTINUE_SEARCH;
}
}
}
其他/ / EXCEPTION_UNWINDINGorEXCEPTION_EXIT_UNWINDflagsareset。
{
/ /進行出放松(觸發由__ global_unwind2功能)
PUSHEBP / / SaveEBP的
EBP =與pRegistrationFrame-_ebp / /
__ local_unwind2(pRegistrationFrame TRYLEVEL_NONE)
BR /> POPEBP / / RestoreEBP。
retvalue的== DISPOSITION_CONTINUE_SEARCH;
}
}
在這里不能不提到的編譯器來生成你的代碼看起來
__嘗試
{
= 0;
}
__除外(EXCEPTION_EXECUTE_HANDLER) { BR /> = 1;
}
據推測,在省略EBP-20的地方FS:[0]設置
__嘗試: MOV [EBP -4],0; trylevel = 0
MOV [EBP-18H],ESP ESP保存
MOV [EBP-20H],0;執行i = 0
> jmp__finish; try語句
__ except_filter:
moveax EXCEPTION_EXECUTE_HANDLER返回,
RET
__ except_body:
movesp,[EBP-18H];第一恢復ESP的值,它是一個運行時堆棧
MOV [EBP-20H],1;執行i = 1;
__完成:
MOV [EBP-4] 0FFFFFFFFH trylevel = -1

TA貢獻1816條經驗 獲得超4個贊
在__excep后面的()中是一個表達式,值可以是:
EXCEPTION_CONTINUE_EXECUTION (–1) 異常被忽略,控制流將在異常出現的點之后,繼續恢復運行。
EXCEPTION_CONTINUE_SEARCH (0) 異常不被識別,也即當前的這個__except模塊不是這個異常錯誤所對應的正確的異常處理模塊。系統將繼續到上一層的try-except域中繼續查找一個恰當的__except模塊。
EXCEPTION_EXECUTE_HANDLER (1) 異常已經被識別,也即當前的這個異常錯誤,系統已經找到了并能夠確認,這個__except模塊就是正確的異常處理模塊??刂屏鲗⑦M入到__except模塊中。
- 2 回答
- 0 關注
- 231 瀏覽
添加回答
舉報