什么是APC
如果想改变一个线程的行为,可以给它提供一个函数,让他自己去调用,这个函数就是APC(Asyncroneus Procedure Call),即异步过程调用。
谁插入的APC
一定是由别的线程插入。给别的线程插入APC相当于给它写一封信,程序会每隔一段时间去信箱里面检查有没有信。
插入到哪里
_KTHREAD +0x34 有两个APC队列 用户APC和内核APC
谁执行的APC
插入的目标线程执行的APC
什么时候执行的APC
内核APC在线程切换的时候执行
用户APC在KiServiceExit的时候执行
Apc队列 _KAPC_STATE
+dt _KAPC_STATE
+0 ApcLIstHead // +0 内核APC队列 +8用户Apc 队列 记录的是所有APC结构体(初始化APC时,申请了一块内存来保存APC信息,链表记录的就是这个结构体+c的地址) 。
+0x10 Process //线程所属或者所挂靠的进程
+0x14 KernelApcInProcess //内核APC是否正在执行
+0x15 KernelApcInPending //是否有正在等待执行的内核APC
+0x16 UserApcPending //是否有正在等待执行的用户APC
_KAPC结构体
typedef struct _KAPC {
CSHORT Type; //apc类型为0x12
CSHORT Size; //结构体大小0x30
ULONG Spare0; //未使用
PVOID Thread; //PKTHREAD 目标线程
LIST_ENTRY ApcListEntry; //LIST_ENTRY
PKKERNEL_ROUTINE KernelRoutine; //指向函数IopDeallocateApc (调用ExFreePoolWithTag释放APC)
PKRUNDOWN_ROUTINE RundownRoutine;
PKNORMAL_ROUTINE NormalRoutine; //用户APC总入口 全局变量KeUserApcDispatcher的值 指向KiUserApcDispatcher函数 或者真正内核apc函数
PVOID NormalContext; //内核APC :NULL 用户APC :真正的apc函数
PVOID SystemArgument1; //QueueUserApc的参数三
PVOID SystemArgument2; //返回值
CCHAR ApcStateIndex; //挂哪一个队列,0 1 2 3 自己用的时候使用0或者1 0挂亲爹 1挂养父
KPROCESSOR_MODE ApcMode; //内核APC 0 用户APC 1
BOOLEAN Inserted; // 表示本APC是否已挂入队列 挂入前 0 挂入后 1
} KAPC, *PKAPC, *RESTRICTED_POINTER PRKAPC;
APC队列结构体的查找
链表里面的值(_KAPC)取出来要-0c才是结构体的首地址(链表挂在腰上)
练习:
1、自己编写代码向某个线程插入一个用户APC
2、分析TerminateThread/SuspendThread是如何实现的(从3环开始分析)
3、自己分析APC的插入过程