日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(75)内核APC执行过程,分析 KiDeliverApc 函数

發布時間:2025/3/21 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (75)内核APC执行过程,分析 KiDeliverApc 函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、內核APC執行過程

通過分析 SwapContext ,KiSwapContexgt , KiSwapThread ,我們得出一個結論:切換線程后會執行內核APC,調用的函數是 KiDeliverApc 。

內核APC和用戶APC都要由 KiDeliverApc 函數調用,KiDeliverApc 函數首先處理內核APC,然后根據 PreviousMode 參數,用戶APC隊列是否有數據來判斷是否需要處理用戶APC。

內核APC的執行比較簡單(相對用戶APC而言),它是直接在 KiDeliverApc 函數內調用內核APC函數的。

// 調用內核APC函數 (NormalRoutine)(NormalContext,SystemArgument1,SystemArgument2);

二、KiDeliverApc 源碼(apcsup.c)

內核APC執行的細節都能在這個函數內分析出來,中文注釋是我寫的,但即使是看源碼,也有不少細節我沒分析清楚,也難免會有錯誤,歡迎讀者留言指正。

用戶APC的執行我會另寫一篇博客介紹。

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.//// 獲取當前線程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);}#endifKeAcquireInStackQueuedSpinLock(&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 隊列中移除這個 APCRemoveEntryList(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 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 是內核APC函數,經分析,我覺得能執行到這里,NormalRoutine 應該不是 NULL 的// 唯一可能修改 NormalRoutine 的就是上面調用的 KernelRoutine 函數if (NormalRoutine != (PKNORMAL_ROUTINE)NULL) {// 內核APC正在執行Thread->ApcState.KernelApcInProgress = TRUE;// 降低IRQL到0KeLowerIrql(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);// 標記插入狀態為FALSEApc->Inserted = FALSE;KeReleaseInStackQueuedSpinLock(&LockHandle);// KernelRoutine 應該就是 PspQueueApcSpecialApc // 因為用戶APC是 NtQueueApcThread 函數構造和插入的,它就是這樣初始化APC的// PspQueueApcSpecialApc 的唯一作用是釋放APC內存(KernelRoutine)(Apc,&NormalRoutine,&NormalContext,&SystemArgument1,&SystemArgument2);if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {// 此函數定義在 thredobj.c KeTestAlertThread(UserMode);} else {// 準備回3環調用 NormalContextKiInitializeUserApc(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.// 檢查當前進程是否發生變化(執行 APC 函數時發生了 attach)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; }

三、總結

  • 內核APC在線程切換的時候就會執行,這也就意味著,只要插入內核APC
    很快就會執行。

  • 在執行用戶APC之前會先執行內核APC。

  • 內核APC在內核空間執行,不需要換棧,一個循環全部執行完畢。

  • 總結

    以上是生活随笔為你收集整理的(75)内核APC执行过程,分析 KiDeliverApc 函数的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。