当产生系统调用、中断或者异常,线程在返回用户空间前都会调用, _KiServiceExit函数,在_KiServiceExit会判断是否有要执行的用户APC,如果有则调用KiDeliverApc函数(第一个参数为1)进行处理。
执行用户APC时的堆栈操作
处理用户APC要比内核APC复杂的多,因为,用户APC函数要在用户空间执行的,这里涉及到大量换栈的操作:
当线程从用户层进入内核层时,要保留原来的运行环境,比如各种寄存 囚器,栈的位置等等(_Trap_Frame),然后切换成内核的堆栈,如果正常返回, 您恢复堆栈环境即可。
但如果有用户APC要执行的话,就意味着线程要提前返回到用户空间去,执行,而且返回的位置不是线程进入内核时的位置,而是返回到其他的位置,每处理一个用户APC都会涉及到:
内核->用户空间->再回到内核空间
堆栈的操作比较复杂,如果不了解堆栈的操作细节不可能理解用户APC是如何执行!
KiDeliverApc函数分析
无论是内核APC还是用户APC首先执行的都是这个函数
- 判断用户APC链表是否为空
- 判断第一个参数是为1
- 判断ApcState.UserApcPending是否为1
- 将ApcState.UserApcPending设置为0
- 链表操作将当前APC从用户队列中拆除
- 调用函数(KAPC.KernelRoutine)释放KAPC结构体内存空间
- 调用KilnitializeUserApc函数
取完内核APC后取用户APC,然后判断你用户APC是不是空的,不为空就跳转。
KilnitializeUserApc函数分析:备份CONTEXT
线程进0环时,原来的运行环境(寄存器栈顶等)保存到Trap_Frame结构体中,如果要提前返回3环去处理用户APC,就必须要修改Trap _Frame结构体:
比如:进0环时的位置存储在EIP中,现在要提前返回,而且返回的并不,是原来的位置,那就意味着必须要修改EIP为新的返回位置。还有堆栈ESP也要修改为处理APC需要的堆栈。那原来的值怎么办呢?处理完APC后该如何返