04 用户APC执行过程

1、执行用户APC时的堆栈操作
处理用户APC要比内核APC复杂得多,因为用户APC要在用户空间执行,这里涉及大量的换栈操作
当线程从用户层进入内核层时,要保留原来的运行环境,如原来的各种寄存器以及栈的位置等等,然后切换成内核堆栈,如果正常返回,恢复堆栈即可
但如果用户APC要执行的话,就意味着要提前返回到用户空间去执行,而且返回的位置不是线程进入内核时的位置,而是返回其他位置,没处理一个用户APC都会涉及
内核–>用户空间–>再回到内核空间

2、KiDeliverApc函数执行流程
<1>判断用户APC链表是否为空
<2>判断第一个参数是否为1
<3>判断ApcState.UserApcPending是否为1
<4>将ApcState.UserApcPending置为0
<5>将当前APC从当前链表中摘除
<5>执行KAPC.KernelRoutine指定的函数,释放KAPC占用的空间
<7>执行完毕,将KiInitializeUsetApc置函数

3、KiInitializeUsetApc函数分析,备份CONTEXT
线程进0环时,原来的运行环境保存到_Trap_Frame结构体中,如果提前返回3环处理APC,就必须要修改_Trap_Frame结构体
KiInitializeUsetApc要做的第一件事就是备份
将原来的_Trap_Frame的值备份到一个新的结构体(CONTEXT)中,这个功能由其子函数KeContextFromKframes来完成

4、KiInitializeUsetApc函数分析:堆栈图
在这里插入图片描述

5、ntdll.KiUserApcDispatcher分析
<1>当用户在3环调用QueueUserApc函数来插入Apc时,不需要提供NormalRoutine,这个参数是在QueueUserApc内部指定的BaseDispatchAPC
<2>ZwContinue函数的意义
1)返回内核,如果还有APC继续执行前面的操作
2)如果没有要执行的APC,会将CONTEXT赋值个Trap_Frame结构体,就像重来没有改过一样,ZwContinue后面的代码不会执行,线程从哪里进0环就从哪里回来

6、总结
<1>内核APC是在线程切换时执行,不需要换栈,比较简单,一个循环执行完毕
<2>用户APC在系统调用、中断或异常返回3环前会进行判断如果有要执行的用户APC,再执行
<3>用户APC执行前会执行内核APC

猜你喜欢

转载自blog.csdn.net/lifeshave/article/details/87465746
04