C++&系统 异常

来源:互联网 发布:嵌入式 linux 忘记密码 编辑:程序博客网 时间:2024/04/30 09:50

User Mode

C++ Demo(Debug)

int       wmain(       int argc,       WCHAR * argv[]){00DF13C0  push        ebp00DF13C1  mov         ebp,esp00DF13C3  push        0FFFFFFFEh00DF13C5  push        0DF6EB8h00DF13CA  push        0DF107Dh00DF13CF  mov         eax,dword ptr fs:[00000000h]00DF13D5  push        eax00DF13D6  add         esp,0FFFFFF38h                    :分配局部变量。Q:没有局部变量,只需esp-8,分配EXCEPTION_REGISTRATION_RECORD剩余的8字节,但为什么这里还有这么大空间?求解答00DF13DC  push        ebx00DF13DD  push        esi00DF13DE  push        edi00DF13DF  lea         edi,[ebp-0D8h]00DF13E5  mov         ecx,30h00DF13EA  mov         eax,0CCCCCCCCh00DF13EF  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],eax00DF13F9  xor         eax,ebp00DF13FB  push        eax                               :计算cookie00DF13FC  lea         eax,[ebp-10h]00DF13FF  mov         dword ptr fs:[00000000h],eax00DF1405  mov         dword ptr [ebp-18h],esp           :存入esp       __try{                                           :至此,异常帧构建完毕,并且插入了fs:[0],开始生效00DF1408  mov         dword ptr [ebp-4],0              getchar();00DF140F  mov         esi,esp00DF1411  call        dword ptr ds:[0DF92B8h]00DF1417  cmp         esi,esp00DF1419  call        __RTC_CheckEsp (0DF1145h)       }__except(EXCEPTION_EXECUTE_HANDLER){00DF141E  mov         dword ptr [ebp-4],0FFFFFFFEh00DF1425  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                                :拿到cookie00DF1444  pop         edi00DF1445  pop         esi00DF1446  pop         ebx}00DF1447  add         esp,0D8h00DF144D  cmp         ebp,esp00DF144F  call        __RTC_CheckEsp (0DF1145h)00DF1454  mov         esp,ebp00DF1456  pop         ebp00DF1457  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,NoAbiosAssistalign 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 returnKt0020:; 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      Kt0010Kt0040:        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    CommonDispatchExceptionCommonDispatchException1Arg0d:        xor     edx, edx                ; zero edxCommonDispatchException1Arg:        mov     ecx, 1                  ; one argument        call    CommonDispatchException ; there is no returnCommonDispatchException2Args0d:        xor     edx, edx                ; zero edxCommonDispatchException2Args:        mov     ecx, 2                  ; two arguments        call    CommonDispatchException ; there is no return-------------------------------------------------------------------------------------------------      public CommonDispatchExceptionalign dwordCommonDispatchException proccPublicFpo 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_RECORDde00:;; 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 de20de10:   mov     eax,[ebp]+TsSegCsde20:   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     _KiExceptionExitCommonDispatchException endp
  • 调用KiDispatchException
VOIDKiDispatchException (    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,进行第二次机会的处理
原创粉丝点击