SEH IN ASM 研究( 2)

  在实际设计过程中,不可能只有个异常处理例程,这就产生了异常处理嵌套问题,可能很多处理例程分别监视若干子并处理其中某种异常,另外个监视所有子可能产生共性异常,这作起来实际很容易,也方便调试.你只要依次建立异常处理框架就可以了.

  有关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,哪位大侠有什么好想法或者有什么,请不吝指正,毕竟我是菜鸟吗...

Tags:  sehasm boseinear二代

延伸阅读

最新评论

发表评论