APC的内部实现

一个经典的投放apc结束线程是这样的


   KeInitializeApc(exitAPC,ethread,OriginalApcEnvironment,KernelKillThreadRoutine,0,0,KernelMode,0);
   KeInsertQueueApc(exitAPC,exitAPC,NULL,2);

1.APC结构

typedef struct _KAPC {
    UCHAR Type;
    UCHAR SpareByte0;
    UCHAR Size;
    UCHAR SpareByte1;
    ULONG SpareLong0;
    struct _KTHREAD *Thread;
    LIST_ENTRY ApcListEntry;   //   插入线程APC链表
    PKKERNEL_ROUTINE KernelRoutine;   内核模式中执行
    PKRUNDOWN_ROUTINE RundownRoutine; //线程终止时还有APC没执行会调用这个函数?
PKNORMAL_ROUTINE NormalRoutine;  // 这个为0 表示是一个特殊内核APC,否则是一个普通的(又分为内核态的和用户态的)。特殊的位于链表前部,普通的位于后部。 普通的APC,normal和kernel例程都将被调用
         
    PVOID NormalContext;

    PVOID SystemArgument1;
    PVOID SystemArgument2;
    CCHAR ApcStateIndex;     //  APC环境状态
    KPROCESSOR_MODE ApcMode;     //内核态or用户态
    BOOLEAN Inserted;
} KAPC, *PKAPC, *PRKAPC;

2.关于APC的环境

这个枚举指的是指示线程attach之后 再回来的情形

typedef enum _KAPC_ENVIRONMENT {
    OriginalApcEnvironment,   //原始的进程环境
    AttachedApcEnvironment, //挂靠后的进程环境
    CurrentApcEnvironment,  // 当前环境
    InsertApcEnvironment //  被插入时的环境
} KAPC_ENVIRONMENT;
typedef struct _KAPC_STATE {
    LIST_ENTRY ApcListHead[MaximumMode];//线程的apc链表 只有两个 内核态和用户态
    struct _KPROCESS *Process;    // 线程 挂靠的进程   
    BOOLEAN KernelApcInProgress;   // 线程正在处理APC对象
    BOOLEAN KernelApcPending;   // 线程有内核apc等待交付
    BOOLEAN UserApcPending; //    有用户态的等待
} KAPC_STATE, *PKAPC_STATE, *PRKAPC_STATE;
KTHREAD 中的KAPC_STATE 中有两个 ApcState ,
ApcState和SaveApcState,

ApcStatePointer是个数组,ApcStateIndex是下标

KiAttachProcess中//额 忘说了 已经attach的进程再次attach会BSOD...

    KiMoveApcState(&Thread->ApcState, SavedApcState); //当前的APC状态移到Save里,然后初始化apc状态
    InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]); //ApcState被初始化
    InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
    Thread->ApcState.KernelApcInProgress = FALSE;
    Thread->ApcState.KernelApcPending = FALSE;
    Thread->ApcState.UserApcPending = FALSE;
    if (SavedApcState == &Thread->SavedApcState) {
        Thread->ApcStatePointer[0] = &Thread->SavedApcState;          //第一个指向保存的apc状态 原始apc环境
        Thread->ApcStatePointer[1] = &Thread->ApcState;                //第二个是当前的 挂靠apc环境
        Thread->ApcStateIndex = 1;                                     //表示现在的状态指向 指向挂靠状态
    }



Dettach时,先派发APC state里面的APC,然后再恢复    //也就是在挂靠过程中线程被插apc 现在要集中解决

       while (Thread->ApcState.KernelApcPending &&
               (Thread->SpecialApcDisable == 0) &&
               (LockHandle.OldIrql < APC_LEVEL)) {        

            //
            // Unlock the thread APC lock and lower IRQL to its previous
            // value. An APC interrupt will immediately occur which will
            // result in the delivery of the kernel APC if possible.
            //释放这个锁将导致 请求APC级别的中断,这样apc将得到释放

            KeReleaseInStackQueuedSpinLock(&LockHandle);
            KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock,
                                                       &LockHandle);
        }

省略无关代码,到这里进行恢复
        KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState); //恢复了
        Thread->SavedApcState.Process = (PKPROCESS)NULL;
        Thread->ApcStatePointer[0] = &Thread->ApcState;
        Thread->ApcStatePointer[1] = &Thread->SavedApcState;
        Thread->ApcStateIndex = 0;

ApcStatePointer这样设计是巧妙的

在KiInsertQueueApc中直接用下标来找当前的环境值,(那是否ApcStateIndex = 0是代表线程在自己的环境中,1在别人的环境中??)

ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex];

所以如果对一个插入别人的线程插入APC,具体插入那个环境,是由APC的环境变量决定的。

0是原本的环境,1是挂靠环境。 如果目标线程挂靠了,线程原本环境是SavedApcState,挂靠环境是ApcState;没有挂靠 原本环境是ApcState,挂靠环境还是ApcState
3.APC的实现过程

VOID
KeInitializeApc

    if (Environment == CurrentApcEnvironment) { //当前环境,那Index就是线程的
        Apc->ApcStateIndex = Thread->ApcStateIndex;

    } else {

        ASSERT((Environment <= Thread->ApcStateIndex) || (Environment == InsertApcEnvironment));

        Apc->ApcStateIndex = (CCHAR)Environment;
}
KeInsertQueueApc

    Thread = Apc->Thread;
    KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle);
//升到synch_level获取apc锁

    if ((Thread->ApcQueueable == FALSE) || //线程退出时不接受APC
        (Apc->Inserted == TRUE)) {
        Inserted = FALSE;

    } else {
        Apc->Inserted = TRUE;
        Apc->SystemArgument1 = SystemArgument1;
        Apc->SystemArgument2 = SystemArgument2;
        KiInsertQueueApc(Apc, Increment);
        Inserted = TRUE;
    }

    //
    // Unlock the thread APC queue lock, exit the scheduler, and return
    // whether the APC was inserted.
    //

    KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
    KiExitDispatcher(LockHandle.OldIrql);

KiInsertQueueApc
{
………….
   Thread = Apc->Thread;
    if (Apc->ApcStateIndex == InsertApcEnvironment) { //被插入线程的环境,这里面赋值
        Apc->ApcStateIndex = Thread->ApcStateIndex;
    }

    ApcState = Thread->ApcStatePointer[Apc->ApcStateIndex];

    ApcMode = Apc->ApcMode;

    ASSERT (Apc->Inserted == TRUE);

    if (Apc->NormalRoutine != NULL) {
        if ((ApcMode != KernelMode) && (Apc->KernelRoutine == PsExitSpecialApc)) {//用户模式
            Thread->ApcState.UserApcPending = TRUE;
            InsertHeadList(&ApcState->ApcListHead[ApcMode],
                           &Apc->ApcListEntry);

        } else {//普通内核模式 插入尾部
            InsertTailList(&ApcState->ApcListHead[ApcMode],
                           &Apc->ApcListEntry);
        }

    } else {//特殊内核模式 找到最后一个特殊APC 插入 始终保持特殊APC在普通的前面,又要保证插入是按照时间顺序的。
        ListEntry = ApcState->ApcListHead[ApcMode].Blink;
        while (ListEntry != &ApcState->ApcListHead[ApcMode]) {
            ApcEntry = CONTAINING_RECORD(ListEntry, KAPC, ApcListEntry);
            if (ApcEntry->NormalRoutine == NULL) {
                break;
            }

            ListEntry = ListEntry->Blink;
        }

        InsertHeadList(ListEntry, &Apc->ApcListEntry);
    }

    if (Apc->ApcStateIndex == Thread->ApcStateIndex) {
   //是线程有的状态环境 0 or 1 原始或者挂靠的环境,另外两种状态已经在之前解决掉了 现在只有这两种。并且当现在状态相同,可以尝试让apc立即执行起来

        //
        // If the target thread is the current thread, then the thread state
        // is running and cannot change.
        //

        if (Thread == KeGetCurrentThread()) {//插入的就是当前线程

            ASSERT(Thread->State == Running);

            //
            // If the APC mode is kernel, then set kernel APC pending and
            // request an APC interrupt if special APC's are not disabled.
            //

            if (ApcMode == KernelMode) {//内核态apc 直接请求apc中断
                Thread->ApcState.KernelApcPending = TRUE;
                if (Thread->SpecialApcDisable == 0) {
                    KiRequestSoftwareInterrupt(APC_LEVEL);
                }
            }

            return;
        }

        RequestInterrupt = FALSE;
        KiLockDispatcherDatabaseAtSynchLevel();
        if (ApcMode == KernelMode) {

            Thread->ApcState.KernelApcPending = TRUE;
            KeMemoryBarrier();
            ThreadState = Thread->State;
            if (ThreadState == Running) {//线程正在运行 请求中断
                RequestInterrupt = TRUE;

            } else if ((ThreadState == Waiting) && //线程处于等待状态,唤醒这个线程 在KiUnwaitThread调用 KiReadyThread 这里面会交付APC
                       (Thread->WaitIrql == 0) &&
                       (Thread->SpecialApcDisable == 0) &&
                       ((Apc->NormalRoutine == NULL) ||
                        ((Thread->KernelApcDisable == 0) &&
                         (Thread->ApcState.KernelApcInProgress == FALSE)))) {

                KiUnwaitThread(Thread, STATUS_KERNEL_APC, Increment);

            } else if (Thread->State == GateWait) { //门等待 从门等待中拆出来 直接插入备用链表
                KiAcquireThreadLock(Thread);
                if ((Thread->State == GateWait) &&
                    (Thread->WaitIrql == 0) &&
                    (Thread->SpecialApcDisable == 0) &&
                    ((Apc->NormalRoutine == NULL) ||
                     ((Thread->KernelApcDisable == 0) &&
                      (Thread->ApcState.KernelApcInProgress == FALSE)))) {

                    GateObject = Thread->GateObject;
                    KiAcquireKobjectLock(GateObject);
                    RemoveEntryList(&Thread->WaitBlock[0].WaitListEntry);
                    KiReleaseKobjectLock(GateObject);
                    if ((Queue = Thread->Queue) != NULL) {
                        Queue->CurrentCount += 1;
                    }

                    Thread->WaitStatus = STATUS_KERNEL_APC;
                    KiInsertDeferredReadyList(Thread);
                }

                KiReleaseThreadLock(Thread);
            }

        } else if ((Thread->State == Waiting) &&
                  (Thread->WaitMode == UserMode) &&
                  (Thread->Alertable || Thread->ApcState.UserApcPending)) {//用户模式 正在等待 并且可以唤醒 调用 KiUnwaitThread

            Thread->ApcState.UserApcPending = TRUE;
            KiUnwaitThread(Thread, STATUS_USER_APC, Increment);
        }
   //其他的情况只能等待其他机会执行APC了
        //
        // Unlock the dispatcher database and request an APC interrupt if
        // required.
        //
   //如果有请求中断 这里执行一个
        KiUnlockDispatcherDatabaseFromSynchLevel();
        if (RequestInterrupt == TRUE) {
            KiRequestApcInterrupt(Thread->NextProcessor);
        }
    }

    return;
}

4.APC的执行过程

APC_LEVEL的中断或者中断从更高级别降下来时,apc得到机会交付。KiDeliverApc分三种不同情况,做处理,逻辑比较简单

VOID
KiDeliverApc (
    IN KPROCESSOR_MODE PreviousMode,
    IN PKEXCEPTION_FRAME ExceptionFrame,
    IN PKTRAP_FRAME TrapFrame
    )
{

    if (TrapFrame != NULL) {
        KiCheckForSListAddress(TrapFrame);
    }

    Thread = KeGetCurrentThread();//更换陷阱帧
    OldTrapFrame = Thread->TrapFrame;
    Thread->TrapFrame = TrapFrame;

    Process = Thread->ApcState.Process;
    Thread->ApcState.KernelApcPending = FALSE;
    if (Thread->SpecialApcDisable == 0) {

        KeMemoryBarrier();
        while (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]) == FALSE) {
    //只处理当前线程当前状态环境下的dpc

            KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);
            NextEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
            if (NextEntry == &Thread->ApcState.ApcListHead[KernelMode]) {//到这已经空了释放并退出
                KeReleaseInStackQueuedSpinLock(&LockHandle);
                break;
            }

            //
            // Clear kernel APC pending, get the address of the APC object,
            // and determine the type of APC.
            //
            // N.B. Kernel APC pending must be cleared each time the kernel
            //      APC queue is found to be non-empty.
            //

            Thread->ApcState.KernelApcPending = FALSE;
            Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
            ReadForWriteAccess(Apc);
            KernelRoutine = Apc->KernelRoutine;
            NormalRoutine = Apc->NormalRoutine;
            NormalContext = Apc->NormalContext;
            SystemArgument1 = Apc->SystemArgument1;
            SystemArgument2 = Apc->SystemArgument2;
            if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {//特殊的内核历程
   
     //调用apc
                RemoveEntryList(NextEntry);
                Apc->Inserted = FALSE;
                KeReleaseInStackQueuedSpinLock(&LockHandle);
                (KernelRoutine)(Apc,
                                &NormalRoutine,
                                &NormalContext,
                                &SystemArgument1,
                                &SystemArgument2);
  

            } else {//普通的内核apc
   
   
                if ((Thread->ApcState.KernelApcInProgress == FALSE) &&
                   (Thread->KernelApcDisable == 0)) {

                    RemoveEntryList(NextEntry);
                    Apc->Inserted = FALSE;
                    KeReleaseInStackQueuedSpinLock(&LockHandle);
                    (KernelRoutine)(Apc,     //普通的apc 比如nt!KiSuspendThread
                                    &NormalRoutine,   //NormalRoutine可能会在这里被改成0,如果没改成0继续执行
                                    &NormalContext,
                                    &SystemArgument1,
                                    &SystemArgument2);

     //还要调用Normal
                    if (NormalRoutine != (PKNORMAL_ROUTINE)NULL) {   //比如这可能是是nt!KiSuspendNop 啥也不干直接返回
                        Thread->ApcState.KernelApcInProgress = TRUE; //在这个apc执行的时候 其他的普通apc不会被交付
                        KeLowerIrql(0);          //这里normal和kernel的irql也不一样,normal在passive运行
                        (NormalRoutine)(NormalContext,   
                                        SystemArgument1,
                                        SystemArgument2);
   
                        KeRaiseIrql(APC_LEVEL, &LockHandle.OldIrql);
                    }
   
                    Thread->ApcState.KernelApcInProgress = FALSE;
   
                } else {
                    KeReleaseInStackQueuedSpinLock(&LockHandle);
                    goto CheckProcess;
                }
            }
        }

   
        if ((PreviousMode == UserMode) &&
            (IsListEmpty(&Thread->ApcState.ApcListHead[UserMode]) == FALSE) &&
            (Thread->ApcState.UserApcPending != FALSE)) {

            KeAcquireInStackQueuedSpinLock(&Thread->ApcQueueLock, &LockHandle);

            Thread->ApcState.UserApcPending = FALSE;
            NextEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
            if (NextEntry == &Thread->ApcState.ApcListHead[UserMode]) {
                KeReleaseInStackQueuedSpinLock(&LockHandle);
                goto CheckProcess;
            }

            Apc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
            ReadForWriteAccess(Apc);
            KernelRoutine = Apc->KernelRoutine;
            NormalRoutine = Apc->NormalRoutine;
            NormalContext = Apc->NormalContext;
            SystemArgument1 = Apc->SystemArgument1;
            SystemArgument2 = Apc->SystemArgument2;
            RemoveEntryList(NextEntry);
            Apc->Inserted = FALSE;
            KeReleaseInStackQueuedSpinLock(&LockHandle);
            (KernelRoutine)(Apc,      //用户态时这里很常见的是nt!IopDeallocateApc
                            &NormalRoutine,  
                            &NormalContext,
                            &SystemArgument1,
                            &SystemArgument2);
   
            if (NormalRoutine == (PKNORMAL_ROUTINE)NULL) {
                KeTestAlertThread(UserMode);
   
            } else {//初始化一个用户态APC,这个函数把一个KAPC_RECORD放入栈中,TrapFrame->Eip = (ULONG)KeUserApcDispatcher; 这是一个ntdll中的函数,
      //陷阱帧的东西还不太明白,不能说太细。。以后补充~~
                KiInitializeUserApc(ExceptionFrame,
                                    TrapFrame,
                                    NormalRoutine,
                                    NormalContext,
                                    SystemArgument1,
                                    SystemArgument2);
            }
        }
    }

5.APC的调用时机

★.内核代码离开临界区或守护区,调用KiCheckForKernelApcDelivery或者请求APC级别中断

★.KiSwapThread返回以前调用一次KiDeliverApc

★.从系统服务或者异常返回时 调用KiDeliverApc 设置用户态apc的交付

★.APC_LEVEL的中断和irql降到passive时KiDeliverApc都会被调用




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值