系统服务调用与异常分发

来源:互联网 发布:金石软件多大 编辑:程序博客网 时间:2024/05/02 07:28

系统服务的调用过程

一、CPU的内部支持

1. int 0x2e 进入 iret返回 (中断门切换)

★.利用中断进入内核,0x2e对应的是KiSystemService

★.每个处理器有一个任务环境,初始化时系统会给CPU在GDT中构造TSS段,并且记录在KPCR中,其中记录着内核栈的地址。线程切换的时候会把处理器的TSS.ESP0 设置为当前的内核栈地址
   所以r3和r0使用的是不同的栈

★.iret从内核栈中弹出eip cs eflag esp ss 返回用户态

2. sysenter sysexit

★.IA32_SYSENTER_CS IA32_SYSENTER_ESP IA32_SYSENTER_EIP在系统初始化时被设置,分别保存指定跳转后的 cs、ss; esp ; eip

★.sysenter指令将装载这些寄存器 切换到r0。 继续执行sysexit则负责恢复这些寄存器并且回到r3,ecx是用户态的esp,edx是eip

二、系统的切换实现

NtCreateFile

kd> u ntdll!ntcreatefile
ntdll!ZwCreateFile:
7c9511f8 b827000000      mov     eax,27h     ;系统服务号
7c9511fd ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300) 
7c951202 ff12            call    dword ptr [edx]
7c951204 c22c00          ret     2Ch

支持快速系统调用的话,edx里存的是这个值
kd> u 7c95ed50 ntdll!KiFastSystemCall:
7c95ed50 8bd4            mov     edx,esp
7c95ed52 0f34            sysenter ;走KiFastCallEntry
ntdll!KiFastSystemCallRet:
7c95ed54 c3              ret
不支持的话,调用到这里
ntdll!KiIntSystemCall:
7c95ed60 8d542408        lea     edx,[esp+8]
7c95ed64 cd2e            int     2Eh   ;走KiSystemService
7c95ed66 c3              ret

不管走哪种方式 实际操作都是建立一个陷阱帧,eax为服务号 edx是当前栈针,esi是线程地址

然后进入KiSystemServiceRepeat,他从ETHREAD中找到服务表 然后把用户栈中的参数拷贝到内核栈 再转入到系统服务 等系统服务返回后 转到 KiServiceExit

KiServiceExit先派发用户态APC,然后EXIT_ALL

实现退出的是这里
u KiSystemCallExitBranch l 10
nt!KiSystemCallExitBranch [E:\Work\Code\wrk-v1.2\base\ntos\ke\i386\trap.asm @ 1396]:
80882948 7506            jne     nt!KiSystemCallExit2 (80882950)    ;由于系统支持快速调用,这里已经被改成 jne     nt!KiSystemCallExit2,否则将执行iretd
8088294a 5a              pop     edx          ;更快速的返回内核的方法
8088294b 59              pop     ecx
8088294c 9d              popfd
8088294d ffe2            jmp     edx
nt!KiSystemCallExit [E:\Work\Code\wrk-v1.2\base\ntos\ke\i386\trap.asm @ 1396]:
8088294f cf              iretd
nt!KiSystemCallExit2 [E:\Work\Code\wrk-v1.2\base\ntos\ke\i386\trap.asm @ 1396]:
80882950 f744240800010000 test    dword ptr [esp+8],100h
80882958 75f5            jne     nt!KiSystemCallExit (8088294f) 如果是陷阱 跳转至KiSystemCallExit 防止程序自己执行Int 2e进入内核的过程
8088295a 5a              pop     edx        弹出eip,为什么这里弹出的是EIP?因为KiFastCallEntry 之前压入了返回地址 ntdll!KiFastSystemCallRet
8088295b 83c404          add     esp,4       移除cs
8088295e 812424fffdffff and     dword ptr [esp],0FFFFFDFFh    禁止eflag的中断标志
80882965 9d              popfd
80882966 59              pop     ecx        弹出esp
80882967 fb              sti
80882968 0f35            sysexit         返回

至此这个过程应该是清楚了

异常分发

异常,IDT的前30号是异常,抛出异常之后直接jmp到KiTrap
KiTrapXX保存的陷阱帧,针对不同情况做出一些处理,然后跳转到CommonDispatchException 保存一个异常记录块,然后调用KiDispatchException,最后调用KiExceptionExit撤退

下面以除0异常为例,这个的kitrap比较简单

_KiTrap00       proc;除0错误的异常处理代码 VDM的部分略去了        push    0                       ; push dummy error code        ENTER_TRAP      kit0_a, kit0_t      ;这个宏 将建立一个陷阱帧 _KTRAP_FRAME        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;; Set up exception record for raising Integer_Divided_by_zero exception; and call _KiDispatchException;Kt0000:        sti        stdCall _Ki386CheckDivideByZeroTrap,<ebp>        ;这里调用异常处理 每个异常都不一样,比如内存错误时是KiTrap0E        mov     ebx, [ebp]+TsEip        ; (ebx)-> faulting instruction        jmp     CommonDispatchException0Args ; Won't return     ;最终都是跳转到CommonDispatchException 参数不同而已_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  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], edide00:;; 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的实现


typedef struct _EXCEPTION_RECORD {    DWORD    ExceptionCode;   异常码    DWORD ExceptionFlags;    标志?    struct _EXCEPTION_RECORD *ExceptionRecord; 相关联的异常记录    PVOID ExceptionAddress;    发生异常的指令地址    DWORD NumberParameters;   参数数量    ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; 参数数组} EXCEPTION_RECORD;

这个函数是派发异常的函数 但不是所有异常都要派发 有些可能在kitrap里已经解决掉了,走到这里,就是一个系统无法解决的异常交由内核调试器/代码本身(SHE、VEH)/用户调试器/win32子系统等等来处理

这是分情况派发的,具体看代码注释

VOIDKiDispatchException (    IN PEXCEPTION_RECORD ExceptionRecord,    IN PKEXCEPTION_FRAME ExceptionFrame,    IN PKTRAP_FRAME TrapFrame,    IN KPROCESSOR_MODE PreviousMode,    IN BOOLEAN FirstChance    ){    CONTEXT ContextFrame;    EXCEPTION_RECORD ExceptionRecord1, ExceptionRecord2;    LONG Length;    ULONG UserStack1;ULONG UserStack2;    KeGetCurrentPrcb()->KeExceptionDispatchCount += 1;    ContextFrame.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;    if ((PreviousMode == UserMode) || KdDebuggerEnabled) {        ContextFrame.ContextFlags |= CONTEXT_FLOATING_POINT;        if (KeI386XMMIPresent) {            ContextFrame.ContextFlags |= CONTEXT_EXTENDED_REGISTERS;        }    }//获取context结构    KeContextFromKframes(TrapFrame, ExceptionFrame, &ContextFrame);    switch (ExceptionRecord->ExceptionCode) {        case STATUS_BREAKPOINT:            ContextFrame.Eip--;        //断点的话,eip退后一个字节            break;        case KI_EXCEPTION_ACCESS_VIOLATION:            ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION;            if (PreviousMode == UserMode) {     //用户模式下检查是否是thunk问题 暂略                if (KiCheckForAtlThunk(ExceptionRecord,&ContextFrame) != FALSE) {                    goto Handled1;                }                if ((SharedUserData->ProcessorFeatures[PF_NX_ENABLED] == TRUE) &&                    (ExceptionRecord->ExceptionInformation [0] == EXCEPTION_EXECUTE_FAULT)) {                                        if (((KeFeatureBits & KF_GLOBAL_32BIT_EXECUTE) != 0) ||                        (PsGetCurrentProcess()->Pcb.Flags.ExecuteEnable != 0) ||                        (((KeFeatureBits & KF_GLOBAL_32BIT_NOEXECUTE) == 0) &&                         (PsGetCurrentProcess()->Pcb.Flags.ExecuteDisable == 0))) {                        ExceptionRecord->ExceptionInformation [0] = 0;                    }                }            }            break;    }    if (PreviousMode == KernelMode) {        if (FirstChance == TRUE) {    //第一次错误 交由调试器处理            if ((KiDebugRoutine != NULL) &&               (((KiDebugRoutine) (TrapFrame,                                   ExceptionFrame,                                   ExceptionRecord,                                   &ContextFrame,                                   PreviousMode,                                   FALSE)) != FALSE)) {                goto Handled1;            }            // 调试器无法处理,尝试找到一个异常处理器            if (RtlDispatchException(ExceptionRecord, &ContextFrame) == TRUE) {                goto Handled1;            }        }        //        // 第二次机会,再处理不了直接bugcheck        //        if ((KiDebugRoutine != NULL) &&            (((KiDebugRoutine) (TrapFrame,                                ExceptionFrame,                                ExceptionRecord,                                &ContextFrame,                                PreviousMode,                                TRUE)) != FALSE)) {            goto Handled1;        }        KeBugCheckEx(            KERNEL_MODE_EXCEPTION_NOT_HANDLED,            ExceptionRecord->ExceptionCode,            (ULONG)ExceptionRecord->ExceptionAddress,            (ULONG)TrapFrame,            0);    } else {//用户模式下的异常        if (FirstChance == TRUE) {    //用户模式下 有内核调试器 没有用户调试器            if ((KiDebugRoutine != NULL) &&                ((PsGetCurrentProcess()->DebugPort == NULL &&                  !KdIgnoreUmExceptions) ||                 (KdIsThisAKdTrap(ExceptionRecord, &ContextFrame, UserMode)))) {                if ((((KiDebugRoutine) (TrapFrame,                                        ExceptionFrame,                                        ExceptionRecord,                                        &ContextFrame,                                        PreviousMode,                                        FALSE)) != FALSE)) {                    goto Handled1;                }            }            if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) {                goto Handled2;            }            ExceptionRecord1.ExceptionCode = 0; // satisfy no_opt compilation        repeat:            try {                if (TrapFrame->HardwareSegSs != (KGDT_R3_DATA | RPL_MASK) ||                    TrapFrame->EFlags & EFLAGS_V86_MASK ) {                    ExceptionRecord2.ExceptionCode = STATUS_ACCESS_VIOLATION;                    ExceptionRecord2.ExceptionFlags = 0;                    ExceptionRecord2.NumberParameters = 0;                    ExRaiseException(&ExceptionRecord2);                }                UserStack1 = (ContextFrame.Esp & ~CONTEXT_ROUND) - CONTEXT_ALIGNED_SIZE;     //CONTEXT结构入用户栈                ProbeForWrite((PCHAR)UserStack1, CONTEXT_ALIGNED_SIZE, CONTEXT_ALIGN);                RtlCopyMemory((PULONG)UserStack1, &ContextFrame, sizeof(CONTEXT));                    Length = (sizeof(EXCEPTION_RECORD) - (EXCEPTION_MAXIMUM_PARAMETERS -                         ExceptionRecord->NumberParameters) * sizeof(ULONG) +3) &                         (~3);                UserStack2 = UserStack1 - Length;     //异常记录块入栈                ProbeForWrite((PCHAR)(UserStack2 - 8), Length + 8, sizeof(ULONG));                RtlCopyMemory((PULONG)UserStack2, ExceptionRecord, Length);         //异常块 上下文的地址入栈                *(PULONG)(UserStack2 - sizeof(ULONG)) = UserStack1;                *(PULONG)(UserStack2 - 2*sizeof(ULONG)) = UserStack2;                //                // 更新TrapFrame                //                KiSegSsToTrapFrame(TrapFrame, KGDT_R3_DATA);                KiEspToTrapFrame(TrapFrame, (UserStack2 - sizeof(ULONG)*2));                TrapFrame->SegCs = SANITIZE_SEG(KGDT_R3_CODE, PreviousMode);                TrapFrame->SegDs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);                TrapFrame->SegEs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);                TrapFrame->SegFs = SANITIZE_SEG(KGDT_R3_TEB, PreviousMode);                TrapFrame->SegGs = 0;     //设置Eip是ntdll中的KiUserExceptionDispatcher,ntdll试图分发给异常处理器,如果异常被处理则可以继续执行     //否则NtRaiseException 继续进入内核 第二次处理                TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher;                return;            } except (KiCopyInformation(&ExceptionRecord1,                        (GetExceptionInformation())->ExceptionRecord)) {                if (ExceptionRecord1.ExceptionCode == STATUS_STACK_OVERFLOW) {                    ExceptionRecord1.ExceptionAddress = ExceptionRecord->ExceptionAddress;                    RtlCopyMemory((PVOID)ExceptionRecord,                                  &ExceptionRecord1, sizeof(EXCEPTION_RECORD));                    goto repeat;                }            }        }        //        // 第二次处理异常 进程调试器处理异常/win32子系统处理异常 如果还是不能处理 进程终止 bugcheck        //        if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) {            goto Handled2;        } else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) {            goto Handled2;        } else {            ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode);            KeBugCheckEx(                KERNEL_MODE_EXCEPTION_NOT_HANDLED,                ExceptionRecord->ExceptionCode,                (ULONG)ExceptionRecord->ExceptionAddress,                (ULONG)TrapFrame,                0);        }    }Handled1://已经解决,把 ContextFrame移动到陷阱帧     KeContextToKframes(TrapFrame, ExceptionFrame, &ContextFrame,                       ContextFrame.ContextFlags, PreviousMode);//被调试器或者子系统解决的,不同更新trap帧直接返回Handled2:    return;}

还能往下走,说明异常或者已经解决 或者已经派发处理掉了

KiExceptionExit是个宏EXIT_ALL,这里可能交付用户态APC,恢复寄存器,内核态则调整esp的值,最后通过iret返回。