02 APC挂入过程

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
将目标线程唤醒(从等待链表中摘出来,并挂到调度链表中)

猜你喜欢

转载自blog.csdn.net/lifeshave/article/details/87459196
02