1.APC属于内核对象,所有的内核对象都有一个结构体 -- 1:1(数据模型)
APC内核对象
1.APC用于特定的线程,一个线程具有多个APC -- 1:N
线程中相关内核重要结构
先解释一下:什么是激活的APC?
激活的APC是指当前可以被交付的APC。首先APC是属于线程的,线程又属于进程,但是线程所属的这个进程不一定亲生的,比如挂靠到另一个进程中去。
如果当一个线程挂靠到另一个进程中去,那么此时线程一般都不会交付原来进程环境中的APC,那么就要让这个APC进行备份一下。所以,在线程挂靠到另一个进程中时,它首先会将当前+0x050 ApcState保存到+0x240 SavedApcState字段中,同时ApcStatePointer[_KAPC.ApcStateIndex]指向的就SavedApcState。
KTHREAD.ApcStateIndex指向的ApcState。
谨记:ApcState总是被激活的那一个,也就是被执行的那一个。
可能比较绕,简而言之KAPC中的ApcStateIndex代表的是它所处的环境(或者它想要的环境),而KTHREAD中的ApcStateIndex指向的当前线程的环境。
由于这篇不设计用户APC,所以不概述用户APC的特征!
普通内核APC:
NormalRoutine !=0 && ApcMode ==0
特殊内核APC:
NormalRoutine ==0 && ApcMode ==0 && NormalContext==0 (三无)
KeInitializeApc:初始化内核APC
VOID
KeInitializeApc (
IN PRKAPC Apc, //得自己分配拟空间
IN PRKTHREAD Thread, //指明线程
IN KAPC_ENVIRONMENT Environment, //指明KAPC.ApcStateIndex
IN PKKERNEL_ROUTINE KernelRoutine,
IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
IN PKNORMAL_ROUTINE NormalRoutine OPTIONAL,
IN KPROCESSOR_MODE ApcMode OPTIONAL,
IN PVOID NormalContext OPTIONAL
)
这里剖析一下Environment:
typedef enum _KAPC_ENVIRONMENT {
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT;
其实OriginalApcEnvironment代表的是,你想往当前线程插入,什么意思呢?其实就是“嫁鸡随鸡,嫁狗随狗”,不管是当前线程是挂靠了还是没挂靠,我都只认现在。而AttachedApcEnvironment代表的是往挂靠的线程插入,它的意思就是我不管你挂靠了还没挂靠,我都认为是你挂靠了! -- 这种就承担了一种风险,如果它没有挂靠,那么再也执行不了!而CurrentApcEnvironment和InsertApcEnvironment,它们俩更像是代表了当前代码所处的阶段。比如CurrentApcEnvironment代表的就是正在执行KeInitializeApc函数。而InsertApcEnvironment代表是正在执行KiInsertQueueApc(是Ki不是Ke)。
前两种像是天生的,后两种就是后生的(延迟)...
KeInsertQueueApc:插入APC
BOOLEAN
KeInsertQueueApc (
IN PRKAPC Apc,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2,
IN KPRIORITY Increment
)
以KiInsertQueueApc函数为分界线,上半部分其实就只做了两件事:
一件是,提升IRQL,只让时钟中断和IPI(核间中断)打断它。
另一件,判断所属的线程是否开启了APC队列。--- 这里引出一个问题,如果一个线程没有开启APC队列后,那么它怎么结束自己呢?或者说是没有办法通过APC结束掉自己。
众人都知KiDeliverApc派发APC,殊不知我在KiInsertQueueApc中已经开始偷偷筹划。
接下来就探一探KiInsertQueueApc。
KiInsertQueueApc:实际插入APC的函数
VOID
FASTCALL
KiInsertQueueApc (
IN PKAPC InApc,
IN KPRIORITY Increment
)
此时我们要考虑两个问题:
第一个问题:
SpecialApcDisable为什么会等于FALSE?其实纵观之前代码,我们发现没有一处修改这里,这说明这个变量有可能是别人禁用APC的标志。
我举一个例子:
KeEnterGuardedRegionThread函数内部会对这个标志进行修改。
这就解释了MSDN上所描述APC关于互斥体的那段。可见SpecialApcDisable体现的也是临界区的用途。
第二个问题:
KiUnwaitThread只会将线程从等待网上摘下来,并不能立马变为就绪线程,而是延迟就绪线程,所以并不能去派发APC,那么如何去派发呢?
这时就要说出KiExitDispatcher(LockHandle.OldIrql)的作用了,它第一个功能是降低IRQL到之前的等级,其次万一KiInsertQueueApc内核唤醒的是等待线程(术语:延迟就绪线程),还会为这个延迟就绪的线程选择合适的处理器,从而去派发。
谨记:此处的派发时通过SwapContext,切换线程时候的才会有执行KiDeliverApc的时机。
KiDeliverApc:派发APC
VOID
KiDeliverApc (
IN KPROCESSOR_MODE PreviousMode,
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-8-22 09:51
被烟花易冷丶编辑
,原因: