Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html
用户异常与模拟异常的派发
一、KiDispatchException函数处理流程图
无论用户模拟异常还是CPU异常,经过前面分析,在经过记录之后,最终都会经过KiDispatchException这个派发函数中。
在KiDispatchException中会对CPU异常和用户异常分别进行处理。
CPU异常:首先调用内核调试器,如果调用失败则调用RtlDispatchException(该函数后面会有介绍)分发,
RtlDispatchException函数会检查SEH链中是否有该程序的处理函数,如果有则返回成功,
若RtlDispatchException函数处理异常失败,其会尝试第二次调用内核调试器进行调试处理,如果最终处理不了直接蓝屏。
用户异常:先尝试内核调试器,再尝试用户调试器,如果还不行,直接返回用户代码尝试使用try_catch_语法来进行处理,之后还不行再尝试两次用户调试器。
如果最终还是处理不了异常,该进程会关闭并报出错误。(用户异常不会导致蓝屏出现)

二、RtlDispatchException的函数解析代码
1 VOID
2 KiDispatchException (
3 IN PEXCEPTION_RECORD ExceptionRecord,
4 IN PKEXCEPTION_FRAME ExceptionFrame,
5 IN PKTRAP_FRAME TrapFrame,
6 IN KPROCESSOR_MODE PreviousMode,
7 IN BOOLEAN FirstChance
8 )
9
10 /*++
11
12 Routine Description:
13
14 This function is called to dispatch an exception to the proper mode and
15 to cause the exception dispatcher to be called. If the previous mode is
16 kernel, then the exception dispatcher is called directly to process the
17 exception. Otherwise the exception record, exception frame, and trap
18 frame contents are copied to the user mode stack. The contents of the
19 exception frame and trap are then modified such that when control is
20 returned, execution will commense in user mode in a routine which will
21 call the exception dispatcher.
22
23 Arguments:
24
25 ExceptionRecord - Supplies a pointer to an exception record.
26
27 ExceptionFrame - Supplies a pointer to an exception frame. For NT386,
28 this should be NULL.
29
30 TrapFrame - Supplies a pointer to a trap frame.
31
32 PreviousMode - Supplies the previous processor mode.
33
34 FirstChance - Supplies a boolean value that specifies whether this is
35 the first (TRUE) or second (FALSE) chance for the exception.
36
37 Return Value:
38
39 None.
40
41 --*/
42
43 {
44 CONTEXT ContextFrame;
45 EXCEPTION_RECORD ExceptionRecord1, ExceptionRecord2;
46 LONG Length;
47 ULONG UserStack1;
48 ULONG UserStack2;
49
50 //
51 // Move machine state from trap and exception frames to a context frame,
52 // and increment the number of exceptions dispatched.
53 //
54
55 //-------------------------------------//
56 // 将当前异常分发次数增加1 //
57 // 修改ContextFrame.ContextFlags标志位 //
58 //-------------------------------------//
59 KeGetCurrentPrcb()->KeExceptionDispatchCount += 1;
60 ContextFrame.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
61
62 //------------------------------------------//
63 // 对于用户模式异常的处理 or 调试器可以执行 //
64 // 标记好Context内容 //
65 //------------------------------------------//
66 if ((PreviousMode == UserMode) || KdDebuggerEnabled) {
67 //
68 // For usermode exceptions always try to dispatch the floating
69 // point state. This allows exception handlers & debuggers to
70 // examine/edit the npx context if required. Plus it allows
71 // exception handlers to use fp instructions without destroying
72 // the npx state at the time of the exception.
73 //
74 // Note: If there's no 80387, ContextTo/FromKFrames will use the
75 // emulator's current state. If the emulator can not give the
76 // current state, then the context_floating_point bit will be
77 // turned off by ContextFromKFrames.
78 //
79
80 ContextFrame.ContextFlags |= CONTEXT_FLOATING_POINT;
81 if (KeI386XMMIPresent) {
82 ContextFrame.ContextFlags |= CONTEXT_EXTENDED_REGISTERS;
83 }
84 }
85
86 //----------------------------------//
87 // 将TrapFrame转换为ContextFrame //
88 // 接下来都是处理ContextFrame的内容 //
89 // 这部分零环无意义,三环有意义 //
90 // 否则修改返回地址不行了。 //
91 //----------------------------------//
92 KeContextFromKframes(TrapFrame, ExceptionFrame, &ContextFrame);
93
94 //
95 // if it is BREAK_POINT exception, we subtract 1 from EIP and report
96 // the updated EIP to user. This is because Cruiser requires EIP
97 // points to the int 3 instruction (not the instruction following int 3).
98 // In this case, BreakPoint exception is fatal. Otherwise we will step
99 // on the int 3 over and over again, if user does not handle it
100 //
101 // if the BREAK_POINT occured in V86 mode, the debugger running in the
102 // VDM will expect CS:EIP to point after the exception (the way the
103 // processor left it. this is also true for protected mode dos
104 // app debuggers. We will need a way to detect this.
105 //
106 //
107
108 switch (ExceptionRecord->ExceptionCode) {
109 //-------------------------//
110 // 如果为int3断点异常 //
111 // 则修改eip执行原来的位置 //
112 //-------------------------//
113 case STATUS_BREAKPOINT:
114 ContextFrame.Eip--;
115 break;
116
117 //------------------------------//
118 // 执行权限问题而发生的访问违例 //
119 //------------------------------//
120 case KI_EXCEPTION_ACCESS_VIOLATION:
121 ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION;
122 if (PreviousMode == UserMode) {
123 if (KiCheckForAtlThunk(ExceptionRecord,&ContextFrame) != FALSE) {
124 goto Handled1;
125 }
126
127 if ((SharedUserData->ProcessorFeatures[PF_NX_ENABLED] == TRUE) &&
128 (ExceptionRecord->ExceptionInformation [0] == EXCEPTION_EXECUTE_FAULT)) {
129
130 if (((KeFeatureBits & KF_GLOBAL_32BIT_EXECUTE) != 0) ||
131 (PsGetCurrentProcess()->Pcb.Flags.ExecuteEnable != 0) ||
132 (((KeFeatureBits & KF_GLOBAL_32BIT_NOEXECUTE) == 0) &&
133 (PsGetCurrentProcess()->Pcb.Flags.ExecuteDisable == 0))) {
134 ExceptionRecord->ExceptionInformation [0] = 0;
135 }
136 }
137 }
138 break;
139 }
140
141 //
142 // Select the method of handling the exception based on the previous mode.
143 //
144
145 ASSERT ((
146 !((PreviousMode == KernelMode) &&
147 (ContextFrame.EFlags & EFLAGS_V86_MASK))
148 ));
149
150 //--------------------------//
151 // 如果是内核模式触发的异常 //
152 //--------------------------//
153 if (PreviousMode == KernelMode) {
154
155 //
156 // Previous mode was kernel.
157 //
158 // If the kernel debugger is active, then give the kernel debugger the
159 // first chance to handle the exception. If the kernel debugger handles
160 // the exception, then continue execution. Else attempt to dispatch the
161 // exception to a frame based handler. If a frame based handler handles
162 // the exception, then continue execution.
163 //
164 // If a frame based handler does not handle the exception,
165 // give the kernel debugger a second chance, if it's present.
166 //
167 // If the exception is still unhandled, call KeBugCheck().
168 //
169
170 //----------------------------------//
171 // 给内核调试器第一次机会来处理异常 //
172 //----------------------------------//
173 if (FirstChance == TRUE) {
174
175 if ((KiDebugRoutine != NULL) &&
176 (((KiDebugRoutine) (TrapFrame,
177 ExceptionFrame,
178 ExceptionRecord,
179 &ContextFrame,
180 PreviousMode,
181 FALSE)) != FALSE)) {
182
183 //------------------------------//
184 // 如果处理成功,不进行下面处理 //
185 //------------------------------//
186 goto Handled1;
187 }
188
189 // Kernel debugger didn't handle exception.
190
191 //--------------------------------------//
192 // 如果第一次调试器不成功,则派发异常 //
193 // 当派发成功之后,也不会处理第二次异常 //
194 //--------------------------------------//
195 if (RtlDispatchException(ExceptionRecord, &ContextFrame) == TRUE) {
196 goto Handled1;
197 }
198 }
199
200 //
201 // This is the second chance to handle the exception.
202 //
203
204 //--------------------------------//
205 // 第二次机会来调用调试器处理异常 //
206 //--------------------------------//
207 if ((KiDebugRoutine != NULL) &&
208 (((KiDebugRoutine) (TrapFrame,
209 ExceptionFrame,
210 ExceptionRecord,
211 &ContextFrame,
212 PreviousMode,
213 TRUE)) != FALSE)) {
214
215 //------------------//
216 // 第二次也处理成功 //
217 //------------------//
218 goto Handled1;
219 }
220
221
222 //------------------------------------------------//
223 // 如果两次调试器处理和派发都不成功,系统直接蓝屏 //
224 //------------------------------------------------//
225 KeBugCheckEx(
226 KERNEL_MODE_EXCEPTION_NOT_HANDLED,
227 ExceptionRecord->ExceptionCode,
228 (ULONG)ExceptionRecord->ExceptionAddress,
229 (ULONG)TrapFrame,
230 0);
231
232 } else {
233 //--------------//
234 // 用户模式异常 //
235 //--------------//
236
237 //
238 // Previous mode was user.
239 //
240 // If this is the first chance and the current process has a debugger
241 // port, then send a message to the debugger port and wait for a reply.
242 // If the debugger handles the exception, then continue execution. Else
243 // transfer the exception information to the user stack, transition to
244 // user mode, and attempt to dispatch the exception to a frame based
245 // handler. If a frame based handler handles the exception, then continue
246 // execution with the continue system service. Else execute the
247 // NtRaiseException system service with FirstChance == FALSE, which
248 // will call this routine a second time to process the exception.
249 //
250 // If this is the second chance and the current process has a debugger
251 // port, then send a message to the debugger port and wait for a reply.
252 // If the debugger handles the exception, then continue execution. Else
253 // if the current process has a subsystem port, then send a message to
254 // the subsystem port and wait for a reply. If the subsystem handles the
255 // exception, then continue execution. Else terminate the process.
256 //
257
258
259 if (FirstChance == TRUE) {
260
261 //
262 // This is the first chance to handle the exception.
263 //
264
265 //-------------------------------------------------------------------------------//
266 // 调用内核调试器有两个条件: //
267 // 1. 当前存在一个调试器 //
268 // 2. (当前进程的调试器端口为NULL && 不忽略异常) or 可以得到当前 ContextFrame ) //
269 //-------------------------------------------------------------------------------//
270 if ((KiDebugRoutine != NULL) &&
271
272 //---------------------------------------------------------------//
273 // 三环调试器最终会建立一个调试对象,挂在被调试进程的DebugPort处 //
274 //---------------------------------------------------------------//
275 ((PsGetCurrentProcess()->DebugPort == NULL &&
276 !KdIgnoreUmExceptions) ||
277 //-------------------------------------//
278 // 判断R3层的INT3是否能进入内核调试器 //
279 //-------------------------------------//
280 (KdIsThisAKdTrap(ExceptionRecord, &ContextFrame, UserMode)))) {
281 //
282 // Now dispatch the fault to the kernel debugger.
283 //
284
285 //------------------------//
286 // 先尝试第一次调试器处理 //
287 //------------------------//
288 if ((((KiDebugRoutine) (TrapFrame,
289 ExceptionFrame,
290 ExceptionRecord,
291 &ContextFrame,
292 PreviousMode,
293 FALSE)) != FALSE)) {
294
295 goto Handled1;
296 }
297 }
298
299
300 //---------------------------------------------------//
301 // 如果零环调试器无法处理 会调用三环调试器来进行处理 //
302 //---------------------------------------------------//
303 if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) {
304 goto Handled2;
305 }
306
307 //
308 // Transfer exception information to the user stack, transition
309 // to user mode, and attempt to dispatch the exception to a frame
310 // based handler.
311
312 ExceptionRecord1.ExceptionCode = 0; // satisfy no_opt compilation
313
314 //---------------------------------------------------------//
315 // 当内核调试器/用户调试器都处理不了该异常时 //
316 // 返回R3层的用户代码,尝试交给自定的try.catch. 来进行处理 //
317 // 对栈的存储跟APC操作类似 //
318 //---------------------------------------------------------//
319 repeat:
320 try {
321
322 //
323 // If the SS segment is not 32 bit flat, there is no point
324 // to dispatch exception to frame based exception handler.
325 //
326
327
328 if (TrapFrame->HardwareSegSs != (KGDT_R3_DATA | RPL_MASK) ||
329 TrapFrame->EFlags & EFLAGS_V86_MASK ) {
330 //-----------------------------------//
331 // 如果TrapFrame保存的不是用户层地址 //
332 // 直接出触发二次异常 //
333 //-----------------------------------//
334 ExceptionRecord2.ExceptionCode = STATUS_ACCESS_VIOLATION;
335 ExceptionRecord2.ExceptionFlags = 0;
336 ExceptionRecord2.NumberParameters = 0;
337 ExRaiseException(&ExceptionRecord2);
338 }
339
340 //
341 // Compute length of context record and new aligned user stack
342 // pointer.
343 //
344
345 //--------------------------------------//
346 // 计算"对齐并提高用户栈的大小"后的指针 //
347 //--------------------------------------//
348 UserStack1 = (ContextFrame.Esp & ~CONTEXT_ROUND) - CONTEXT_ALIGNED_SIZE;
349
350 //
351 // Probe user stack area for writability and then transfer the
352 // context record to the user stack.
353 //
354
355 //--------------------------------------------------------------------//
356 // ProbeForWrite 检测是否可写入,如果可以写入则写入内存,否则抛出异常 //
357 // 将Context结构保存到三环栈帧中,为进入零环时恢复三环的环境做准备 // //
358 //--------------------------------------------------------------------//
359 ProbeForWrite((PCHAR)UserStack1, CONTEXT_ALIGNED_SIZE, CONTEXT_ALIGN);
360 RtlCopyMemory((PULONG)UserStack1, &ContextFrame, sizeof(CONTEXT));
361
362 //
363 // Compute length of exception record and new aligned stack
364 // address.
365 //
366
367 //----------------------------//
368 // 计算放入异常参数后栈的大小 //
369 //----------------------------//
370 Length = (sizeof(EXCEPTION_RECORD) - (EXCEPTION_MAXIMUM_PARAMETERS -
371 ExceptionRecord->NumberParameters) * sizeof(ULONG) +3) &
372 (~3);
373 UserStack2 = UserStack1 - Length;
374
375 //
376 // Probe user stack area for writeability and then transfer the
377 // context record to the user stack area.
378 // N.B. The probing length is Length+8 because there are two
379 // arguments need to be pushed to user stack later.
380 //
381
382 //----------------------------------------------//
383 // 将参数放入内核中 这里提升8因为后面还需要参数 //
384 //----------------------------------------------//
385 ProbeForWrite((PCHAR)(UserStack2 - 8), Length + 8, sizeof(ULONG));
386 RtlCopyMemory((PULONG)UserStack2, ExceptionRecord, Length);
387
388 //
389 // Push address of exception record, context record to the
390 // user stack. They are the two parameters required by
391 // _KiUserExceptionDispatch.
392 //
393
394 //----------------------------------------------------------------------//
395 // 将上述两个栈地址压入栈中,将该参数是 _KiUserExceptionDispatch 需要的 //
396 //----------------------------------------------------------------------//
397 *(PULONG)(UserStack2 - sizeof(ULONG)) = UserStack1;
398 *(PULONG)(UserStack2 - 2*sizeof(ULONG)) = UserStack2;
399
400 //
401 // Set new stack pointer to the trap frame.
402 //
403
404 //-------------------------------------------//
405 // 修改_KTRAP_FRAME中寄存器ss与esp寄存器的值 //
406 // 这样返回用户层时栈帧就是修改栈帧的数据了 //
407 //-------------------------------------------//
408 KiSegSsToTrapFrame(TrapFrame, KGDT_R3_DATA);
409 KiEspToTrapFrame(TrapFrame, (UserStack2 - sizeof(ULONG)*2));
410
411 //
412 // Force correct R3 selectors into TrapFrame.
413 //
414
415 //-------------------------//
416 // 修改 TrapFrame后的数据 //
417 //-------------------------//
418 TrapFrame->SegCs = SANITIZE_SEG(KGDT_R3_CODE, PreviousMode);
419 TrapFrame->SegDs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);
420 TrapFrame->SegEs = SANITIZE_SEG(KGDT_R3_DATA, PreviousMode);
421 TrapFrame->SegFs = SANITIZE_SEG(KGDT_R3_TEB, PreviousMode);
422 TrapFrame->SegGs = 0;
423
424 //
425 // Set the address of the exception routine that will call the
426 // exception dispatcher and then return to the trap handler.
427 // The trap handler will restore the exception and trap frame
428 // context and continue execution in the routine that will
429 // call the exception dispatcher.
430 //
431
432 //--------------------------------------//
433 // 修改到返回三环的地址 //
434 // KeUserExceptionDispathcer 这个函数中 //
435 //--------------------------------------//
436 TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher;
437 return;
438
439 } except (KiCopyInformation(&ExceptionRecord1,
440 (GetExceptionInformation())->ExceptionRecord)) {
441
442 //
443 // If the exception is a stack overflow, then attempt
444 // to raise the stack overflow exception. Otherwise,
445 // the user's stack is not accessible, or is misaligned,
446 // and second chance processing is performed.
447 //
448
449 if (ExceptionRecord1.ExceptionCode == STATUS_STACK_OVERFLOW) {
450 ExceptionRecord1.ExceptionAddress = ExceptionRecord->ExceptionAddress;
451 RtlCopyMemory((PVOID)ExceptionRecord,
452 &ExceptionRecord1, sizeof(EXCEPTION_RECORD));
453 goto repeat;
454 }
455 }
456 }
457
458 //
459 // This is the second chance to handle the exception.
460 //
461
462 if (DbgkForwardException(ExceptionRecord, TRUE, TRUE)) {
463 goto Handled2;
464 } else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE)) {
465 goto Handled2;
466 } else {
467 ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode);
468 KeBugCheckEx(
469 KERNEL_MODE_EXCEPTION_NOT_HANDLED,
470 ExceptionRecord->ExceptionCode,
471 (ULONG)ExceptionRecord->ExceptionAddress,
472 (ULONG)TrapFrame,
473 0);
474 }
475 }
476
477 //
478 // Move machine state from context frame to trap and exception frames and
479 // then return to continue execution with the restored state.
480 //
481
482 Handled1:
483
484 KeContextToKframes(TrapFrame, ExceptionFrame, &ContextFrame,
485 ContextFrame.ContextFlags, PreviousMode);
486
487 //
488 // Exception was handled by the debugger or the associated subsystem
489 // and state was modified, if necessary, using the get state and set
490 // state capabilities. Therefore the context frame does not need to
491 // be transferred to the trap and exception frames.
492 //
493
494 Handled2:
495 return;
496 }