APC插入:KiDeliverApc(位于:ntoskrnl/ke/apc.c
):
- 保存当前TrapFrame到局部变量里,设置新的TrapFrame(参数提供)
- 设置APC状态的内核APC挂起状态为FALSE
- 判断特殊APC是否被禁用,被禁用就返回
- 内核APC处理:内核APC链表不为空则循环
- 给APC队列上锁
- 如果链表是空的,就解锁退出循环
- 再次设置APC状态的APC挂起状态为FALSE
- 获取链表中下一个内核APC,将其2个回调3个参数保存到局部变量里
- 判断,如果是特殊内核APC
- APC摘链,插入标记设置为FALSE
- 释放APC锁,执行APC Kernel Routinue
- 如果是普通内核APC
- 判断当前APC是否在执行中,或在内核APC是否被禁用,如果是则解锁,返回
- APC摘链,插入标记设置为FALSE
- 释放APC锁,执行APC Kernel Routinue
- 将APC执行标记设置为TRUE
- 调整IRQL等级为PASSIVE_LEVEL调用执行NormalRoutine,再调回IRQL等级
- 将APC执行标记设置为FALSE
- 用户APC处理:传递模式为用户模式,用户APC链表不为空,用户APC挂起标志为1则向下进行
- 给APC队列上锁
- 如果链表是空的,就解锁退出循环
- 再次设置APC状态的APC挂起状态为FALSE
- 获取链表中下一个用户APC,将其2个回调3个参数保存到局部变量里
- APC摘链,插入标记设置为FALSE
- 释放APC锁,执行APC Kernel Routinue(在Ring0执行)
- 判断有没有NormalRoutinue
- 如果有就调用KiInitializeUserApc去进一步进行初始化执行(在Ring3执行)
- 如果没有则调用KeTestAlertThread判断有没有更多挂起的用户APC
- 确认一下线程是否还在同一个进程中,不在就蓝屏
- 还原TrapFrame到线程里,返回退出
【简洁版流程介绍】用户APC和内核APC处理过程很像:
- 都是先从APC队列获取一个APC,保存2个回调3个参数到局部变量里
- 内核APC的话,先执行KernelRoutine,如果有NormalRoutine,就执行完KernelRoutine后执行一下
- 用户APC的话,先执行KernelRoutine,如果有NormalRoutine,就执行KiInitializeUserApc,如果没有就执行KeTestAlertThread
KiDeliverApc
/*++
* @name KiDeliverApc
* @implemented @NT4
*
* The KiDeliverApc routine is called from IRQL switching code if the
* thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
* pending.
*
* @param DeliveryMode
* Specifies the current processor mode.
*
* @param ExceptionFrame
* Pointer to the Exception Frame on non-i386 builds.
*
* @param TrapFrame
* Pointer to the Trap Frame.
*
* @return None.
*
* @remarks First, Special APCs are delivered, followed by Kernel-Mode APCs and
* User-Mode APCs. Note that the TrapFrame is only valid if the
* delivery mode is User-Mode.
* Upon entry, this routine executes at APC_LEVEL.
*
*--*/
VOID
NTAPI
KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame)
{
PKTHREAD Thread = KeGetCurrentThread();
PKPROCESS Process = Thread->ApcState.Process;
PKTRAP_FRAME OldTrapFrame;
PLIST_ENTRY ApcListEntry;
PKAPC Apc;
KLOCK_QUEUE_HANDLE ApcLock;
PKKERNEL_ROUTINE KernelRoutine;
PVOID NormalContext;
PKNORMAL_ROUTINE NormalRoutine;
PVOID SystemArgument1;
PVOID SystemArgument2;
ASSERT_IRQL_EQUAL(APC_LEVEL);
/* Save the old trap frame and set current one */
// 保存老的TrapFrame到局部变量
OldTrapFrame = Thread->TrapFrame;
// 用参数的TrapFrame设置线程的TrapFrame
Thread->TrapFrame = TrapFrame;
/* Clear Kernel APC Pending */
// 清空线程结构中APC状态的内核APC执行标记
Thread->ApcState.KernelApcPending = FALSE;
// 判断特殊APC是否被禁用,禁用就跳转
/* Check if Special APCs are disabled */
if (Thread->SpecialApcDisable) goto Quickie;
/* Do the Kernel APCs first */
// 先处理内核APC
// 内核APC链表不为空就循环遍历每一个内核APC
while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
{
/* Lock the APC Queue */
// 锁定APC队列
KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
// 检查内核APC链表现在是否变空了
/* Check if the list became empty now */
if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
{
/* It is, release the lock and break out */
// 释放锁,退出循环
KiReleaseApcLock(&ApcLock);
break;
}
// 清空执行标记
/* Kernel APC is not pending anymore */
Thread->ApcState.KernelApcPending = FALSE;
// 获取下一个内核APC结构
/* Get the next Entry */
ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
/* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
// 参数保存APC的2个回调,3个参数
NormalRoutine = Apc->NormalRoutine;
KernelRoutine = Apc->KernelRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
// 特殊APC
/* Special APC */
if (!NormalRoutine) // 没有NormalContext表示是特殊APC
{
// 把当前获取到的APC移除链表
/* Remove the APC from the list */
RemoveEntryList(ApcListEntry);
// Apc插入标志设置为False
Apc->Inserted = FALSE;
/* Release the APC lock */
// 释放APC锁
KiReleaseApcLock(&ApcLock);
// 执行特殊APC
/* Call the Special APC */
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
// 判断IRQL,确保正确返回
/* Make sure it returned correctly */
if (KeGetCurrentIrql() != ApcLock.OldIrql)
{
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
(KeGetCurrentIrql() << 16) |
(ApcLock.OldIrql << 8),
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
}
}
else
{
// 普通APC,判断是否符合内核APC执行状态
/* Normal Kernel APC, make sure it's safe to deliver */
if ((Thread->ApcState.KernelApcInProgress) || // 判断当前内核APC是否正在执行中
(Thread->KernelApcDisable)) // 或线程里的内核APC禁用标志被设置了
{
// 解放锁并返回
/* Release lock and return */
KiReleaseApcLock(&ApcLock);
goto Quickie;
}
/* Dequeue the APC */
// 移除APC链表,清空标记
// APC断链,设置插入标记为False
RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE;
// 解锁
/* Go back to APC_LEVEL */
KiReleaseApcLock(&ApcLock);
// 执行内核APC
/* Call the Kernel APC */
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
/* Make sure it returned correctly */
// 判断IRQL等级和之前是否一致
if (KeGetCurrentIrql() != ApcLock.OldIrql)
{
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
(KeGetCurrentIrql() << 16) |
(ApcLock.OldIrql << 8),
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
}
/* Check if there still is a Normal Routine */
// 确保依然是普通内核APC
if (NormalRoutine)
{
/* At Passive Level, an APC can be prempted by a Special APC */
// 在Passive_LEVEL,APC能被特殊APC执行
//设置IRQL等级,然后再调回来
// 设置APC执行标记为true
Thread->ApcState.KernelApcInProgress = TRUE;
KeLowerIrql(PASSIVE_LEVEL);
/* Call and Raise IRQL back to APC_LEVEL */
// 调用执行APC
NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql);
}
/* Set Kernel APC in progress to false and loop again */
// 设置APC执行标记为false
Thread->ApcState.KernelApcInProgress = FALSE;
}
}
// 用户APC
/* Now we do the User APCs */
// 传递模式为用户模式,用户APC链表不为空,用户APC执行状态为true,挂起
if ((DeliveryMode == UserMode) &&
!(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
(Thread->ApcState.UserApcPending))
{
/* Lock the APC Queue */
// APC队列上锁
KiAcquireApcLockAtApcLevel(Thread, &ApcLock);
/* It's not pending anymore */
// 设置挂起标志为FALSE
Thread->ApcState.UserApcPending = FALSE;
// 判断链表现在是不是空的
/* Check if the list became empty now */
if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
{
// 解锁,然后跳出
/* It is, release the lock and break out */
KiReleaseApcLock(&ApcLock);
goto Quickie;
}
/* Get the actual APC object */
// 获取一个用户APC
ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
// 给参数保存2个回调,3个参数
/* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
NormalRoutine = Apc->NormalRoutine;
KernelRoutine = Apc->KernelRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
/* Remove the APC from Queue, and release the lock */
// 移除链表,设置插入标记为FALSE,解锁
RemoveEntryList(ApcListEntry);
Apc->Inserted = FALSE;
KiReleaseApcLock(&ApcLock);
/* Call the kernel routine */
// 执行APC
KernelRoutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
/* Check if there's no normal routine */
// 用户模式没有特殊APC
if (!NormalRoutine)// 如果没有NormalRoutine
{
/* Check if more User APCs are Pending */
// 判断有没有更多用户APC
KeTestAlertThread(UserMode);
}
else // 如果有NormalRoutine
{
/* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
// 设置TrapFrame并准备在ntdll.dll中执行
KiInitializeUserApc(ExceptionFrame,
TrapFrame,
NormalRoutine,
NormalContext,
SystemArgument1,
SystemArgument2);
}
}
Quickie:
/* Make sure we're still in the same process */
// 判断是不是属于同一个进程,不相等就蓝屏
if (Process != Thread->ApcState.Process)
{
/* Erm, we got attached or something! BAD! */
KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
(ULONG_PTR)Process,
(ULONG_PTR)Thread->ApcState.Process,
Thread->ApcStateIndex,
KeGetCurrentPrcb()->DpcRoutineActive);
}
/* Restore the trap frame */
// 还原TrapFrame
Thread->TrapFrame = OldTrapFrame;
}