1、KAPC结构
kd> dt _KAPC
ntdll!_KAPC
+0x000 Type : Int2B //类型 APC类型为0x12
+0x002 Size : Int2B //本结构体的大小
+0x004 Spare0 : Uint4B //未使用
+0x008 Thread : Ptr32 _KTHREAD //目标线程
+0x00c ApcListEntry : _LIST_ENTRY //APC队列挂的位置
+0x014 KernelRoutine : Ptr32 void //指向一个函数(调用ExFreePoolWithTag释放APC)
+0x018 RundownRoutine : Ptr32 void
+0x01c NormalRoutine : Ptr32 void //用户APC总入口 或者 真正的内核APC函数
+0x020 NormalContext : Ptr32 Void //内核APC:NULL 用户APC:真正的APC函数
+0x024 SystemArgument1 : Ptr32 Void //APC函数的参数
+0x028 SystemArgument2 : Ptr32 Void //APC函数的参数
+0x02c ApcStateIndex : Char //挂载哪个队列 有四个值 0 1 2 3
+0x02d ApcMode : Char //内核APC 用户APC
+0x02e Inserted : UChar //表示本APC是否已挂入内核队列 挂入前:0 挂入后:1
2、挂入流程
3、KeInitializeApc函数说明
VOID KeInitializeApc
(
IN PKAPC Apc, //KAPC指针
IN PKTHREAD Thread, //目标线程
IN KAPC_ENVIRONMENT TargetEnvironment, //0 1 2 3 四种状态
IN PKKERNEL_ROUYTINE KernelRoutine, //销毁KAPC函数地址
IN PKNORMAL_ROUTINE NormalRoutine, //用户APC总入口 或者 真正的内核APC函数
IN KPROCESSOR_MODE Mode, //要插入用户APC队列还是内核APC队列
IN PVOID Context //内核APC:NULL 用户APC:真正的APC函数
)
4、ApcStateIndex
与KTHREAD(+0x165)的属性同名,但含义不同
ApcStateIndex有四个值
0 原始环境 1 挂靠环境 2 当前环境 3 插入APC时当前环境
正常情况下:
ApcStatePointer[0]指向ApcState
ApcStatePointer[1]指向SavedApcState
挂靠情况下
ApcStatePointer[0]指向SavedApcState
ApcStatePointer[1]指向ApcState
2初始化的时候,当前进程的ApcState
3插入的时候,当前进程的ApcState
5、KiInsertAueueApc函数说明
<1>根据KAPC结构中的ApcStateIndex找到对应的APC队列
<2>根据KAPC结构中的ApcMode确定是用户队列还是内核队列
<3>将KAPC挂到对应的队列中(挂到KAPC的ApcListEntry)
<4>再将KAPC中的Inserted置1, 标识当前KAPC为已插入状态
<5>修改KAPC_STATE结构中的KernelApcPending/UserApcPending
6、Alertable属性说明(是否运行APC被吵醒)
kd> dt _KTHREAD
ntdll!_KTHREAD
+0x164 Alertable : UChar
此值为真说明当天线程是可以被APC唤醒的,否则不能唤醒
DWORD WINAPI SleepEx(DWORD dwMilliseconds,BOOL bAlertable);
WORD WINAPI WaitForSingleObjectEx( In HANDLE hHandle, In DWORD dwMilliseconds, In BOOL bAlertable );
7、总结:
Alertable=0当前插入的APC函数未必有机会执行,UserApcPending=0
Alertable=1
UserApcPending=0
将目标线程唤醒(从等待链表中摘出来,并挂到调度链表中)