selph
selph
发布于 2021-11-16 / 368 阅读
0
0

APC投递过程--基于Windows XP源码

APC插入:KiDeliverApc(位于:ntoskrnl/ke/apc.c):

  • 保存当前TrapFrame到局部变量里,设置新的TrapFrame(参数提供)
  • 设置APC状态的内核APC挂起状态为FALSE
  • 判断特殊APC是否被禁用,被禁用就返回
  • 内核APC处理:内核APC链表不为空则循环
    • 给APC队列上锁
    • 如果链表是空的,就解锁退出循环
    • 再次设置APC状态的APC挂起状态为FALSE
    • 获取链表中下一个内核APC,将其2个回调3个参数保存到局部变量里
    • 判断,如果是特殊内核APC
      • APC摘链,插入标记设置为FALSE
      • 释放APC锁,执行APC Kernel Routinue
    • 如果是普通内核APC
      • 判断当前APC是否在执行中,或在内核APC是否被禁用,如果是则解锁,返回
      • APC摘链,插入标记设置为FALSE
      • 释放APC锁,执行APC Kernel Routinue
      • 将APC执行标记设置为TRUE
      • 调整IRQL等级为PASSIVE_LEVEL调用执行NormalRoutine,再调回IRQL等级
      • 将APC执行标记设置为FALSE
  • 用户APC处理:传递模式为用户模式,用户APC链表不为空,用户APC挂起标志为1则向下进行
    • 给APC队列上锁
    • 如果链表是空的,就解锁退出循环
    • 再次设置APC状态的APC挂起状态为FALSE
    • 获取链表中下一个用户APC,将其2个回调3个参数保存到局部变量里
    • APC摘链,插入标记设置为FALSE
    • 释放APC锁,执行APC Kernel Routinue(在Ring0执行)
    • 判断有没有NormalRoutinue
      • 如果有就调用KiInitializeUserApc去进一步进行初始化执行(在Ring3执行)
      • 如果没有则调用KeTestAlertThread判断有没有更多挂起的用户APC
  • 确认一下线程是否还在同一个进程中,不在就蓝屏
  • 还原TrapFrame到线程里,返回退出

【简洁版流程介绍】用户APC和内核APC处理过程很像:

  • 都是先从APC队列获取一个APC,保存2个回调3个参数到局部变量里
  • 内核APC的话,先执行KernelRoutine,如果有NormalRoutine,就执行完KernelRoutine后执行一下
  • 用户APC的话,先执行KernelRoutine,如果有NormalRoutine,就执行KiInitializeUserApc,如果没有就执行KeTestAlertThread

KiDeliverApc

/*++
 * @name KiDeliverApc
 * @implemented @NT4
 *
 *     The KiDeliverApc routine is called from IRQL switching code if the
 *     thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
 *     pending.
 *
 * @param DeliveryMode
 *        Specifies the current processor mode.
 *
 * @param ExceptionFrame
 *        Pointer to the Exception Frame on non-i386 builds.
 *
 * @param TrapFrame
 *        Pointer to the Trap Frame.
 *
 * @return None.
 *
 * @remarks First, Special APCs are delivered, followed by Kernel-Mode APCs and
 *          User-Mode APCs. Note that the TrapFrame is only valid if the
 *          delivery mode is User-Mode.
 *          Upon entry, this routine executes at APC_LEVEL.
 *
 *--*/
VOID
NTAPI
KiDeliverApc(IN KPROCESSOR_MODE DeliveryMode,
             IN PKEXCEPTION_FRAME ExceptionFrame,
             IN PKTRAP_FRAME TrapFrame)
{
    PKTHREAD Thread = KeGetCurrentThread();
    PKPROCESS Process = Thread->ApcState.Process;
    PKTRAP_FRAME OldTrapFrame;
    PLIST_ENTRY ApcListEntry;
    PKAPC Apc;
    KLOCK_QUEUE_HANDLE ApcLock;
    PKKERNEL_ROUTINE KernelRoutine;
    PVOID NormalContext;
    PKNORMAL_ROUTINE NormalRoutine;
    PVOID SystemArgument1;
    PVOID SystemArgument2;
    ASSERT_IRQL_EQUAL(APC_LEVEL);

    /* Save the old trap frame and set current one */
    // 保存老的TrapFrame到局部变量
    OldTrapFrame = Thread->TrapFrame;
    // 用参数的TrapFrame设置线程的TrapFrame
    Thread->TrapFrame = TrapFrame;

    /* Clear Kernel APC Pending */
    // 清空线程结构中APC状态的内核APC执行标记
    Thread->ApcState.KernelApcPending = FALSE;

    // 判断特殊APC是否被禁用,禁用就跳转
    /* Check if Special APCs are disabled */
    if (Thread->SpecialApcDisable) goto Quickie;

    /* Do the Kernel APCs first */
    // 先处理内核APC
    // 内核APC链表不为空就循环遍历每一个内核APC
    while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
    {
        /* Lock the APC Queue */
        // 锁定APC队列
        KiAcquireApcLockAtApcLevel(Thread, &ApcLock);

        // 检查内核APC链表现在是否变空了
        /* Check if the list became empty now */
        if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
        {
            /* It is, release the lock and break out */
            // 释放锁,退出循环
            KiReleaseApcLock(&ApcLock);
            break;
        }

        // 清空执行标记
        /* Kernel APC is not pending anymore */
        Thread->ApcState.KernelApcPending = FALSE;

        // 获取下一个内核APC结构
        /* Get the next Entry */
        ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
        Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);

        /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
        // 参数保存APC的2个回调,3个参数
        NormalRoutine = Apc->NormalRoutine;
        KernelRoutine = Apc->KernelRoutine;
        NormalContext = Apc->NormalContext;
        SystemArgument1 = Apc->SystemArgument1;
        SystemArgument2 = Apc->SystemArgument2;

        // 特殊APC
        /* Special APC */
        if (!NormalRoutine) // 没有NormalContext表示是特殊APC
        {
            // 把当前获取到的APC移除链表
            /* Remove the APC from the list */
            RemoveEntryList(ApcListEntry);
            // Apc插入标志设置为False
            Apc->Inserted = FALSE;

            /* Release the APC lock */
            // 释放APC锁
            KiReleaseApcLock(&ApcLock);

            // 执行特殊APC
            /* Call the Special APC */
            KernelRoutine(Apc,
                          &NormalRoutine,
                          &NormalContext,
                          &SystemArgument1,
                          &SystemArgument2);

            // 判断IRQL,确保正确返回
            /* Make sure it returned correctly */
            if (KeGetCurrentIrql() != ApcLock.OldIrql)
            {
                KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
                             (KeGetCurrentIrql() << 16) |
                             (ApcLock.OldIrql << 8),
                             (ULONG_PTR)KernelRoutine,
                             (ULONG_PTR)Apc,
                             (ULONG_PTR)NormalRoutine);
            }
        }
        else
        {
            // 普通APC,判断是否符合内核APC执行状态
            /* Normal Kernel APC, make sure it's safe to deliver */
            if ((Thread->ApcState.KernelApcInProgress) ||   // 判断当前内核APC是否正在执行中
                (Thread->KernelApcDisable))                 // 或线程里的内核APC禁用标志被设置了
            {
                // 解放锁并返回
                /* Release lock and return */
                KiReleaseApcLock(&ApcLock);
                goto Quickie;
            }

            /* Dequeue the APC */
            // 移除APC链表,清空标记
            // APC断链,设置插入标记为False
            RemoveEntryList(ApcListEntry);
            Apc->Inserted = FALSE;

            // 解锁
            /* Go back to APC_LEVEL */
            KiReleaseApcLock(&ApcLock);
            // 执行内核APC
            /* Call the Kernel APC */
            KernelRoutine(Apc,
                          &NormalRoutine,
                          &NormalContext,
                          &SystemArgument1,
                          &SystemArgument2);

            /* Make sure it returned correctly */
            // 判断IRQL等级和之前是否一致
            if (KeGetCurrentIrql() != ApcLock.OldIrql)
            {
                KeBugCheckEx(IRQL_UNEXPECTED_VALUE,
                             (KeGetCurrentIrql() << 16) |
                             (ApcLock.OldIrql << 8),
                             (ULONG_PTR)KernelRoutine,
                             (ULONG_PTR)Apc,
                             (ULONG_PTR)NormalRoutine);
            }

            /* Check if there still is a Normal Routine */
            // 确保依然是普通内核APC
            if (NormalRoutine)
            {
                /* At Passive Level, an APC can be prempted by a Special APC */
                // 在Passive_LEVEL,APC能被特殊APC执行
                //设置IRQL等级,然后再调回来
                // 设置APC执行标记为true
                Thread->ApcState.KernelApcInProgress = TRUE;
                KeLowerIrql(PASSIVE_LEVEL);

                /* Call and Raise IRQL back to APC_LEVEL */
                // 调用执行APC
                NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
                KeRaiseIrql(APC_LEVEL, &ApcLock.OldIrql);
            }

            /* Set Kernel APC in progress to false and loop again */
            // 设置APC执行标记为false
            Thread->ApcState.KernelApcInProgress = FALSE;
        }
    }

    // 用户APC
    /* Now we do the User APCs */
    // 传递模式为用户模式,用户APC链表不为空,用户APC执行状态为true,挂起
    if ((DeliveryMode == UserMode) &&
        !(IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
         (Thread->ApcState.UserApcPending))
    {
        /* Lock the APC Queue */
        // APC队列上锁
        KiAcquireApcLockAtApcLevel(Thread, &ApcLock);

        /* It's not pending anymore */
        // 设置挂起标志为FALSE
        Thread->ApcState.UserApcPending = FALSE;

        // 判断链表现在是不是空的
        /* Check if the list became empty now */
        if (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]))
        {
            // 解锁,然后跳出
            /* It is, release the lock and break out */
            KiReleaseApcLock(&ApcLock);
            goto Quickie;
        }

        /* Get the actual APC object */
        // 获取一个用户APC
        ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
        Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);

        // 给参数保存2个回调,3个参数
        /* Save Parameters so that it's safe to free the Object in the Kernel Routine*/
        NormalRoutine = Apc->NormalRoutine;
        KernelRoutine = Apc->KernelRoutine;
        NormalContext = Apc->NormalContext;
        SystemArgument1 = Apc->SystemArgument1;
        SystemArgument2 = Apc->SystemArgument2;

        /* Remove the APC from Queue, and release the lock */
        // 移除链表,设置插入标记为FALSE,解锁
        RemoveEntryList(ApcListEntry);
        Apc->Inserted = FALSE;
        KiReleaseApcLock(&ApcLock);

        /* Call the kernel routine */
        // 执行APC
        KernelRoutine(Apc,
                      &NormalRoutine,
                      &NormalContext,
                      &SystemArgument1,
                      &SystemArgument2);

        /* Check if there's no normal routine */
        // 用户模式没有特殊APC
        if (!NormalRoutine)// 如果没有NormalRoutine
        {
            /* Check if more User APCs are Pending */
            // 判断有没有更多用户APC
            KeTestAlertThread(UserMode);
        }
        else    // 如果有NormalRoutine
        {
            /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
            // 设置TrapFrame并准备在ntdll.dll中执行
            KiInitializeUserApc(ExceptionFrame,
                                TrapFrame,
                                NormalRoutine,
                                NormalContext,
                                SystemArgument1,
                                SystemArgument2);
        }
    }

Quickie:
    /* Make sure we're still in the same process */
    // 判断是不是属于同一个进程,不相等就蓝屏
    if (Process != Thread->ApcState.Process)
    {
        /* Erm, we got attached or something! BAD! */
        KeBugCheckEx(INVALID_PROCESS_ATTACH_ATTEMPT,
                     (ULONG_PTR)Process,
                     (ULONG_PTR)Thread->ApcState.Process,
                     Thread->ApcStateIndex,
                     KeGetCurrentPrcb()->DpcRoutineActive);
    }

    /* Restore the trap frame */
    // 还原TrapFrame
    Thread->TrapFrame = OldTrapFrame;
}

评论