Structured Exception Handling

節譯自: Reversing , Secrets of Reverse Engineering by Eldad Eilam

exception是指當程式執行時所遭遇的某種特殊條件, 它會讓執行中的程式立刻跳至稱作exception handler的特殊函式中. 然後由exception handler決定如何處理這個丟出exception的問題, 它可以修正問題後從原本跳出的code繼續執行下去, 或是從某個地方重新開始, 假如問題無法被修正, 它也可以強迫程式中止執行.

基本上有兩種exception: hardware exception和software exception, hardware exception是由處理器自動產生的, 例如程式試圖存取某個不合法的memory page(page fault)或是發生一個除以零的錯誤等等. software exception則是由程式本身回報error而產生的, 例如在C++中的throw指令. 這是一種很常用來將程式錯誤集中管理的技術(另一種相對的技術則是在每個函式的return值加入error code), 在windows中, throw這個指令是用一個叫做RaiseException的Win32 API來實作, 它會深入kernel並且遵循和hardware exception相似的路徑, 最後再返回user mode並且將exception回報給執行程式.

所謂的 Structured exception handling是指OS提供一種有組織地將exception分派給AP的機制. 每個thread都會被指定一個exception-handler list, 這其實就是一個exception handler的串列. 當發生exception時, OS會一個個地呼叫每個註冊過的exception handler, 以決定由其中的哪一個來處理這個exception.

exception-handler list被儲存在thread information block(TIB)這個資料結構中, 它可以在user mode下被存取, 其內容包含以下資訊:

_NT_TIB:
+0x000 ExceptionList : 0x0012fecc
+0x004 StackBase : 0x00130000
+0x008 StackLimit : 0x0012e000
+0x00c SubSystemTib : (null)
+0x010 FiberData : 0x00001e00
+0x010 Version : 0x1e00
+0x014 ArbitraryUserPointer : (null)
+0x018 Self : 0x7ffde000

TIB 是存在AP本身所配置的user mode記憶體中, 我們已經知道一個單一的process可以擁有許多個thread, 但是所有的thread都只能看到相同的記憶體, 因為它們享有共同的記憶體空間. 這表示一個process可以擁有許多個TIB結構, 問題來了, 每一個thread是怎麼找到它們自己專屬的TIB呢? well, 在IA-32的系統上, windows使用FS暫存器做為目前執行中的thread相關資料結構的指標, 也就是說, 執行中的thread其TIB可以由FS:[0]存取.

在這裡我們有興趣的是ExceptionList這個欄位(注: 關於TIB, TEB, PEB的”有趣”應用可以參考The Last Stage of Delirium在2002年發表的”Win32 Assembly Components”), 它是一個指標, 指向exception-handler list的頭. 當exception發生時, 處理器會從IDT中呼叫註冊過的exception handler. 以page-fault這個exception來說好了, 當你存取一個不合法的記憶體位址(指的是在page-table entry中找不到這塊記憶體), 處理器會產生page-fault interrupt(interrupt #14), 並且呼叫IDT中的entry 14所指向的interrupt handler. 在Windows裡, 這個entry通常指向kernel裡的KiTrap0E. KiTrap0E會決定是哪種page fault並分派出去. user-mode的page fault不會由memory manager來處理(像是AP存取不合法的記憶體空間所產生的錯誤), windows會呼叫user-mode的exception分派器–一個在NTDLL.DLL中叫做 KiUserExceptionDispatcher的routine. KiUserExceptionDispatcher會呼叫RtlDispatchException, RtlDispatchException則負責巡訪這個叫做ExceptionList的linked list, 尋找可以處理這個exception的exception handler. 這個linked list資料結構基本上是一個叫做_EXCEPTION_REGISTRATION_RECORD的鏈, 其定義如下:

_EXCEPTION_REGISTRATION_RECORD:
+0x000 Next : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : Ptr32

一個設定exception handler程序的基本骨架看起來就像這樣:

00411F8A push ExceptionHandler
00411F8F mov eax, dword ptr fs:[00000000h]
00411F95 push eax
00411F96 mov dword ptr fs:[0], esp

這段code只是單純地將_EXCEPTION_REGISTRATION_RECORD這個entry加到目前thread的exception handler list中, 每一個item都在stack中.

但是在真實的世界裡你將很難遇到像上面這樣簡單的exception handler設定程序, 這是因為compiler通常會加以善用OS的exception handler機制, 以提供支援巢狀的exception-handling區塊和單一函式中的複數區塊. 在Microsoft的compiler裡, 是藉由將exception轉給_except_handler3這個exception handler來實現, 它會藉著目前函式的layout呼叫正確的exception filter和exception handler. 要達到這樣的功能, compiler本身必需維護額外的資料結構, 其包含了目前函式中的exception handlers層次結構. 下面是一個典型的Microsoft C/C++ compiler的SEH安裝程序:

00411F83 push 0FFFFFFFFh
00411F85 push 425090h
00411F8A push offset @ILT+420(__except_handler3) (4111A9h)
00411F8F mov eax, dword ptr fs:[00000000h]
00411F95 push eax
00411F96 mov dword ptr fs:[0], esp

如同你所看到的, compiler延伸了_EXCEPTION_REGISTRATION_RECORD這個資料結構, 加入了兩個新的members, 這些新的members將會在_except_handler3被使用, 以決定該呼叫哪一個handler.

比起frame-based的exception handler, 最近版本的OS也支援exception handlers vector, 這是一種handlers的linear list, 它會被每一個exception呼叫, 不論是哪裡的code產生的. vectored exception handlers是由叫做AddVectoredExceptionHandler這個API安裝.

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值