C++&系统 异常

User Mode

C++ Demo(Debug)

int
       wmain(
       int argc,
       WCHAR * argv[]
)
{
00DF13C0  push        ebp
00DF13C1  mov         ebp,esp
00DF13C3  push        0FFFFFFFEh

00DF13C5  push        0DF6EB8h
00DF13CA  push        0DF107Dh
00DF13CF  mov         eax,dword ptr fs:[00000000h]
00DF13D5  push        eax
00DF13D6  add         esp,0FFFFFF38h                    :分配局部变量。Q:没有局部变量,只需esp-8,分配EXCEPTION_REGISTRATION_RECORD剩余的8字节,但为什么这里还有这么大空间?求解答
00DF13DC  push        ebx
00DF13DD  push        esi
00DF13DE  push        edi
00DF13DF  lea         edi,[ebp-0D8h]
00DF13E5  mov         ecx,30h
00DF13EA  mov         eax,0CCCCCCCCh
00DF13EF  rep stos    dword ptr es:[edi]                :对栈上的(局部变量)进行0xcccccccc赋值。每个局部变量的两端都会被增加0xcccccccc,也就是说一个DWORD实际会占用3个DWORD。使用0xcccccccc的原因:1.数值较大,且为负,容易被察觉 2.int 3的机器码为0xcc,可以探测到栈被执行
00DF13F1  mov         eax,dword ptr ds:[00DF8000h]
00DF13F6  xor         dword ptr [ebp-8],eax
00DF13F9  xor         eax,ebp
00DF13FB  push        eax                               :计算cookie
00DF13FC  lea         eax,[ebp-10h]
00DF13FF  mov         dword ptr fs:[00000000h],eax
00DF1405  mov         dword ptr [ebp-18h],esp           :存入esp
       __try{                                           :至此,异常帧构建完毕,并且插入了fs:[0],开始生效
00DF1408  mov         dword ptr [ebp-4],0
              getchar();
00DF140F  mov         esi,esp
00DF1411  call        dword ptr ds:[0DF92B8h]
00DF1417  cmp         esi,esp
00DF1419  call        __RTC_CheckEsp (0DF1145h)
       }__except(EXCEPTION_EXECUTE_HANDLER){
00DF141E  mov         dword ptr [ebp-4],0FFFFFFFEh
00DF1425  jmp         $LN6+0Ah (0DF1437h)
$LN10:
00DF1427  mov         eax,1
$LN9:
00DF142C  ret
$LN6:
00DF142D  mov         esp,dword ptr [ebp-18h]
              GetExceptionCode();
       }
00DF1430  mov         dword ptr [ebp-4],0FFFFFFFEh

       return 0;
00DF1437  xor         eax,eax
}
00DF1439  mov         ecx,dword ptr [ebp-10h]
00DF143C  mov         dword ptr fs:[0],ecx               :撤销之前插入的异常链节点
00DF1443  pop         ecx                                :拿到cookie
00DF1444  pop         edi
00DF1445  pop         esi
00DF1446  pop         ebx
}
00DF1447  add         esp,0D8h
00DF144D  cmp         ebp,esp
00DF144F  call        __RTC_CheckEsp (0DF1145h)
00DF1454  mov         esp,ebp
00DF1456  pop         ebp
00DF1457  ret

堆栈图:
这里写图片描述


ntdll!__RtlUserThreadStart

.text:778337CE     ; __stdcall __RtlUserThreadStart(x, x)
.text:778337CE     ___RtlUserThreadStart@8 proc near       ; CODE XREF: _RtlUserThreadStart(x,x)+16p
.text:778337CE
.text:778337CE     ExitStatus      = dword ptr -24h
.text:778337CE     var_20          = dword ptr -20h
.text:778337CE     var_1C          = dword ptr -1Ch
.text:778337CE     ms_exc          = CPPEH_RECORD ptr -18h
.text:778337CE     arg_0           = dword ptr  8
.text:778337CE     arg_4           = dword ptr  0Ch
.text:778337CE
.text:778337CE     ; FUNCTION CHUNK AT .text:777D5E77 SIZE 00000009 BYTES
.text:778337CE     ; FUNCTION CHUNK AT .text:77847EEB SIZE 00000030 BYTES
.text:778337CE     ; FUNCTION CHUNK AT .text:77848260 SIZE 0000001E BYTES
.text:778337CE
.text:778337CE 000                 push    14h
.text:778337D0 004                 push    offset off_77821278
.text:778337D5 008                 call    __SEH_prolog4
.text:778337DA 038                 and     [ebp+ms_exc.registration.TryLevel], 0
                                   ...     ...
---------------------------------------------------------------------------------------------------

.text:77822C0C     __SEH_prolog4   proc near               ; CODE XREF: LdrFlushAlternateResourceModules()+7p
.text:77822C0C                                             ; RtlpCopyMappedMemoryEx(x,x,x,x,x,x)+7p ...
.text:77822C0C
.text:77822C0C     arg_4           = dword ptr  8
.text:77822C0C
.text:77822C0C 000                 push    offset __except_handler4
.text:77822C11 004                 push    large dword ptr fs:0
.text:77822C18 008                 mov     eax, [esp+8+arg_4]
.text:77822C1C 008                 mov     [esp+8+arg_4], ebp
.text:77822C20 008                 lea     ebp, [esp+8+arg_4]
.text:77822C24 008                 sub     esp, eax
.text:77822C26 008                 push    ebx
.text:77822C27 00C                 push    esi
.text:77822C28 010                 push    edi
.text:77822C29 014                 mov     eax, ___security_cookie
.text:77822C2E 014                 xor     [ebp-4], eax
.text:77822C31 014                 xor     eax, ebp
.text:77822C33 014                 push    eax
.text:77822C34 018                 mov     [ebp-18h], esp
.text:77822C37 018                 push    dword ptr [ebp-8]               :这块和上面的例子不一样,因为要把__SEH_prolog4函数下一条指令的地址重新压栈,为了后面的retn能正常返回
.text:77822C3A 01C                 mov     eax, [ebp-4]
.text:77822C3D 01C                 mov     dword ptr [ebp-4], 0FFFFFFFEh
.text:77822C44 01C                 mov     [ebp-8], eax
.text:77822C47 01C                 lea     eax, [ebp-10h]
.text:77822C4A 01C                 mov     large fs:0, eax
.text:77822C50 01C                 retn
.text:77822C50     __SEH_prolog4   endp


  • 只要发生异常第一就是进入内核,然后构造EXCEPTION_RECORD和CONTEXT,并且进行处理逻辑,然后返回UserMode,在某个时机调用异常链上的Handler。

mouseOS《SEH stack 结构探索(1)— 从 SEH 链的最底层(线程第1个SEH结构)说起》:
http://www.mouseos.com/windows/SEH3.html

Kernel Mode

Tarp00(divide error)

  • 发生异常之后,陷入内核,执行Trap00函数
        ASSUME  DS:NOTHING, SS:NOTHING, ES:NOTHING
        ENTER_DR_ASSIST kit0_a, kit0_t,NoAbiosAssist
align dword
        public  _KiTrap00
_KiTrap00       proc

        push    0                       ; push dummy error code
        ENTER_TRAP      kit0_a, kit0_t                                             :构建TrapFrame

.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
        test    byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
        jnz     Kt0040                  ; trap occured in V86 mode                 :如果是虚拟86模式

        test    byte ptr [ebp]+TsSegCs, MODE_MASK  ; Is previous mode = USER
        jz      short Kt0000                                                       :如果是用户模式

        cmp     word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
        jne     Kt0020                                                             :如果是vdm
;
; Set up exception record for raising Integer_Divided_by_zero exception
; and call _KiDispatchException
;

Kt0000:

if DBG
        test    [ebp]+TsEFlags, EFLAGS_INTERRUPT_MASK   ; faulted with
        jnz     short @f                                ; interrupts disabled?

        xor     eax, eax
        mov     esi, [ebp]+TsEip        ; [esi] = faulting instruction
    stdCall _KeBugCheck2,<IRQL_NOT_LESS_OR_EQUAL,eax,-1,eax,esi,ebp>
@@:
endif

        sti


;
; Flat mode
;
; The intel processor raises a divide by zero exception on DIV instructions
; which overflow. To be compatible with other processors we want to
; return overflows as such and not as divide by zero's.  The operand
; on the div instruction is tested to see if it's zero or not.
;
        stdCall _Ki386CheckDivideByZeroTrap,<ebp>
        mov     ebx, [ebp]+TsEip        ; (ebx)-> faulting instruction
        jmp     CommonDispatchException0Args ; Won't return                         :跳到异常处理过程

Kt0010:
;
; 16:16 mode
;
        sti
        mov     ebx, [ebp]+TsEip        ; (ebx)-> faulting instruction
        mov     eax, STATUS_INTEGER_DIVIDE_BY_ZERO
        jmp     CommonDispatchException0Args ; never return

Kt0020:
; Check to see if this process is a vdm
        mov     ebx,PCR[PcPrcbData+PbCurrentThread]
        mov     ebx,[ebx]+ThApcState+AsProcess
        cmp     dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
        je      Kt0010

Kt0040:
        stdCall _Ki386VdmReflectException_A, <0>

        or      al,al
        jz      short Kt0010             ; couldn't reflect, gen exception
        jmp     _KiExceptionExit

_KiTrap00       endp
  • 准备数据结构,然后真正的去处理异常
CommonDispatchException0Args:
        xor     ecx, ecx                ; zero arguments
        call    CommonDispatchException

CommonDispatchException1Arg0d:
        xor     edx, edx                ; zero edx
CommonDispatchException1Arg:
        mov     ecx, 1                  ; one argument
        call    CommonDispatchException ; there is no return

CommonDispatchException2Args0d:
        xor     edx, edx                ; zero edx
CommonDispatchException2Args:
        mov     ecx, 2                  ; two arguments
        call    CommonDispatchException ; there is no return

-------------------------------------------------------------------------------------------------

      public CommonDispatchException
align dword
CommonDispatchException proc
cPublicFpo 0, ExceptionRecordLength/4
;
;       Set up exception record for raising exception
;

        sub     esp, ExceptionRecordLength
                                        ; allocate exception record
        mov     dword ptr [esp]+ErExceptionCode, eax
                                        ; set up exception code
        xor     eax, eax
        mov     dword ptr [esp]+ErExceptionFlags, eax
                                        ; set exception flags
        mov     dword ptr [esp]+ErExceptionRecord, eax
                                        ; set associated exception record
        mov     dword ptr [esp]+ErExceptionAddress, ebx
        mov     dword ptr [esp]+ErNumberParameters, ecx
                                        ; set number of parameters
        cmp     ecx, 0
        je      short de00

        lea     ebx, [esp + ErExceptionInformation]
        mov     [ebx], edx
        mov     [ebx+4], esi
        mov     [ebx+8], edi                                                       :整个这一段都是在栈上准备EXCEPTION_RECORD
de00:
;
; set up arguments and call _KiDispatchException
;

        mov     ecx, esp                ; (ecx)->exception record

.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
        test    byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
        jz      short de10

        mov     eax,0FFFFh
        jmp     short de20

de10:   mov     eax,[ebp]+TsSegCs
de20:   and     eax,MODE_MASK

; 1 - first chance TRUE
; eax - PreviousMode
; ebp - trap frame addr
; 0 - Null exception frame
; ecx - exception record addr

        stdCall _KiDispatchException,<ecx, 0, ebp, eax, 1>                         :正儿八经去处理异常了

        mov     esp, ebp                ; (esp) -> trap frame
        jmp     _KiExceptionExit

CommonDispatchException endp
  • 调用KiDispatchException
VOID
KiDispatchException (
    IN PEXCEPTION_RECORD ExceptionRecord,
    IN PKEXCEPTION_FRAME ExceptionFrame,
    IN PKTRAP_FRAME TrapFrame,
    IN KPROCESSOR_MODE PreviousMode,
    IN BOOLEAN FirstChance
    );
  • 注意最后一个参数,FirstChance,我们陷入内核是第一次。如果是UserMode Exception,主要是构建CONTEXT结构,并且返到ntdll!KiUserExceptionDispatcher,给用户态机会,来调用异常链,如果异常链没有处理OK,则会在ntdll!KiUserExceptionDispatcher中调用ZwRaiseException,进行第二次机会的处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值