SavedApcState与线程挂靠
_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这样设计是巧妙的
//