(75)内核APC执行过程,分析 KiDeliverApc 函数
一、內(nèi)核APC執(zhí)行過(guò)程
通過(guò)分析 SwapContext ,KiSwapContexgt , KiSwapThread ,我們得出一個(gè)結(jié)論:切換線程后會(huì)執(zhí)行內(nèi)核APC,調(diào)用的函數(shù)是 KiDeliverApc 。
內(nèi)核APC和用戶APC都要由 KiDeliverApc 函數(shù)調(diào)用,KiDeliverApc 函數(shù)首先處理內(nèi)核APC,然后根據(jù) PreviousMode 參數(shù),用戶APC隊(duì)列是否有數(shù)據(jù)來(lái)判斷是否需要處理用戶APC。
內(nèi)核APC的執(zhí)行比較簡(jiǎn)單(相對(duì)用戶APC而言),它是直接在 KiDeliverApc 函數(shù)內(nèi)調(diào)用內(nèi)核APC函數(shù)的。
// 調(diào)用內(nèi)核APC函數(shù) (NormalRoutine)(NormalContext,SystemArgument1,SystemArgument2);二、KiDeliverApc 源碼(apcsup.c)
內(nèi)核APC執(zhí)行的細(xì)節(jié)都能在這個(gè)函數(shù)內(nèi)分析出來(lái),中文注釋是我寫的,但即使是看源碼,也有不少細(xì)節(jié)我沒(méi)分析清楚,也難免會(huì)有錯(cuò)誤,歡迎讀者留言指正。
用戶APC的執(zhí)行我會(huì)另寫一篇博客介紹。
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 ormore of the APC pending flags are set at system exit and the previousIRQL is zero. All special kernel APC's are delivered first, followedby normal kernel APC's if one is not already in progress, and finallyif 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 entryto this routine IRQL is set to APC_LEVEL.N.B. The exception frame and trap frame addresses are only guaranteedto 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.//// 獲取當(dāng)前線程Thread = KeGetCurrentThread();OldTrapFrame = Thread->TrapFrame;Thread->TrapFrame = TrapFrame;// 獲取當(dāng)前進(jìn)程(提供CR3的進(jìn)程)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.// // 接下來(lái)要執(zhí)行內(nèi)核APC,這里提前聲明處理完畢Thread->ApcState.KernelApcPending = FALSE;// 遍歷內(nèi)核APC隊(duì)列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 的情況屬于特殊內(nèi)核APC,我不知道什么時(shí)候會(huì)插入這樣的APC// 所以這里就不分析了,假如您讀到這里,又知道相關(guān)的信息,不妨留言提示我一下^_^// 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// 藍(lán)屏警告if (KeGetCurrentIrql() != LockHandle.OldIrql) {KeBugCheckEx(IRQL_UNEXPECTED_VALUE,KeGetCurrentIrql() << 16 | LockHandle.OldIrql << 8,(ULONG_PTR)KernelRoutine,(ULONG_PTR)Apc,(ULONG_PTR)NormalRoutine);}#endifKeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);}else{// 走這個(gè)分支說(shuō)明 NormalRoutine 非空,是普通的內(nèi)核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) && // 沒(méi)有內(nèi)核APC正在執(zhí)行 并且(Thread->KernelApcDisable == 0)) // 沒(méi)有禁用內(nèi)核APC{// 從內(nèi)核 APC 隊(duì)列中移除這個(gè) APCRemoveEntryList(NextEntry);// APC Inserted 標(biāo)志清零Apc->Inserted = FALSE;KeReleaseInStackQueuedSpinLock(&LockHandle);// 調(diào)用 KernelRoutine,舉兩個(gè)例子說(shuō)明// 如果 APC 通過(guò)PspTerminateThreadByPointer 構(gòu)造, KernelRoutine 是 PsExitSpecialApc ,那么執(zhí)行的操作就是釋放APC內(nèi)存,并終止當(dāng)前線程// 如果 APC 通過(guò) NtQueueApcThread 構(gòu)造,KernelRoutine 是 PspQueueApcSpecialApc ,執(zhí)行的操作僅僅是釋放APC內(nèi)存// 不過(guò) NtQueueApcThread 插入的屬于用戶APC,不走這里,而是等內(nèi)核APC執(zhí)行完后再執(zhí)行// // KernelRoutine 的工作是釋放APC內(nèi)存,也可能包括一些額外的工作,如退出、掛起、恢復(fù)線程// KernelRoutine 是調(diào)用 KeInitializeApc 時(shí)決定的,是不確定的,各種函數(shù)對(duì)參數(shù)的使用情況都不一樣// 例如 PspTerminateThreadByPointer 初始化 KernelRoutine 傳的函數(shù)是 PsExitSpecialApc ,就只使用了第一個(gè)參數(shù) Apc(KernelRoutine)(Apc,&NormalRoutine,&NormalContext,&SystemArgument1,&SystemArgument2);#if DBGif (KeGetCurrentIrql() != LockHandle.OldIrql) {KeBugCheckEx(IRQL_UNEXPECTED_VALUE,KeGetCurrentIrql() << 16 | LockHandle.OldIrql << 8 | 1,(ULONG_PTR)KernelRoutine,(ULONG_PTR)Apc,(ULONG_PTR)NormalRoutine);}#endif// NormalRoutine 是內(nèi)核APC函數(shù),經(jīng)分析,我覺(jué)得能執(zhí)行到這里,NormalRoutine 應(yīng)該不是 NULL 的// 唯一可能修改 NormalRoutine 的就是上面調(diào)用的 KernelRoutine 函數(shù)if (NormalRoutine != (PKNORMAL_ROUTINE)NULL) {// 內(nèi)核APC正在執(zhí)行Thread->ApcState.KernelApcInProgress = TRUE;// 降低IRQL到0KeLowerIrql(0);// 調(diào)用內(nèi)核APC函數(shù)(NormalRoutine)(NormalContext,SystemArgument1,SystemArgument2);// 恢復(fù)IRQL到APC_LEVEL(1)KeRaiseIrql(APC_LEVEL, &LockHandle.OldIrql);}KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);// 沒(méi)有內(nèi)核APC正在執(zhí)行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.//// 內(nèi)核APC執(zhí)行完畢// 如果 PreviousMode 是用戶模式(1),并且有用戶APC,并且用戶APC隊(duì)列非空if ((IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) == FALSE) &&(PreviousMode == UserMode) && (Thread->ApcState.UserApcPending != FALSE)) {// 提前聲明用戶APC隊(duì)列已清空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隊(duì)列中取出RemoveEntryList(NextEntry);// 標(biāo)記插入狀態(tài)為FALSEApc->Inserted = FALSE;KeReleaseInStackQueuedSpinLock(&LockHandle);// KernelRoutine 應(yīng)該就是 PspQueueApcSpecialApc // 因?yàn)橛脩鬉PC是 NtQueueApcThread 函數(shù)構(gòu)造和插入的,它就是這樣初始化APC的// PspQueueApcSpecialApc 的唯一作用是釋放APC內(nèi)存(KernelRoutine)(Apc,&NormalRoutine,&NormalContext,&SystemArgument1,&SystemArgument2);if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {// 此函數(shù)定義在 thredobj.c KeTestAlertThread(UserMode);} else {// 準(zhǔn)備回3環(huán)調(diào)用 NormalContextKiInitializeUserApc(ExceptionFrame,TrapFrame,NormalRoutine, // 用戶APC總?cè)肟?BaseDispatchAPC(3環(huán)函數(shù))NormalContext, // 3環(huán)APC函數(shù)SystemArgument1, // 3環(huán)APC函數(shù)的參數(shù)SystemArgument2); // 作用不明,BaseDispatchAPC 里用到了}} else {KeReleaseInStackQueuedSpinLock(&LockHandle);}//// Check if process was attached during the APC routine.// 檢查當(dāng)前進(jìn)程是否發(fā)生變化(執(zhí)行 APC 函數(shù)時(shí)發(fā)生了 attach)CheckProcess:if (Thread->ApcState.Process != Process) {// 藍(lán)屏警告KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,(ULONG_PTR)Process,(ULONG_PTR)Thread->ApcState.Process,(ULONG)Thread->ApcStateIndex,(ULONG)KeIsExecutingDpc());}Thread->TrapFrame = OldTrapFrame;return; }三、總結(jié)
內(nèi)核APC在線程切換的時(shí)候就會(huì)執(zhí)行,這也就意味著,只要插入內(nèi)核APC
很快就會(huì)執(zhí)行。
在執(zhí)行用戶APC之前會(huì)先執(zhí)行內(nèi)核APC。
內(nèi)核APC在內(nèi)核空間執(zhí)行,不需要換棧,一個(gè)循環(huán)全部執(zhí)行完畢。
總結(jié)
以上是生活随笔為你收集整理的(75)内核APC执行过程,分析 KiDeliverApc 函数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: (74)分析 APC 插入过程 —— K
- 下一篇: vs2019+wdk10开发 xp, w