Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.htmlhtml
APC的本质异步
1、对于线程关闭问题的启发函数
线程,自己占据CPU,对CPU有直接控制权。spa
这就存在一个问题,若是一个线程不想关闭本身,则外界是没法干涉它的。线程
所以,线程须要本身杀死本身。code
可是线程自己是代码,其并不知道什么时候才须要杀死本身,这时须要咱们另外提供代码,让其杀死本身。orm
所以,线程的操做机制就是:定时检查是否有另外执行的代码,而后去执行。该代码(函数),就是APC。htm
2、APC介绍blog
1. APC,即Asynchronous procedure call,异步程序调用。
索引
咱们理解“异步”这个词,线程自己的代码是“同步”的,在此以外能够认为是异步。
2. _KAPC_STATE 结构体(具体做用右侧已经标记出来)
其在 _KTHREAD + 0x34 的位置。
kd> dt _KAPC_STATE
ntdll!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY // APC队列,两个双向链表,分别指向用户与内核结构的APC
+0x010 Process : Ptr32 _KPROCESS // 线程所属的进程或者挂靠的进程
+0x014 KernelApcInProgress : UChar // 表示当前内核中的APC程序是否正在执行
+0x015 KernelApcPending : UChar // 表示APC队列中是否有内核APC函数,若是有为1,不然为0.
+0x016 UserApcPending : UChar // 表示APC队列中是否用用户APC函数,若是有为1,不然为0.
3. _KAPC 结构体
kd> dt _KAPC
ntdll!_KAPC
+0x000 Type : Int2B
+0x002 Size : Int2B
+0x004 Spare0 : Uint4B
+0x008 Thread : Ptr32 _KTHREAD
+0x00c ApcListEntry : _LIST_ENTRY
+0x014 KernelRoutine : Ptr32 void
+0x018 RundownRoutine : Ptr32 void
+0x01c NormalRoutine : Ptr32 void
+0x020 NormalContext : Ptr32 Void
+0x024 SystemArgument1 : Ptr32 Void
+0x028 SystemArgument2 : Ptr32 Void
+0x02c ApcStateIndex : Char
+0x02d ApcMode : Char
+0x02e Inserted : UChar
在 _KAPC_STATE中前两个双向链表指向的就是一个个 _KAPC函数成员, 经过+0x01c NormalRoutine,能够查看代码(注意并非指向函数地址)
3、何时执行APC函数
1. 先介绍两个函数
a. KiServiceExit函数:这个函数是系统调用、异常或中断的必经之路。
b. KiDeliverApc函数: 负责执行APC函数。
2. 在执行 KiServiceExit函数时,其会从线程结构体中拿出 _KAPC_STATE.KernelApcPending是否为零。
若是不为零,则会调用KiDeliverApc去执行APC函数,当执行完一次后再跳转回来进行遍历。
4、KiServiceExit2部分源码解读:
在WindowsXp 专业版的 ntoskrnl.exe 中并未搜索到 KiServiceExit 函数;在有关快速调用的代码中查看到 KiServiceExit2 (ntkrnlpa.exe)
.text:004667F0 cli .text:004667F1 test dword ptr [ebp+70h], 20000h .text:004667F8 jnz short loc_466800 ; 获取Kthread .text:004667FA test byte ptr [ebp+6Ch], 1 .text:004667FE jz short loc_466834 .text:00466800 .text:00466800 loc_466800: ; CODE XREF: _KiServiceExit2+8↑j .text:00466800 ; _KiServiceExit2+41↓j .text:00466800 mov ebx, ds:0FFDFF124h ; 获取Kthread .text:00466806 mov byte ptr [ebx+2Eh], 0 .text:0046680A cmp byte ptr [ebx+4Ah], 0 ; 判断是否有用户APC请求 .text:0046680E jz short loc_466834 ; 若是有用户APC请求会走这里 .text:00466810 mov ebx, ebp .text:00466812 mov ecx, 1 ; NewIrql .text:00466817 call ds:__imp_@KfRaiseIrql@4 ; KfRaiseIrql(x) .text:0046681D push eax .text:0046681E sti .text:0046681F push ebx .text:00466820 push 0 .text:00466822 push 1 .text:00466824 call _KiDeliverApc@12 ; 该函数实现对APC的处理 .text:00466829 pop ecx ; NewIrql .text:0046682A call ds:__imp_@KfLowerIrql@4 ; KfLowerIrql(x) .text:00466830 cli .text:00466831 jmp short loc_466800 ; 获取Kthread