异常篇——异常记录

博客 动态
0 158
羽尘
羽尘 2022-02-26 22:55:50
悬赏:0 积分 收藏

异常篇——异常记录

异常篇之异常记录,详细介绍 CPU 异常和软件模拟异常相关知识点。

写在前面

??此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。

??看此教程之前,问几个问题,基础知识储备好了吗?保护模式篇学会了吗?练习做完了吗?没有的话就不要继续了。

?


?? 华丽的分割线 ??


?

概述

??异常产生后,首先是要记录异常信息。异常分为CPU产生的异常和软件模拟产生的异常。废话不多说,下面我们开始介绍:

CPU 异常

??当出现CPU异常时,比如除以零的操作。我们就以该异常(中断号为0)为例,出现除零异常之后,CPU就会在IDT查找,执行中断处理函数,那么是如何记录异常的呢?下面我们来简单通过反汇编了解一下:

_KiTrap00       proc near               ; DATA XREF: INIT:_IDT↓ovar_2           = word ptr -2arg_4           = dword ptr  8; FUNCTION CHUNK AT .text:00467013 SIZE 00000021 BYTES                push    0                mov     word ptr [esp+2], 0                push    ebp                push    ebx                push    esi                push    edi                push    fs                mov     ebx, 30h ; '0'                mov     fs, bx                assume fs:nothing                mov     ebx, large fs:_KPCR                push    ebx                sub     esp, 4                push    eax                push    ecx                push    edx                push    ds                push    es                push    gs                mov     ax, 23h ; '#'                sub     esp, 30h                mov     ds, ax                assume ds:nothing                mov     es, ax                assume es:nothing                mov     ebp, esp                test    [esp+_KTRAP_FRAME.EFlags], 20000h                jnz     short V86_kit0_aloc_4671DE:                             ; CODE XREF: V86_kit0_a+25↑j                cld                mov     ebx, [ebp+_KTRAP_FRAME._Ebp]                mov     edi, [ebp+_KTRAP_FRAME._Eip]                mov     [ebp+_KTRAP_FRAME.DbgArgPointer], edx                mov     [ebp+_KTRAP_FRAME.DbgArgMark], 0BADB0D00h                mov     [ebp+_KTRAP_FRAME.DbgEbp], ebx                mov     [ebp+_KTRAP_FRAME.DbgEip], edi                test    byte ptr ds:0FFDFF050h, 0FFh ; KPCR.DebugActive                jnz     Dr_kit0_aloc_467202:                             ; CODE XREF: Dr_kit0_a+10↑j                                        ; Dr_kit0_a+7C↑j                test    [ebp+_KTRAP_FRAME.EFlags], 20000h                jnz     short isVM8086                test    byte ptr [ebp+_KTRAP_FRAME.SegCs], 1                jz      short isKernelMode                cmp     word ptr [ebp+_KTRAP_FRAME.SegCs], 1Bh                jnz     short loc_467235isKernelMode:                           ; CODE XREF: _KiTrap00+73↑j                sti                push    ebp                call    _Ki386CheckDivideByZeroTrap@4 ; Ki386CheckDivideByZeroTrap(x)                mov     ebx, [ebp+_KTRAP_FRAME._Eip]                jmp     loc_467013; ---------------------------------------------------------------------------loc_467227:                             ; CODE XREF: _KiTrap00+A9↓j                                        ; _KiTrap00+B4↓j                sti                mov     ebx, [ebp+_KTRAP_FRAME._Eip]                mov     eax, STATUS_INTEGER_DIVIDE_BY_ZERO                jmp     loc_467013; ---------------------------------------------------------------------------loc_467235:                             ; CODE XREF: _KiTrap00+7A↑j                mov     ebx, ds:0FFDFF124h                mov     ebx, [ebx+_KTHREAD.ApcState.Process]                cmp     [ebx+_EPROCESS.VdmObjects], 0                jz      short loc_467227isVM8086:                               ; CODE XREF: _KiTrap00+6D↑j                push    0                call    _Ki386VdmReflectException_A@4 ; Ki386VdmReflectException_A(x)                or      al, al                jz      short loc_467227                jmp     Kei386EoiHelper@0 ; Kei386EoiHelper()_KiTrap00       endp

??可以看出最后执行到如下代码:

loc_467227:                             ; CODE XREF: _KiTrap00+A9↓j                                        ; _KiTrap00+B4↓j                sti                mov     ebx, [ebp+_KTRAP_FRAME._Eip]                mov     eax, STATUS_INTEGER_DIVIDE_BY_ZERO                jmp     loc_467013

??最后跳到loc_467013,我们来看看它的汇编代码:

loc_467013:                             ; CODE XREF: _KiTrap00+86↓j                                        ; _KiTrap00+94↓j ...                xor     ecx, ecx                call    CommonDispatchException

??发现它会调用CommonDispatchException函数,这个函数就是用来派发异常的,我们继续分析流程:

CommonDispatchException proc near       ; CODE XREF: _KiTrap00-187↑p                                        ; _KiTrap00-17B↑p ...var_50          = dword ptr -50hvar_4C          = dword ptr -4Chvar_48          = dword ptr -48hvar_44          = dword ptr -44hvar_40          = dword ptr -40hvar_3C          = byte ptr -3Ch                sub     esp, EXCEPTION_RECORD_LENGTH                mov     [esp+_EXCEPTION_RECORD32.ExceptionCode], eax                xor     eax, eax                mov     [esp+_EXCEPTION_RECORD32.ExceptionFlags], eax                mov     [esp+_EXCEPTION_RECORD32.ExceptionRecord], eax                mov     [esp+_EXCEPTION_RECORD32.ExceptionAddress], ebx                mov     [esp+_EXCEPTION_RECORD32.NumberParameters], ecx                cmp     ecx, 0                jz      short loc_46705D                lea     ebx, [esp+_EXCEPTION_RECORD32.ExceptionInformation]                mov     [ebx], edx                mov     [ebx+4], esi                mov     [ebx+8], ediloc_46705D:                             ; CODE XREF: CommonDispatchException+1B↑j                mov     ecx, esp                test    [ebp+_KTRAP_FRAME.EFlags], 20000h                jz      short loc_46706F                mov     eax, 0FFFFh                jmp     short loc_467072; ---------------------------------------------------------------------------loc_46706F:                             ; CODE XREF: CommonDispatchException+32↑j                mov     eax, [ebp+_KTRAP_FRAME.SegCs]loc_467072:                             ; CODE XREF: CommonDispatchException+39↑j                and     eax, 1                push    1               ; FirstChance                push    eax             ; PreviousMode                push    ebp             ; TrapFrame                push    0               ; ExceptionFrame                push    ecx             ; ExceptionRecord                call    _KiDispatchException@20 ; KiDispatchException(x,x,x,x,x)                mov     esp, ebp                jmp     Kei386EoiHelper@0 ; Kei386EoiHelper()CommonDispatchException endp

??可以看出它会提升堆栈提供EXCEPTION_RECORD结构体,存储一些异常信息,这就是CPU的异常记录流程。如下就是该结构体的成员:

type struct _EXCEPTION_RECORD{    DWORD ExceptionCode;        //异常代码    DWORD ExceptionFlags;       //异常状态    struct _EXCEPTION_RECORD* ExceptionRecord;  //下一个异常    PVOID ExceptionAddress;     //异常发生地址    DWORD NumberParameters;     //附加参数个数    ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //附加参数指针}  

??不同的CPU异常对应不同的IDT索引,与此同时Windows自己定义了不同的错误码标识不同的错误:

??CommonDispatchException最后调用KiDispatchException函数真正地派发异常,有关CPU异常记录就介绍到这里。

软件模拟异常

??对于软件模拟的异常,我们以C++的为例,如下是测试代码:

#include "stdafx.h"int main(int argc, char* argv[]){    throw;    return 0;}

??代码很简单,我们生成的反汇编如下:

#include "stdafx.h"int main(int argc, char* argv[]){00401010   push        ebp00401011   mov         ebp,esp00401013   sub         esp,40h00401016   push        ebx00401017   push        esi00401018   push        edi00401019   lea         edi,[ebp-40h]0040101C   mov         ecx,10h00401021   mov         eax,0CCCCCCCCh00401026   rep stos    dword ptr [edi]    throw;00401028   push        00040102A   push        00040102C   call        __CxxThrowException@8 (00401090)    return 0;}00401031   pop         edi00401032   pop         esi00401033   pop         ebx00401034   add         esp,40h00401037   cmp         ebp,esp00401039   call        __chkesp (00401050)0040103E   mov         esp,ebp00401040   pop         ebp00401041   ret

??可以发现,抛出一个异常是用CxxThrowException函数实现的,我们查看它的反汇编:

__CxxThrowException@8:00401090   push        ebp00401091   mov         ebp,esp00401093   sub         esp,20h00401096   push        esi00401097   push        edi00401098   mov         ecx,80040109D   mov         esi,offset string "The value of ESP was not properl"...+0E0h (00422110)004010A2   lea         edi,[ebp-20h]004010A5   rep movs    dword ptr [edi],dword ptr [esi]004010A7   mov         eax,dword ptr [ebp+8]004010AA   mov         dword ptr [ebp-8],eax004010AD   mov         ecx,dword ptr [ebp+0Ch]004010B0   mov         dword ptr [ebp-4],ecx004010B3   lea         edx,[ebp-0Ch]004010B6   push        edx004010B7   mov         eax,dword ptr [ebp-10h]004010BA   push        eax004010BB   mov         ecx,dword ptr [ebp-1Ch]004010BE   push        ecx004010BF   mov         edx,dword ptr [ebp-20h]004010C2   push        edx004010C3   call        dword ptr [__imp__RaiseException@16 (0042a154)]004010C9   pop         edi004010CA   pop         esi004010CB   mov         esp,ebp004010CD   pop         ebp004010CE   ret         8

??该函数又是调用RaiseException函数实现功能,传入的异常号为E06D7363,注意不同的编译器模拟异常实现,这个异常号是不同的。我们继续查看RaiseException函数,由于反编译的结果十分好,为了节省篇幅伪代码如下:

void __stdcall RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, const ULONG_PTR *lpArguments){  DWORD v4; // ecx  struct _EXCEPTION_RECORD ExceptionRecord; // [esp+4h] [ebp-50h] BYREF  ExceptionRecord.ExceptionRecord = 0;  ExceptionRecord.ExceptionCode = dwExceptionCode;  ExceptionRecord.ExceptionFlags = dwExceptionFlags & 1;  ExceptionRecord.ExceptionAddress = RaiseException;  if ( lpArguments )  {    v4 = nNumberOfArguments;    if ( nNumberOfArguments > 0xF )      v4 = 15;    ExceptionRecord.NumberParameters = v4;    if ( v4 )      qmemcpy(ExceptionRecord.ExceptionInformation, lpArguments, 4 * v4);  }  else  {    ExceptionRecord.NumberParameters = 0;  }  RtlRaiseException(&ExceptionRecord);}

??这个函数只是构造了一个结构体用来记录,最后又会调用RtlRaiseException函数实现功能,其汇编代码如下:

; void __stdcall RtlRaiseException(PEXCEPTION_RECORD ExceptionRecord)                public _RtlRaiseException@4_RtlRaiseException@4 proc near          ; CODE XREF: RtlRaiseException(x)+B3↓p                                        ; RtlDispatchException(x,x)+D3↓p ...var_2F4         = dword ptr -2F4hvar_2F0         = dword ptr -2F0hvar_2EC         = dword ptr -2EChvar_2E4         = dword ptr -2E4hContext         = CONTEXT ptr -2D4hvar_4           = dword ptr -4var_s0          = dword ptr  0ExceptionRecord = dword ptr  8arg_4           = byte ptr  0Ch                push    ebp                mov     ebp, esp                pushf                sub     esp, 2D0h                mov     [ebp+Context._Eax], eax                mov     [ebp+Context._Ecx], ecx                mov     eax, [ebp+ExceptionRecord]                mov     ecx, [ebp+4]    ; 获取调用该函数的地址                mov     [eax+_EXCEPTION_RECORD.ExceptionAddress], ecx                lea     eax, [ebp+Context]                mov     [eax+_CONTEXT._Eip], ecx                mov     [eax+_CONTEXT._Ebx], ebx                mov     [eax+_CONTEXT._Edx], edx                mov     [eax+_CONTEXT._Esi], esi                mov     [eax+_CONTEXT._Edi], edi                lea     ecx, [ebp+0Ch]  ; 调用该函数之前的堆栈栈顶                mov     [eax+_CONTEXT._Esp], ecx                mov     ecx, [ebp+0]    ; 获取保存的 ebp                mov     [eax+_CONTEXT._Ebp], ecx                mov     ecx, [ebp-4]    ; 获取保存的 eflag                mov     [eax+_CONTEXT.EFlags], ecx                mov     word ptr [eax+_CONTEXT.SegCs], cs                mov     word ptr [eax+_CONTEXT.SegDs], ds                mov     word ptr [eax+_CONTEXT.SegEs], es                mov     word ptr [eax+_CONTEXT.SegFs], fs                mov     word ptr [eax+_CONTEXT.SegGs], gs                mov     word ptr [eax+_CONTEXT.SegSs], ss                mov     [eax+_CONTEXT.ContextFlags], 10007h                push    1               ; SearchFrames                push    eax             ; Context                push    [ebp+ExceptionRecord] ; ExceptionRecord                call    _ZwRaiseException@12 ; ZwRaiseException(x,x,x)                sub     esp, 20h                mov     [esp], eax                mov     dword ptr [esp+4], 1                mov     dword ptr [esp+10h], 0                mov     eax, [ebp+ExceptionRecord]                mov     [esp+8], eax                mov     eax, esp                push    eax             ; ExceptionRecord                call    _RtlRaiseException@4 ; RtlRaiseException(x)_RtlRaiseException@4 endp

??然后又调用ZwRaiseException,通过系统调用最终调用NtRaiseException实现:

; NTSTATUS __stdcall NtRaiseException(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT Context, BOOLEAN SearchFrames)_NtRaiseException@12 proc near          ; DATA XREF: .text:0042AE60↑ovar_s0          = dword ptr  0ExceptionRecord = dword ptr  8Context         = dword ptr  0ChFirstChance     = byte ptr  10harg_34          = dword ptr  3Ch                push    ebp                mov     ebx, ds:0FFDFF124h                mov     edx, [ebp+3Ch]                mov     [ebx+_KTHREAD.TrapFrame], edx                mov     ebp, esp                mov     ebx, [ebp+0]                mov     edx, dword ptr [ebp+FirstChance]                mov     eax, [ebx+_KTRAP_FRAME.ExceptionList]                mov     ecx, [ebp+Context]                mov     ds:0FFDFF000h, eax                mov     eax, [ebp+ExceptionRecord]                push    edx             ; FirstChance                push    ebx             ; TrapFrame                push    0               ; ExceptionFrame                push    ecx             ; ContextRecord                push    eax             ; ExceptionRecord                call    _KiRaiseException@20 ; KiRaiseException(x,x,x,x,x)                pop     ebp                mov     esp, ebp                or      eax, eax                jnz     _KiServiceExit                jmp     _KiServiceExit2_NtRaiseException@12 endp

??这个函数又会调用KiRaiseException实现:

NTSTATUS __stdcall KiRaiseException(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT ContextRecord, _KTRAP_FRAME *ExceptionFrame, _KTRAP_FRAME *TrapFrame, BOOLEAN FirstChance){  // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]  ExceptionRecord_1 = ExceptionRecord;  ContextFrame = ContextRecord;  ExceptionFrame_1 = ExceptionFrame;  TrapFrame_1 = TrapFrame;  ms_exc.registration.TryLevel = 0;  currentThread = KeGetCurrentThread();  LOBYTE(PreviousMode) = currentThread->PreviousMode;  if ( !PreviousMode )  {LABEL_19:    ms_exc.registration.TryLevel = -1;    KeContextToKframes(TrapFrame_1, ExceptionFrame_1, ContextFrame, ContextFrame->ContextFlags, PreviousMode);    HIBYTE(ExceptionRecord_1->ExceptionCode) &= 0xEFu;    KiDispatchException(ExceptionRecord_1, ExceptionFrame_1, TrapFrame_1, PreviousMode, FirstChance);    goto LABEL_20;  }  if ( (ContextRecord & 3) != 0 )    ExRaiseDatatypeMisalignment();  if ( ContextFrame >= MmUserProbeAddress )    *MmUserProbeAddress = 0;  if ( (ExceptionRecord & 3) != 0 )    ExRaiseDatatypeMisalignment();  if ( ExceptionRecord >= MmUserProbeAddress )    *MmUserProbeAddress = 0;  NumberParameters = ExceptionRecord->NumberParameters;  NumberParameters_1 = NumberParameters;  if ( NumberParameters <= 0xF )  {    v13 = 4 * NumberParameters + 20;    if ( 4 * NumberParameters != 4294967276 )    {      if ( (ExceptionRecord & 3) != 0 )        ExRaiseDatatypeMisalignment();      v7 = &ExceptionRecord->ExceptionInformation[NumberParameters];      if ( v7 < ExceptionRecord || v7 > MmUserProbeAddress )        ExRaiseAccessViolation();    }    qmemcpy(&context, ContextFrame, sizeof(context));    qmemcpy(&exRecord, ExceptionRecord, v13);    ContextFrame = &context;    ExceptionRecord_1 = &exRecord;    v10 = &exRecord;    exRecord.NumberParameters = NumberParameters;    goto LABEL_19;  }  ms_exc.registration.TryLevel = -1;LABEL_20:  xHalReferenceHandler(v19);  return result;}

??这个函数调用KiDispatchException进行异常派发。软件抛出模拟的异常记录流程就到此结束了。

小结

??最后我们简单的用流程图表示一下CPU异常和软件模拟异常的记录流程:

graph TD 1[CPU 异常] --> 2[查找 IDT , 生成 ExceptionRecord 记录] --> 3[CommonExceptionDispatch] --> 4[KiDispatchException] a[软件模拟异常] --> b[CxxThrowException] --> c[kernel32.RaiseException] --> d[Ntdll.RtlRaiseException] --> e[Ntdll.ZwRaiseException] --> f[Nt.NtRaiseException] --> g[Nt.KiRaiseException] --> 4

下一篇

??异常篇——异常处理

知识共享许可协议
知识共享许可协议
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
本文来自博客园,作者:寂静的羽夏 ,一个热爱计算机技术的菜鸟
转载请注明原文链接:https://www.cnblogs.com/wingsummer/p/15940667.html
posted @ 2022-02-26 22:08 寂静的羽夏 阅读(10) 评论(0) 编辑 收藏 举报
回帖
    羽尘

    羽尘 (王者 段位)

    2335 积分 (2)粉丝 (11)源码

     

    温馨提示

    亦奇源码

    最新会员