(未完成)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;
}
相關文章
- 深入理解 函式、匿名函式、自執行匿名函式函式
- 立即執行函式函式
- 自執行函式函式
- python函式每日一講 - exec執行函式Python函式
- MySQL 對window函式執行sum函式疑似BugMySql函式
- JavaScript 匿名函式與具名函式執行效率比較JavaScript函式
- js立即執行函式原理JS函式
- 多執行緒常用函式執行緒函式
- 03-立即執行函式函式
- Oracle分析函式與視窗函式Oracle函式
- 鉤子函式的執行機理函式
- js函式執行過程的探究JS函式
- Golang時間函式及測試函式執行時間案例Golang函式
- python效能優化之函式執行時間分析Python優化函式
- 常見函式之單行函式函式
- 前端進階-執行時函式前端函式
- js解惑-函式執行順序JS函式
- JavaScript 匿名立即自執行函式JavaScript函式
- 立即執行函式(IIFE)&&閉包函式
- python之為函式執行設定超時時間(允許函式執行的最大時間)Python函式
- React 中 render 函式的執行時機React函式
- 關於執行緒的幾個函式執行緒函式
- ORALCE函式:LAG()和LEAD() 分析函式詳解函式
- MySQL函式大全(字串函式,數學函式,日期函式,系統級函式,聚合函式)MySql函式字串
- main函式的入口函式AI函式
- APC 篇—— APC 執行
- python中id()函式、zip()函式、map()函式、lamda函式Python函式
- Python 擴充之特殊函式(lambda 函式,map 函式,filter 函式,reduce 函式)Python函式Filter
- JS小知識——立即執行函式JS函式
- 圖片載入完執行函式函式
- oracle資料庫常用分析函式與聚合函式的用法Oracle資料庫函式
- Oracle分析函式之開窗函式over()詳解Oracle函式
- Hive之分析函式Hive函式
- 胡扯JS系列-匿名函式的自動執行JS函式
- 執行建構函式中指定的任務函式
- 「python函式:」給定一個函式,怎麼在不修改原始碼的前提下,實現函式執行前和執行後輸出Python函式原始碼
- 箭頭函式、簡寫函式、普通函式的區別函式
- 第7章 IF函式 COUNTIF函式 SUMIF函式函式