SEH IN ASM 研究(二)---提高篇:關於異常處理的巢狀和堆疊展開 (15千字)
SEH IN ASM 研究(二)
---提高篇
By Hume[AfO]/冷雨飄心
part 4 關於異常處理的巢狀和堆疊展開
在實際程式設計過程中,不可能只有一個異常處理例程,這就產生了異常處理程式巢狀的問題,可能很多
處理例程分別監視若干子程式並處理其中某種異常,另外一個監視所有子程式可能產生的共性異常,這作
起來實際很容易,也方便除錯.你只要依次建立異常處理框架就可以了.
關於VC++異常處理可以巢狀很多人可能比較熟悉,用起來更容易不過實現比這裡也就複雜得多,在VC++
中一個程式所有異常只指向一個相同的處理句例程,然後在這個處理例程裡再實現對各個子異常處理例程的
呼叫,他的大致方法是建立一個子異常處理例程入口的陣列表,然後根據指標來呼叫子處理例程,過程比較煩
瑣,原來打算大致寫一點,現在發現自己對C/C++瞭解實在太少,各位有興趣還是自己
參考MSDN Matt Pietrek 1996年寫的一篇文章<Crash Course on the Depths of Win32? Stru
>
ctured Exception Handling>>,裡面有非常詳細的說明,對於系統的實現細節也有所討論,不過相信很多
人都沒有興趣.hmmm...:)實際上Kernel的異常處理過程和VC++的很相似.
有異常巢狀就涉及到異常展開的問題,也許你注意到瞭如果按照我前面的例子包括final都不處理異常的話,
最後系統在終結程式之前會來一次展開,在試驗之後發現,展開不會呼叫final只是對per_thread例程展開
(right?).什麼是堆疊展開?為什麼要進行堆疊展開?如何進行堆疊展開?
我曾經為堆疊展開迷惑過,原因是各種資料的描述很不一致,Matt Pietrek說展開後前面的ERR結構被釋放,
並且好像鏈後面如果決定處理必須展開,很多C/C++講述異常處理的書也如斯說這使人很迷惑,我們再來看看Jeremy
Gordon的描述,堆疊展開是處理異常的例程自願進行的.呵呵,究竟事實如何?
在迷惑好久之後我終於找到了答案:Matt Pietrek講的沒有錯,那是VC++以及系統kernel的處理方法,Jeremy
Gordon說的也是正確的,那是我門asm Fans的自由!
好了現在來說堆疊展開,堆疊展開是異常處理例程在決定處理某個異常的時候給前面不處理這個異常的處理
例程的一個清洗的機會,前面拒絕處理這個異常的例程可以釋放必要的控制程式碼物件或者釋放堆疊或者乾點別的工作...
那完全是你的自由,叫stack unwind似乎有點牽強.堆疊展開有一個重要的標誌就是
EXCEPTION_RECORD.ExceptionFlag為2,表示正在展開,你可以進行相應的處理工作,但實際上經常用的是6這是
因為還有一個UNWIND_EXIT什麼的,具體含義我也沒有搞明白,不過對我們的工作好像沒有什麼影響.
注意在自己的異常處理例程中,unwind不是自動的,必須你自己自覺地引發,如果所有例程都不處理系統最後的
展開是註定的,當然如果沒有必要你也可以不展開.
win32提供了一個api RtlUnwind來引發展開,如果你想展開一下,就呼叫這個api吧,少候講述自己程式碼如何展開
RtlUnwind呼叫描述如下:
PUSH Return value ;返回值,一般不用
PUSH pExceptionRecord ;指向EXCEPTION_RECORD的指標
PUSH OFFSET CodeLabel ;展開後從哪裡執行
PUSH LastStackFrame ;展開到哪個處理例程終止返回,通常是處理異常的Err結構
CALL RtlUnwind
呼叫這個api之前要注意保護ebx,esi和edi,否則...嘿嘿
MASM格式如下:
invoke RtlUnwind,pFrame,OFFSET return_code_Address,pExceptionRecord,Return_value
這樣在展開的時候,就以pExceptionRecord.flag=2 依次呼叫前面的異常處理例程,到決定異常的處理例程停止,
Jeremy Gordon手動展開程式碼和我下面的例子有所不同.他描述最後決定處理異常的ERR結構的prev成員為-1,好像
我的結果和他的有所差異,因此採用了另外的方法,具體看下面的例子.
最後一點要注意在巢狀異常處理程式的時候要注意儲存暫存器,否則你經常會得到系統異常程式碼為C00000027h
的異常呼叫,然後就是被終結.
一下給出一點垃圾程式碼演示可能有助於理解,注意link的時候要加入 /section:.text,RWE 否則例子裡面的
程式碼段不能寫,SMC功能會產生異常以致整個程式不能進行.
注意:2K/XP下非法指令異常的程式碼不一致,另外用下面的方法SMC程式碼段也不可以!不知如何解決?
只用於9X,為了在2k/Xp下也能執行我加了點程式碼,有興趣看看,另外幫我解決一下2K/Xp下SMC的問題?thx!
下面例子很爛,不過MASM格式寫起來容易一點,也便於理解.
;-----------------------------------------
;Ex4,演示堆疊展開和異常巢狀處理 by Hume,2002
;humewen@21cn.com
;hume.longcity.net
;-----------------------------------------
.586
.model flat, stdcall
option casemap :none ; case sensitive
include hd.h
include mac.h
;;--------------
per_xHandler1 proto C :DWORD,:DWORD,:DWORD,:DWORD
per_xHandler2 proto C :DWORD,:DWORD,:DWORD,:DWORD
per_xHandler3 proto C :DWORD,:DWORD,:DWORD,:DWORD
;-----------------------------------------
.data
sztit db "except Mess,by hume[AfO]",0
count dd 0,0
Expt1_frm dd 0
;ERR結構指標,用於堆疊展開手動程式碼
Expt2_frm dd 0
Expt3_frm dd 0
;;-----------------------------------------
.CODE
_Start:
assume fs:nothing
push offset per_xHandler3
push fs:[0]
mov fs:[0],esp
mov Expt3_frm,esp
push offset per_xHandler2
push fs:[0]
mov fs:[0],esp
mov Expt2_frm,esp
push offset per_xHandler1
push fs:[0]
mov fs:[0],esp
mov Expt1_frm,esp
;--------------------------
;install xhnadler
;-----------------------------------------
xor ebx,ebx
mov eax,200
cdq
div ebx
;除法錯誤
invoke MessageBox,0,ddd("Good,divide
overflow was solved!"),addr sztit,40h
sub eax,eax
mov [eax],ebx
;記憶體寫錯誤
succ:
invoke MessageBox,0,ddd("Good,memory
write violation solved!"),addr sztit,40h
db 0F0h,0Fh,0C7h,0C8h
;什麼cmpchg8b指令的非法形式?我從來沒有成功過!!
;演示程式中使用seh實現SMC技術,加密??...
invoke MessageBox,0,ddd("illeagal
instruction was solved!"),addr sztit,20h
;--------------------------
;uninstall xhnadler
;-----------------------------------------
pop fs:[0]
add esp,4
pop fs:[0]
add esp,4
;或者add esp,10h
pop fs:[0]
add esp,4
invoke ExitProcess,0
;-----------------------------------------
;異常處理控制程式碼1,處理除法異常錯誤
per_xHandler1 PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
pushad
MOV ESI,pExcept
ASSUME ESI:PTR EXCEPTION_RECORD
TEST [ESI].ExceptionFlags,1
JNZ @cantdo1
TEST [ESI].ExceptionFlags,6
JNZ @unwind1
CMP [ESI].ExceptionCode,0C0000094h
JNZ @cantdo1
MOV EDI,pContext
ASSUME EDI:PTR CONTEXT
m2m [edi].regEbx,20
;將ebx置20,修復除法錯誤,繼續執行
popad
MOV EAX, ExceptionContinueExecution
RET
@unwind1:
invoke MessageBox,0,CTEXT("state:
unwinding in xhandler1..."),addr sztit,0
@cantdo1:
popad
MOV EAX,ExceptionContinueSearch
RET
per_xHandler1 ENDP
;-----------------------------------------
;異常處理控制程式碼2,處理記憶體寫錯誤,擴充套件可以有其他的例子如自動擴充堆疊
per_xHandler2 PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
pushad
MOV ESI,pExcept
ASSUME ESI:PTR EXCEPTION_RECORD
MOV EDI,pContext
ASSUME EDI:PTR CONTEXT
call Dispcont
;顯示一點lame的訊息,自己除錯用
TEST [ESI].ExceptionFlags,1
JNZ @cantdo2
TEST [ESI].ExceptionFlags,6
JNZ @unwind2
CMP [ESI].ExceptionCode,0C0000005h
JNZ @cantdo2
.data
;ASM的資料定義靈活性,如果需要這是可以的
validAddress dd 0
.code
m2m [EDI].regEax,<offset validAddress>
;置eax為有效地址
popad
MOV EAX, ExceptionContinueExecution
RET
@unwind2:
invoke MessageBox,0,CTEXT("hmmm...
unwinding in xhandler2..."),addr sztit,40h
@cantdo2:
popad
MOV EAX,ExceptionContinueSearch
RET
per_xHandler2 ENDP
;-----------------------------------------
per_xHandler3 PROC C pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD
pushad
MOV ESI,pExcept
ASSUME ESI:PTR EXCEPTION_RECORD
MOV EDI,pContext
ASSUME EDI:PTR CONTEXT
TEST [ESI].ExceptionFlags,1
JNZ @cantdo3
TEST [ESI].ExceptionFlags,6
JNZ @unwind3
;-----------------------------------------
push ecx
mov ecx,cs
xor cl,cl
jecxz win2k_Xp
win9X:
pop ecx
CMP [ESI].ExceptionCode,0C000001DH
;非法指令異常,與2K/XP下的不一致
JNZ @cantdo3
jmp ok_here
win2k_Xp:
pop ecx
;注意,只有在9X下才可以
CMP [ESI].ExceptionCode,0C000001EH
;非法指令異常->2K/XP
JNZ @cantdo3
;sMc不成
mov [edi].regEip,offset safereturn
popad
mov eax,0
ret
push ebx
push esi
push edi
comment $ 呼叫RtlUnwind展開堆疊
lea ebx,unwindback
invoke RtlUnwind,Expt3_frm,ebx,esi,0
$
mov dword ptr [esi+4],2
;置展開標誌,準備展開,這裡是
;手動程式碼
mov ebx,fs:[0]
selfun:
;mov eax,Expt2_frm
;這裡顯示了ASM手動展開的靈活性
mov eax,Expt3_frm
cmp ebx,eax
;按照Jeremy
Gordon的好像不大對頭
;cmp dword ptr [ebx],-1
;這樣好像有問題,只好如上,請教答案
jz unwindback
push ebx
push esi
; 壓入Err和Exeption_registration結構
call dword ptr[ebx+4]
add esp,8
mov ebx,[ebx]
jmp selfun
unwindback:
invoke MessageBox,0,CTEXT("I am
Back!"),addr sztit,40h
pop edi
pop esi
pop ebx
;一定要儲存這三個暫存器!
MOV EAX,[EDI].regEip
MOV DWORD PTR[EAX],90909090H
;改為nop指令...SMC呵呵這次不神秘了吧
;SMC注意連線選項
popad
MOV EAX, ExceptionContinueExecution
RET
@unwind3:
invoke MessageBox,0,CTEXT("Note...
unwinding in xhandler3..."),addr sztit,40h
@cantdo3:
popad
MOV EAX,ExceptionContinueSearch
RET
per_xHandler3 ENDP
;-----------------------------------------
;lame routine for debug
Dispcont proc
inc count
call dispMsg
ret
Dispcont endp
dispMsg proc
local szbuf[200]:byte
pushad
mov eax,dword ptr[esi]
mov ebx,dword ptr[esi+4]
mov ecx,dword ptr[edi+0b8h]
mov edx,dword ptr[edi+0a4h]
.data
fmt db "Context eip--> %8X ebx-->
%8X ",0dh,0ah
db "Flags Ex.c->
%8x flg--> %8X",0dh,0ah
db "it's the %d times
xhandler was called!",0
.code
invoke wsprintf,addr szbuf,addr
fmt,ecx,edx,eax,ebx,count
invoke MessageBox,0,addr szbuf,CTEXT("related
Mess of context"),0
popad
ret
dispMsg endp
;;------------------------------------------------
END _Start
;---------------------------------下面是上面用到的宏,我的mac.h比較長,就不貼了-----
ddd MACRO Text
;define data in .data section
local name
;This and other can be used as: ddd("My god!")
.data
;isn't cool?
name db Text,0
.code
EXITM <addr name>
ENDM
CTEXT MACRO y:VARARG
;This is a good macro
LOCAL sym
CONST segment
IFIDNI <y>,<>
sym db 0
ELSE
sym db y,0
ENDIF
CONST ends
EXITM <OFFSET sym>
ENDM
m2m MACRO M1, M2
;mov is too boring sometimes!
push M2
pop M1
ENDM
;-----------------------------------------
最後更正一點前面介紹的傳送給final型的引數是指向EXCEPTION_POINTERS 的指標,壓棧前的堆疊
是如下的,不好意思,原來寫的時候我也沒深入研究,可能模糊了一點,如有錯誤,請大家指正
push ptEXCEPTION_POINTERS
call xHandler
下面補充一個final引數獲得的一個例子
;--------------------------------------------
; Ex5,演示final處理控制程式碼的引數獲取,更正前面
; 模糊的介紹
;--------------------------------------------
.586
.model flat, stdcall
option casemap :none ; case sensitive
include hd.h
include mac.h
;;--------------
.data
sztit db "exceptION MeSs,by hume[AfO]",0
fmt db "Context eip--> %8X ebx--> %8X ",0dh,0ah
db "Flags Ex.c-> %8x flg--> %8X",0
szbuf db 200 dup(0)
;;-----------------------------------------
.CODE
_Start:
assume fs:nothing
push offset _final_xHandler0
call SetUnhandledExceptionFilter
xor ebx,ebx
mov eax,200
cdq
div ebx
invoke MessageBox,0,ddd("Good,divide
overflow was solved!"),addr sztit,40h
xor eax,eax
mov [eax],ebx
invoke ExitProcess,0
;-----------------------------------------
_final_xHandler0:
push ebp
mov ebp,esp
mov eax,[ebp+8]
;the pointer to EXCEPTION_POINTERS
mov esi,[eax]
;pointer to _EXCEPTION_RECORD
mov edi,[eax+4]
;pointer to _CONTEXT
test dword ptr[esi+4],1
jnz @_final_cnotdo
test dword ptr[esi+4],6
jnz @_final_unwind
;call dispMsg
cmp dword ptr[esi],0c0000094h
jnz @_final_cnotdo
mov dword ptr [edi+0a4h],10
call dispMsg
mov eax,EXCEPTION_CONTINUE_EXECUTION
;GO ON
jmp @f
@_final_unwind:
invoke MessageBox,0,CTEXT("state:In
final unwind..."),addr sztit,0
;好像不論處理不處理異常,系統展開的時候
;都不會被呼叫,right?
@_final_cnotdo:
;請教是真的嗎?還是我寫的有問題
mov eax,EXCEPTION_CONTINUE_SEARCH
jmp @f
@@:
mov esp,ebp
pop ebp
ret
;-----------------------------------------
dispMsg proc
pushad
mov eax,[esi]
mov ebx,[esi+4]
mov ecx,[edi+0b8h]
mov edx,[edi+0a4h]
invoke wsprintf,addr szbuf,addr
fmt,ecx,edx,eax,ebx
invoke MessageBox,0,addr szbuf,CTEXT("related
Mess of context"),0
popad
ret
dispMsg endp
;;------------------------------------------------
END _Start
;====================================================================================
BTW:夠長了吧,基本內容介紹完畢,更多內容下一部分介紹一點利用Seh的tricks,哪位大俠有什麼好的想法
或者有什麼錯誤,請不吝指正,畢竟我是菜鳥嗎...
=====================================================================================
一點閒話:請CUT--------------------------
最近很鬱悶,生活上的工作上的,就這樣懶懶懶散散地目無光彩地苟活於世,最近看了一點C++,有的地方
頭大,於是拿起老傢伙asm寫了點以前許諾過的東西,感覺還是ASM最有助於理解基本原理~~~不過C/C++給了我
們另外的工具,雖然目的碼不夠緊湊(理想狀態),畢竟不需要我們每個人寫核心,C/C++可以提高生產能力.
crack也一樣,如果一些基本概念都不懂明白還談什麼crack?昨天看精華III一些高手的文章,真是慚愧啊!
我的專業不是計算機或者以後永遠也不會搞計算機,這些當也許只是業餘愛好,永遠不會放棄的業餘愛好.
以後也許很長一段時間裡面我會離開大家,畢竟,面臨的還有生活.
感謝CCG,BCG的各位高手,AfO的成員們,以及那些曾無私指導過以及給我生活動力的朋友們!
我要奮鬥!
我以此激勵自己也同樣希望能夠激勵大家!
2002.1.28深夜~~沉思中
----------------------------------------cut------------------------------------
相關文章
- SEH in ASM 研究(一) (7千字)2001-12-29ASM
- 異常篇——異常處理2022-02-27
- 利用SEH異常處理機制繞過GS保護2017-12-25
- Python錯誤處理和異常處理(二)2019-03-07Python
- Windows核心讀書筆記——SEH結構化異常處理2016-04-11Windows筆記
- 關於resmgr:cpu quantum異常等待處理2017-06-24
- 異常處理機制(二)之異常處理與捕獲2023-11-14
- 統一返回物件和異常處理(二)2019-02-12物件
- [原創]利用SEH異常處理機制繞過GS保護2018-04-20
- 轉載 利用SEH異常處理機制繞過GS保護2018-04-19
- 關於Asp.net ajax下的異常處理2009-02-03ASP.NET
- 關於java程式異常處理(講義)(轉)2007-08-15Java
- 【Java入門提高篇】Day16 Java異常處理(上)2021-09-09Java
- NodeJS異常處理uncaughtException篇2015-05-08NodeJSException
- 展開巢狀列表2024-04-10巢狀
- Java 傳統異常處理(二)2017-03-27Java
- Java異常處理設計(二)2013-11-19Java
- MySQL Slave異常關機的處理2015-11-10MySql
- PHP錯誤處理和異常處理2017-11-12PHP
- JavaScript 錯誤處理和堆疊追蹤淺析2017-03-08JavaScript
- 約束和異常處理2018-11-12
- 迭代器和異常處理2021-11-19
- JVM異常不列印堆疊資訊 [ -XX:-OmitStackTraceInFastThrow ]2020-12-29JVMMITAST
- 異常-throws的方式處理異常2018-09-02
- Spring Boot 中關於自定義異常處理的套路!2019-04-19Spring Boot
- 關於專案中遇到的NullPointerException異常時處理手段2014-11-12NullException
- C中關於堆疊的總結2019-05-09
- MySQL定義異常和異常處理詳解2014-10-04MySql
- 異常處理2016-02-21
- springboot下新增全域性異常處理和自定義異常處理2023-12-11Spring Boot
- 關於C++ 的異常處理,解答在這來看看吧~2020-08-06C++
- Java 異常處理:使用和思考2023-04-09Java
- Java異常處理和設計2014-06-05Java
- MySQL遊標和異常處理2016-06-14MySql
- C與C++中的異常處理15 (轉)2008-08-06C++
- log列印及異常處理相關2018-01-05
- Java提高篇(二):IO位元組流、字元流和處理流2018-10-22Java字元
- 關於 MySQL 的巢狀事務2020-01-02MySql巢狀