系统服务调用与异常分发
来源:互联网 发布:金石软件多大 编辑:程序博客网 时间: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返回。
- 系统服务调用与异常分发
- 中断、异常与系统调用
- DDS数据分发与服务
- 【中断异常】系统调用
- 中断、异常、系统调用
- 中断,异常,系统调用
- android调用系统服务
- ios调用系统服务
- 采访:视频CDN分发、调度与服务
- 服务提供与调用
- 中断、异常和系统调用
- 中断、异常和系统调用
- 中断、异常和系统调用
- 操作系统 中断异常系统调用
- 关于OMG的DDS,实时系统的数据分发服务
- 调用系统发短信服务
- SYSENTER系统服务调用过程
- SYSENTER系统服务调用过程
- 【GIS】GDAL之OGR入门
- 面向程序员的数据库访问性能优化法则
- cakephp 文件下载
- 三星手机进入挖煤模式方法小结(变砖用)
- MyBatis 3 中,使用MySql 的AUTO_INCREMENT
- 系统服务调用与异常分发
- 安卓-恢复模式--Recovery
- 今天第一帖
- Android OpenGL ES 开发教程(14):三维坐标系及坐标变换初步
- 多级树形菜单的实现
- 传美光科技竞标尔必达获胜 收购金额25亿美元
- 惠特曼:惠普需要我“两条腿走路”
- 查看ADT版本,安装、卸载、更新eclipse中的ADT
- 修改oracle10.3数据库字符集