KiTrap03DebugService: ; ; If caller is user mode, we want interrupts back on. ; . all relevant state has already been saved ; . user mode code always runs with ints on ; ; If caller is kernel mode, we want them off! ; . some state still in registers, must prevent races ; . kernel mode code can run with ints off ; ; ; Arguments: ; eax - ServiceClass - which call is to be performed ; ecx - Arg1 - generic first argument ; edx - Arg2 - generic second argument ;
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh) test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h ;检查是否在虚拟8086模式产生 jnz kit03_30 ; fault occured in V86 mode => Usermode
.errnz (MODE_MASK AND 0FFFFFF00h) test byte ptr [ebp]+TsSegCs,MODE_MASK; jz kit03_10
cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK jne kit03_30
kit03_05: sti kit03_10:
; 调用CommonDispatchException函数, 并将参数通过寄存器传递过去, ; CommonDispatchException函数的参数是: ; (eax) = 异常代码 ; (ebx) = 异常地址 ; (ecx) = 附加参数的个数 ; (edx) = Parameter1(附加参数1) ; (esi) = Parameter2(附加参数2) ; (edi) = Parameter3(附加参数3) ; ; Set up exception record and arguments for raising breakpoint exception ;
VOID KiDispatchException ( IN PEXCEPTION_RECORD ExceptionRecord, IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame, IN KPROCESSOR_MODE PreviousMode, IN BOOLEAN FirstChance )
/*++ Routine Description: This function is called to dispatch an exception to the proper mode and to cause the exception dispatcher to be called. If the previous mode is kernel, then the exception dispatcher is called directly to process the exception. Otherwise the exception record, exception frame, and trap frame contents are copied to the user mode stack. The contents of the exception frame and trap are then modified such that when control is returned, execution will commense in user mode in a routine which will call the exception dispatcher. 该函数用于分发异常, 根据异常所产生的模式找到异常的处理函数. 如果异常发生在 内核模式, 函数会直接调用一个异常处理函数直接处理异常. 如果异常发生在用户模 式, 则函数会将异常记录, 异常栈帧, 和陷阱栈帧拷贝到用户模式的线程的栈 (这些 信息在用户态可以被修改, 被修改后会重新设置到线程环境上), 随后函数会进入到用 户态, 到了用户态之后会又专门的函数去处理异常. Arguments: ExceptionRecord - Supplies a pointer to an exception record. ExceptionFrame - Supplies a pointer to an exception frame. For NT386, this should be NULL. TrapFrame - Supplies a pointer to a trap frame. PreviousMode - Supplies the previous processor mode. FirstChance - Supplies a boolean value that specifies whether this is the first (TRUE) or second (FALSE) chance for the exception. Return Value: None. --*/
// // Move machine state from trap and exception frames to a context frame, // and increment the number of exceptions dispatched. // 获取发生异常的线程的线程上下文,输出到ContextRecord结构体变量中.
if ((PreviousMode == UserMode) || KdDebuggerEnabled) { // // For usermode exceptions always try to dispatch the floating // point state. This allows exception handlers & debuggers to // examine/edit the npx context if required. Plus it allows // exception handlers to use fp instructions without destroying // the npx state at the time of the exception. // // Note: If there's no 80387, ContextTo/FromKFrames will use the // emulator's current state. If the emulator can not give the // current state, then the context_floating_point bit will be // turned off by ContextFromKFrames. //
// // if it is BREAK_POINT exception, we subtract 1 from EIP and report // the updated EIP to user. This is because Cruiser requires EIP // points to the int 3 instruction (not the instruction following int 3). // In this case, BreakPoint exception is fatal. Otherwise we will step // on the int 3 over and over again, if user does not handle it // // if the BREAK_POINT occured in V86 mode, the debugger running in the // VDM will expect CS:EIP to point after the exception (the way the // processor left it. this is also true for protected mode dos // app debuggers. We will need a way to detect this. // //
switch (ExceptionRecord->ExceptionCode) { case STATUS_BREAKPOINT: // 断点异常就将eip减到int3指令所在的首地址 ContextFrame.Eip--; break;
case KI_EXCEPTION_ACCESS_VIOLATION: // 内存访问异常, 设置附加参数 ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION; if (PreviousMode == UserMode) { if (KiCheckForAtlThunk(ExceptionRecord,&ContextFrame) != FALSE) { goto Handled1; }
// 如果异常是在内核模式下被触发 if (PreviousMode == KernelMode) {
// // Previous mode was kernel. // // If the kernel debugger is active, then give the kernel debugger the // first chance to handle the exception. If the kernel debugger handles // the exception, then continue execution. Else attempt to dispatch the // exception to a frame based handler. If a frame based handler handles // the exception, then continue execution. // // If a frame based handler does not handle the exception, // give the kernel debugger a second chance, if it's present. // // If the exception is still unhandled, call KeBugCheck(). //
// 如果调试器和SEH异常处理都没有处理得了异常, 则进行第二次异常分发: // This is the second chance to handle the exception. // // 判断有无内核调试器,并调用(再给内核调试器一次处理异常得机会) if ((KiDebugRoutine != NULL) && (((KiDebugRoutine) (TrapFrame, ExceptionFrame, ExceptionRecord, &ContextFrame, PreviousMode, TRUE)) != FALSE)) {
// // Previous mode was user. // // If this is the first chance and the current process has a debugger // port, then send a message to the debugger port and wait for a reply. // 如果异常是第一次分发并且进程具有调试端口(被调试状态), 则发送一个消息 // 到调试端口,并等待回复. // If the debugger handles the exception, then continue execution. Else // 如果调试器处理了这个异常, 则结束异常的分发. 否则, // transfer the exception information to the user stack, transition to // 将异常信息拷贝到用户态的栈中, 并转到 // user mode, and attempt to dispatch the exception to a frame based // 用户模式 , 在用户模式下尝试将异常派发给异常处理程序. // handler. If a frame based handler handles the exception, then continue // 如果异常处理程序处理了异常, 则结束异常分发. // execution with the continue system service. Else execute the // 如果用户层的异常处理程序处理不了, 则调用NtRaiseException函数 // NtRaiseException system service with FirstChance == FALSE, which // 主动触发异常, 并将FirstChance设置为TRUE. 这个函数(KiDispatchException) // will call this routine a second time to process the exception. // 将会被第二次调用以继续处理异常. // If this is the second chance and the current process has a debugger // 如果这次处理是第二次异常处理,并且进程有一个调试 // port, then send a message to the debugger port and wait for a reply. // 端口, 则发送一个消息到调试端口,并等待调试器回复. // If the debugger handles the exception, then continue execution. Else // 如果调试器回复已经处理了异常, 则结束异常分发. 否则 // if the current process has a subsystem port, then send a message to // 如果当前触发异常的进程有子系统端口,则发送一个消息到 // the subsystem port and wait for a reply. If the subsystem handles the // 子系统端口,并等其回复. 若子系统处理了 // exception, then continue execution. Else terminate the process. // 异常, 则异常分发结束, 否则直接结束掉当前进程. // If the current process is a wow64 process, an alignment fault has // occurred, and the AC bit is set in EFLAGS, then clear AC in EFLAGS // and continue execution. Otherwise, attempt to resolve the exception. //
// if (FirstChance == TRUE) {
// // This is the first chance to handle the exception. // // 检查是进程是否被调试,如果当前有内核调试器, 并且进程没有被调试,则将异常交给 // 内核调试器去处理. if ((KiDebugRoutine != NULL) && ((PsGetCurrentProcess()->DebugPort == NULL && !KdIgnoreUmExceptions) || (KdIsThisAKdTrap(ExceptionRecord, &ContextFrame, UserMode)))) { // // Now dispatch the fault to the kernel debugger. // // 将异常信息交给内核调试器处理 if ((((KiDebugRoutine) (TrapFrame, ExceptionFrame, ExceptionRecord, &ContextFrame, PreviousMode, FALSE)) != FALSE)) { // 处理成功则异常分发结束 goto Handled1; } } // 将异常交给调试子系统去处理. DbgkForwardException函数会将 // 异常记录发送给3环的调试器进程, 并等待3环的调试器回复. // 如果调试器回复了异常被处理, 则异常分发到此结束. if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) { goto Handled2; } // 如果没有用户调试器,或用户调试器没有处理异常则接着往下走. // 函数会试图将异常记录, 线程环境 // // Transfer exception information to the user stack, transition // to user mode, and attempt to dispatch the exception to a frame // based handler. ExceptionRecord1.ExceptionCode = 0; // satisfy no_opt compilation
repeat: try {
// // If the SS segment is not 32 bit flat, there is no point // to dispatch exception to frame based exception handler. //
// // Compute length of context record and new aligned user stack // pointer. // // 为将线程上下文块整个结构体拷贝到用户的栈空间, 需要找到栈空间上一个空闲 // 的地址, 此处是计算esp(栈顶位置) - 一个线程上下文块的大小, 也就是相当于 // sub esp , sizeof(CONTEXT) UserStack1 = (ContextFrame.Esp & ~CONTEXT_ROUND) - CONTEXT_ALIGNED_SIZE;
// // Probe user stack area for writability and then transfer the // context record to the user stack. // // 将指定地址设置为可写入 ProbeForWrite((PCHAR)UserStack1, CONTEXT_ALIGNED_SIZE, CONTEXT_ALIGN); // 将线程上下文拷贝到用户的栈空间中. RtlCopyMemory((PULONG)UserStack1, &ContextFrame, sizeof(CONTEXT));
// // Compute length of exception record and new aligned stack // address. // // 计算处异常信息结构体的在用户栈空间中的位置, 也是为了将异常信息写入 // 到用户栈空间中. Length = (sizeof(EXCEPTION_RECORD) - (EXCEPTION_MAXIMUM_PARAMETERS - ExceptionRecord->NumberParameters) * sizeof(ULONG) +3) & (~3); UserStack2 = UserStack1 - Length;
// // Probe user stack area for writeability and then transfer the // context record to the user stack area. // N.B. The probing length is Length+8 because there are two // arguments need to be pushed to user stack later. // // 将地址设置为可写 ProbeForWrite((PCHAR)(UserStack2 - 8), Length + 8, sizeof(ULONG)); // 将异常信息拷贝用户的栈空间中. RtlCopyMemory((PULONG)UserStack2, ExceptionRecord, Length);
// // Push address of exception record, context record to the // user stack. They are the two parameters required by // _KiUserExceptionDispatch. // // 构造处一个EXCEPTION_POINTERS的结构体, 并保存线程上下文,异常信息两个结构体 // 变量的首地址. *(PULONG)(UserStack2 - sizeof(ULONG)) = UserStack1; *(PULONG)(UserStack2 - 2*sizeof(ULONG)) = UserStack2;
// // Set the address of the exception routine that will call the // exception dispatcher and then return to the trap handler. // The trap handler will restore the exception and trap frame // context and continue execution in the routine that will // call the exception dispatcher. // // 将发生异常的线程的eip的地址设置为KeUserExceptionDispatcher函数的地址 // 这个函数是ntdll中的导出函数,这个导出函数就是负责用户层的异常分发的, // 在这个函数中,它会把异常发给进程的异常处理机制(VEH,SEH)去处理. // 这样一来, 当执行流从0环回到3环的时候, eip指向何处, 那个地方的代码就开始 // 被执行. TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher; return;
// // If the exception is a stack overflow, then attempt // to raise the stack overflow exception. Otherwise, // the user's stack is not accessible, or is misaligned, // and second chance processing is performed. //
// // Exception was handled by the debugger or the associated subsystem // and state was modified, if necessary, using the get state and set // state capabilities. Therefore the context frame does not need to // be transferred to the trap and exception frames. //
/* 7C90E47C */ /* * 用户层的异常派发源头 */ VOID KeUserExceptionDispatcher(PCONTEXT ContextRecord, PEXCEPTION_RECORD ExceptionRecord) { NTSTATUS Status; // 调用RtlDispatchException函数去派发异常. if (RtlDispatchException(ContextRecord, ExceptionRecord)) { /* 7C90E48E modify the execution context of the current thread to whatever the chosen exception handler gives us */ // 结束异常分发 Status = ZwContinue(ContextRecord, 0); } else { /* 7C90E49A no exception handler found so re-raise the exception for a debugger or the NT exception handler */ // 在此触发一个异常, 并触发时最后一个参数传FALSE,表明这是一个第二次触发的异常. Status = ZwRaiseException(ExceptionRecord, ContextRecord, FALSE); } /* 7C90E4A5 build an exception record with 20 bytes on the stack, second chance exception? */ PEXCEPTION_RECORD exception = (PEXCEPTION_RECORD) alloca(0x14); exception->ExceptionCode = Status; exception->ExceptionFlags = 1; exception->ExceptionRecord = ContextRecord; exception->NumberParameters = 1;