APC异步过程调用
+ -

SavedApcState与线程挂靠

2023-08-02 23 0

_KTHREAD线程0x258地址处:

   +0x258 SavedApcState    : _KAPC_STATE
   +0x258 SavedApcStateFill : [43] UChar

此为备份SavedApcState,用于当线程挂靠到别的进程时,保存当前进程的APC State。
所以:

  • 当线程未挂靠时,ApcState使用当前线程的APCState,不使用SavedApcState。
  • 当线程挂靠到别的进程时,ApcState表示的是挂靠后进程的APCState,而SavedApcState是原进程的APCState。

也就是主ApcState永远是当前使用的,而SavedApcState只是对ApcState特殊状态下的一个备份。

当线程未挂靠时,ApcStateIndex为0。
当线程挂靠时,ApcStateIndex为1。

KeAttachProcess

当一个线程调用KeAttachProcess,在另外的进程上下文中执行后续的代码时,ApcState域的内容就被拷贝到SavedApcState域。然后ApcState域被清空,它的APC队列重新初始化,控制变量设置为0,当前进程域设置为新的进程。这些步骤成功的确保先前在线程所属的进程上下文地址空间中等待的APCs,当线程运行在其它不同的进程上下文时,这些APCs不被传送执行。随后,ApcStatePointer域数组内容被更新来反映新的状态,数组中第一个元素指向SavedApcState域,第二个元素指向ApcState域,表明线程所属进程上下文的APC环境位于SavedApcState域。线程的新的进程上下文的APC环境位于ApcState域。最后,当前进程上下文切换到新的进程上下文。

对于一个APC对象,决定当前APC环境的是ApcStateIndex域。ApcStateIndex域的值作为ApcStatePointer域数组的索引来得到目标APC环境指针。随后,目标APC环境指针用来在相应的队列中存放apc对象.

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;                                     //表示现在的状态指向 指向挂靠状态
}

注意已经Attach的进程再次Attach会BSOD

KeDetachProcess

当线程从新的进程中脱离时(KeDetachProcess), 任何在新的进程地址空间中等待执行的未决的内核APCs被派发执行。随后SavedApcState 域的内容被拷贝回ApcState域。SavedApcState 域的内容被清空,线程的ApcStateIndex域被设为OriginalApcEnvironment,ApcStatePointer域更新,当前进程上下文切换到线程所属进程。

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这样设计是巧妙的
//

0 篇笔记 写笔记

IRP完成APC执行函数IopCompleteRequest
IRP在完成时调用IoCompleteRequest,其最终会执行一个APC调用,该调用的函数名为IopCompleteRequest。其调用APC调用时的代码如下:KeInitializeApc(&Irp->Tail.Apc, &......
APC本质
APC全称Asynchronous Procedure Call,中文名异步过程调用。程序是以进程为载体的,其执行体是进程中的线程。一个线程在运行的过程中,因为其占有的CPU,其它进程或者线程是无法占用CPU的,所以从理论上来讲,这个线程是无法被杀死,挂起和恢复的。但在实际的软件开发中,以上的操作......
APC挂入
挂入ApcListHead链表中的叫做APC,每个APC的结构如下:2: kd> dt _KAPCnt!_KAPC +0x000 Type : UChar +0x001 SpareByte0 : UChar +0x002 Size ......
APC的执行及执行时机
当线程中的ApcListHead链表中不为空时,就表示该线程拥有APC,线程应在合适的时机执行该APC。每个线程都有自己的 APC 队列。如果线程进入警报状态,它将开始以先进先出 (FIFO) 的形式执行 APC 作业。线程可以通过使用SleepEx、SignalObjectAndWait、Msg......
SavedApcState与线程挂靠
_KTHREAD线程0x258地址处: +0x258 SavedApcState : _KAPC_STATE +0x258 SavedApcStateFill : [43] UChar此为备份SavedApcState,用于当线程挂靠到别的进程时,保存当前进程的APC State......
PsExitSpecialApc线程的退出示例
在上一节KiInsertQueueApc中,可以看到有一个特殊的APC,其为PsExitSpecialApc,表示线程退出的APC. if (Apc->NormalRoutine) { /* Normal APC; is it the Thread Termin......
APC的执行KiDeliverApc
APC执行是通过KiDeliverApc实现的,不过该函数是一个内核函数。不过APC分为内核APC和应用层APC。对于内核层的APC直接调用即可,但对于应用层的APC,需要临时进入用户层,执行用户层的APC,执行完成后再进入内核层,再通过内核层返回到应用层原来的返回地址。ReactOS关于其代码如下......
线程ETHREAD
每个线程在内核都有一个对应的结构体:ETHREAD,ETHREAD的第一个成员为KTHREAD.1: kd> dt _ETHREADnt!_ETHREAD +0x000 Tcb : _KTHREAD +0x5e0 CreateTime : ......
NtTestAlert主动触发APC调用
线程只有在进入alertable状态时才能运行 APC 作业。那是否有不用alertable状态运行 APC 作业的方法。还真有一个就是NtTestAlert函数,它检查当前线程的 APC 队列,如果有任何排队的作业,它会运行它们以清空队列。当一个线程启动时,NtTestAlert会被首先调用在执行......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!