(未完成)APC函式的執行,分析 KiDeliverApc 函式

hambaga發表於2020-11-30

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;
}

相關文章