(未完成)APC函式的執行,分析 KiDeliverApc 函式
KiDeliverApc 原始碼在 apcsup.c
VOID
KiDeliverApc (
IN KPROCESSOR_MODE PreviousMode,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame
)
/*++
Routine Description:
This function is called from the APC interrupt code and when one or
more of the APC pending flags are set at system exit and the previous
IRQL is zero. All special kernel APC's are delivered first, followed
by normal kernel APC's if one is not already in progress, and finally
if the user APC queue is not empty, the user APC pending flag is set,
and the previous mode is user, then a user APC is delivered. On entry
to this routine IRQL is set to APC_LEVEL.
N.B. The exception frame and trap frame addresses are only guaranteed
to be valid if, and only if, the previous mode is user.
Arguments:
PreviousMode - Supplies the previous processor mode.
ExceptionFrame - Supplies a pointer to an exception frame.
TrapFrame - Supplies a pointer to a trap frame.
Return Value:
None.
--*/
{
PKAPC Apc;
PKKERNEL_ROUTINE KernelRoutine;
KLOCK_QUEUE_HANDLE LockHandle;
PLIST_ENTRY NextEntry;
ULONG64 NewPC;
PVOID NormalContext;
PKNORMAL_ROUTINE NormalRoutine;
ULONG64 PC;
PKPROCESS Process;
PVOID SystemArgument1;
PVOID SystemArgument2;
PKTHREAD Thread;
PKTRAP_FRAME OldTrapFrame;
//
// If the thread was interrupted in the middle of the SLIST pop code,
// then back up the PC to the start of the SLIST pop.
//
if (TrapFrame != NULL) {
#if defined(_AMD64_)
if ((TrapFrame->Rip >= (ULONG64)&ExpInterlockedPopEntrySListResume) &&
(TrapFrame->Rip <= (ULONG64)&ExpInterlockedPopEntrySListEnd)) {
TrapFrame->Rip = (ULONG64)&ExpInterlockedPopEntrySListResume;
}
#elif defined(_IA64_)
//
// Add the slot number so we do the right thing for the instruction
// group containing the interlocked compare exchange.
//
PC = TrapFrame->StIIP + ((TrapFrame->StIPSR & IPSR_RI_MASK) >> PSR_RI);
NewPC = (ULONG64)((PPLABEL_DESCRIPTOR)ExpInterlockedPopEntrySListResume)->EntryPoint;
if ((PC >= NewPC) &&
(PC <= (ULONG64)((PPLABEL_DESCRIPTOR)ExpInterlockedPopEntrySListEnd)->EntryPoint)) {
TrapFrame->StIIP = NewPC;
TrapFrame->StIPSR &= ~IPSR_RI_MASK;
}
#elif defined(_X86_)
if ((TrapFrame->Eip >= (ULONG)&ExpInterlockedPopEntrySListResume) &&
(TrapFrame->Eip <= (ULONG)&ExpInterlockedPopEntrySListEnd)) {
TrapFrame->Eip = (ULONG)&ExpInterlockedPopEntrySListResume;
}
#else
#error "No Target Architecture"
#endif
}
//
// Raise IRQL to dispatcher level and lock the APC queue.
//
// 獲取當前執行緒
Thread = KeGetCurrentThread();
OldTrapFrame = Thread->TrapFrame;
Thread->TrapFrame = TrapFrame;
// 獲取當前程式(提供CR3的程式)
Process = Thread->ApcState.Process;
KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
//
// Get address of current thread object, clear kernel APC pending, and
// check if any kernel mode APC's can be delivered.
//
// 接下來要執行核心APC,這裡提前宣告處理完畢
Thread->ApcState.KernelApcPending = FALSE;
// 遍歷核心APC佇列
while (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]) == FALSE) {
// 獲取 APC,獲取 APC 的成員
NextEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
KernelRoutine = Apc->KernelRoutine;
NormalRoutine = Apc->NormalRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
if (NormalRoutine == (PKNORMAL_ROUTINE)NULL)
{
// NormalRoutine 等於 NULL 的情況屬於特殊核心APC,我不知道什麼時候會插入這樣的APC
// 所以這裡就不分析了,假如您讀到這裡,又知道相關的資訊,不妨留言提示我一下^_^
// 2020年11月29日21:04:21
//
// First entry in the kernel APC queue is a special kernel APC.
// Remove the entry from the APC queue, set its inserted state
// to FALSE, release dispatcher database lock, and call the kernel
// routine. On return raise IRQL to dispatcher level and lock
// dispatcher database lock.
//
RemoveEntryList(NextEntry);
Apc->Inserted = FALSE;
KeReleaseInStackQueuedSpinLock(&LockHandle);
(KernelRoutine)(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
#if DBG
// 藍屏警告
if (KeGetCurrentIrql() != LockHandle.OldIrql) {
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
KeGetCurrentIrql() << 16 | LockHandle.OldIrql << 8,
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
}
#endif
KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
}
else
{
// 走這個分支說明 NormalRoutine 非空,是普通的核心APC,PspTerminateThreadByPointer 和 NtQueueApcThread 都走這裡
//
// First entry in the kernel APC queue is a normal kernel APC.
// If there is not a normal kernel APC in progress and kernel
// APC's are not disabled, then remove the entry from the APC
// queue, set its inserted state to FALSE, release the APC queue
// lock, call the specified kernel routine, set kernel APC in
// progress, lower the IRQL to zero, and call the normal kernel
// APC routine. On return raise IRQL to dispatcher level, lock
// the APC queue, and clear kernel APC in progress.
//
if ((Thread->ApcState.KernelApcInProgress == FALSE) && // 沒有核心APC正在執行 並且
(Thread->KernelApcDisable == 0)) // 沒有禁用核心APC
{
// 從核心 APC 佇列中移除這個 APC
RemoveEntryList(NextEntry);
// APC Inserted 標誌清零
Apc->Inserted = FALSE;
KeReleaseInStackQueuedSpinLock(&LockHandle);
// 呼叫 KernelRoutine,舉兩個例子說明
// 如果 APC 通過PspTerminateThreadByPointer 構造, KernelRoutine 是 PsExitSpecialApc ,那麼執行的操作就是釋放APC記憶體,並終止當前執行緒
// 如果 APC 通過 NtQueueApcThread 構造,KernelRoutine 是 PspQueueApcSpecialApc ,執行的操作僅僅是釋放APC記憶體
// 不過 NtQueueApcThread 插入的屬於使用者APC,不走這裡,而是等核心APC執行完後再執行
//
// KernelRoutine 的工作是釋放APC記憶體,也可能包括一些額外的工作,如退出、掛起、恢復執行緒
// KernelRoutine 是呼叫 KeInitializeApc 時決定的,是不確定的,各種函式對引數的使用情況都不一樣
// 例如 PspTerminateThreadByPointer 初始化 KernelRoutine 傳的函式是 PsExitSpecialApc ,就只使用了第一個引數 Apc
(KernelRoutine)(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
#if DBG
if (KeGetCurrentIrql() != LockHandle.OldIrql) {
KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
KeGetCurrentIrql() << 16 | LockHandle.OldIrql << 8 | 1,
(ULONG_PTR)KernelRoutine,
(ULONG_PTR)Apc,
(ULONG_PTR)NormalRoutine);
}
#endif
// NormalRoutine 是核心APC函式,經分析,我覺得能執行到這裡,NormalRoutine 應該不是 NULL 的
// 唯一可能修改 NormalRoutine 的就是上面呼叫的 KernelRoutine 函式
if (NormalRoutine != (PKNORMAL_ROUTINE)NULL) {
// 核心APC正在執行
Thread->ApcState.KernelApcInProgress = TRUE;
// 降低IRQL到0
KeLowerIrql(0);
// 呼叫核心APC函式
(NormalRoutine)(NormalContext,
SystemArgument1,
SystemArgument2);
// 恢復IRQL到APC_LEVEL(1)
KeRaiseIrql(APC_LEVEL, &LockHandle.OldIrql);
}
KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
// 沒有核心APC正在執行
Thread->ApcState.KernelApcInProgress = FALSE;
} else {
KeReleaseInStackQueuedSpinLock(&LockHandle);
goto CheckProcess;
}
}
}
//
// Kernel APC queue is empty. If the previous mode is user, user APC
// pending is set, and the user APC queue is not empty, then remove
// the first entry from the user APC queue, set its inserted state to
// FALSE, clear user APC pending, release the dispatcher database lock,
// and call the specified kernel routine. If the normal routine address
// is not NULL on return from the kernel routine, then initialize the
// user mode APC context and return. Otherwise, check to determine if
// another user mode APC can be processed.
//
// 核心APC執行完畢
// 如果 PreviousMode 是使用者模式(1),並且有使用者APC,並且使用者APC佇列非空
if ((IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) == FALSE) &&
(PreviousMode == UserMode) &&
(Thread->ApcState.UserApcPending != FALSE))
{
// 提前宣告使用者APC佇列已清空
Thread->ApcState.UserApcPending = FALSE;
// 獲取APC和其屬性
NextEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
KernelRoutine = Apc->KernelRoutine;
NormalRoutine = Apc->NormalRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
// 從使用者APC佇列中取出
RemoveEntryList(NextEntry);
// 標記插入狀態為FALSE
Apc->Inserted = FALSE;
KeReleaseInStackQueuedSpinLock(&LockHandle);
// KernelRoutine 應該就是 PspQueueApcSpecialApc
// 因為使用者APC是 NtQueueApcThread 函式構造和插入的,它就是這樣初始化APC的
// PspQueueApcSpecialApc 的唯一作用是釋放APC記憶體
(KernelRoutine)(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {
KeTestAlertThread(UserMode);
} else {
KiInitializeUserApc(ExceptionFrame,
TrapFrame,
NormalRoutine, // 使用者APC總入口 BaseDispatchAPC(3環函式)
NormalContext, // 3環APC函式
SystemArgument1, // 3環APC函式的引數
SystemArgument2); // 作用不明,BaseDispatchAPC 裡用到了
}
} else {
KeReleaseInStackQueuedSpinLock(&LockHandle);
}
//
// Check if process was attached during the APC routine.
//
CheckProcess:
if (Thread->ApcState.Process != Process) {
// 藍屏警告
KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
(ULONG_PTR)Process,
(ULONG_PTR)Thread->ApcState.Process,
(ULONG)Thread->ApcStateIndex,
(ULONG)KeIsExecutingDpc());
}
Thread->TrapFrame = OldTrapFrame;
return;
}
相關文章
- 深入理解 函式、匿名函式、自執行匿名函式函式
- 自執行函式函式
- 立即執行函式函式
- 自執行函式的理解函式
- MySQL 對window函式執行sum函式疑似BugMySql函式
- python函式每日一講 - exec執行函式Python函式
- Oracle聚合函式/分析函式Oracle函式
- Oracle分析函式七——分析函式案例Oracle函式
- JavaScript 匿名函式與具名函式執行效率比較JavaScript函式
- js匿名函式和具名函式執行效率比較JS函式
- windows核心原理分析之DPC函式的執行(1)Windows函式
- 1.自執行函式表示式函式
- C++建構函式解構函式的執行過程C++函式
- JavaScript中的立即執行函式JavaScript函式
- 正規表示式exec()函式只有第一執行有效分析函式
- 分析函式函式
- js立即執行函式原理JS函式
- js 匿名自執行函式JS函式
- 03-立即執行函式函式
- 多執行緒常用函式執行緒函式
- C/C++——建構函式、複製建構函式和解構函式的執行時刻C++函式
- Oracle分析函式與視窗函式Oracle函式
- 【Analytic】分析函式之MIN函式函式
- 【Analytic】分析函式之MAX函式函式
- 【Analytic】分析函式之AVG函式函式
- 【Analytic】分析函式之RANK函式函式
- 【Analytic】分析函式之COUNT函式函式
- oracle的分析函式over 及開窗函式Oracle函式
- js 立即執行函式表示式介紹JS函式
- 【開發篇sql】 分析函式(二) 行篩選相關的函式SQL函式
- 鉤子函式的執行機理函式
- js函式執行過程的探究JS函式
- javascript如何終止函式的執行JavaScript函式
- JavaScript自執行函式(function(){})()的理解JavaScript函式Function
- javascript中的自執行(立即執行)函式(function(){…})()JavaScript函式Function
- Golang時間函式及測試函式執行時間案例Golang函式
- 【分析函式】Oracle分析函式之LAG和LEAD函式Oracle
- ORACLE單行函式與多行函式之七:多行函式之分組函式示例Oracle函式