avatar

Catalog
APC执行时机

前言

在学习了APC挂入过程后,按顺序接下来就该探究APC的执行过程了,但是在这之前,还需要了解一件事,就是APC何时执行。

内核APC执行时机

了解完APC执行时机,才能跟进探究APC的执行过程,本篇先从内核APC开始。

SwapContext

首先观察SwapContext函数,之前的文章分析SwapContext曾分析过这个函数,但那次主要是分析函数的整体执行流程,仍有细节被忽视。

位于SwapContext最后的部分,有一个判断,会判断当前线程的KernelApcPending字段的值是否为0。在APC的本质这一篇中提到过,该字段表示是否有正在等待执行的内核APC。观察绿色方框框住的不同情况的结果:

  • 若有等待执行的内核APC,则发生跳转,并将eax低8位的值,修改为1
  • 若无等待执行的内核APC,会修改eax的值为0

可以发现,有无待执行的内核APC,影响的值是eax,而eax,通常作为返回值而存在。所以向上分析,看是谁调用了SwapContext,谁就会用到eax。

可以看到,上一层调用SwapContext函数的是KiSwapContext

KiSwapContext

观察KiSwapContext,可以看到,它在调用了SwapContext后并没有使用到eax的值,就返回了,因此还需要再往上看一层,看哪个函数调用了KiSwapContext,由图,可以得到KiSwapThread。

KiSwapThread

定位到调用KiSwapContext的位置,在执行完后,对返回值eax做了一次判断(test指令即对eax作与运算);若此时eax的值不为0(即有等待执行的内核APC),则会进行跳转,并在接下来调用KiDeliverApc函数KiDeliverApc函数,就是用来执行APC的函数。

KiDeliverApc函数既可以处理内核APC又可以处理用户APC,具体情况取决于其第一个参数。当第一个参数值为0时,仅处理内核APC;当第一个参数值为1时,先处理内核APC,再处理用户APC。所以,在任何情况下,都会优先处理内核APC,因而可以得出结论,当发生线程切换(SwapContext)的时候,内核APC会被执行

继续分析图中代码,在执行KiDeliverApc函数之前,eax会先被清零,这意味着,此处调用KiDeliverApc,仅会处理内核APC。那么用户APC何时得到机会执行呢?接着往下看。

用户APC执行时机

在线程切换时,内核APC得到执行的机会,但是用户APC却不行。下面来看一个函数KiServiceExit。

观察红框框住的部分,这里有一个判断,和SwapContext类似,这里会判断当前线程的UserApcPending的值是否为0,即判断是否有等待执行的用户APC。若值为0,说明不存待执行的用户APC,跳转离开。否则,继续执行,就可以执行到前面提到的KiDeliverApc函数。此时可以看到,第一个参数被固定为1,即先处理内核APC再处理用户APC。这里即为用户APC的执行点。

当0环返回3环时,KiServiceExit函数会被调用,此时便会执行用户APC。

参考资料

  • 滴水3期中级预习班
Author: cataLoc
Link: http://cataloc.gitee.io/blog/2020/08/07/Apc%E6%89%A7%E8%A1%8C%E6%97%B6%E6%9C%BA/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶