有关VC异常处理可以嵌套很多人可能比较熟悉,用起来更容易不过实现比这里也就复杂得多,在VC 中个所有异常只指向个相同处理句例程,然后在这个处理例程里再实现对各个子异常处理例程,他大致思路方法是建立个子异常处理例程入口表,然后根据指针来子处理例程,过程比较烦琐,原来打算大致写点,现在发现自己对C/C了解实在太少,各位有兴趣还是自己参考MSDN Matt Pietrek 1996年写篇文章<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 _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
;[email protected]
;hume.longcity.net
;-----------------------------------------
.586
.model flat, stdcall
option map :none ; sensitive
hd.h
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 off per_xHandler3
push fs:[0]
mov fs:[0],esp
mov Expt3_frm,esp
push off per_xHandler2
push fs:[0]
mov fs:[0],esp
mov Expt2_frm,esp
push off per_xHandler1
push fs:[0]
mov fs:[0],esp
mov Expt1_frm,esp
;--------------------------
; 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
;--------------------------
;un 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,<off 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,off safe
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 ;定要保存这 3个寄存器!
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]:
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 wsprf,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 ; 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 map :none ; sensitive
hd.h
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 off _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 poer to EXCEPTION_POINTERS
mov esi,[eax] ;poer to _EXCEPTION_RECORD
mov edi,[eax+4] ;poer 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 _disibledevent=> BTW:够长了吧,基本内容介绍完毕,更多内容下部分介绍点利用Sehtricks,哪位大侠有什么好想法或者有什么,请不吝指正,毕竟我是菜鸟吗...
延伸阅读
最新评论