备用APC队列
Kd>dt _KTHREAD
Nt!_KTHREAD
+0x034 ApcState :_KAPC_STATE
+0x138 ApcStatePointer:[2]Ptr32_KAPC_STATE
+0x14c SavedApcState :_KAPC_STATE
+0x165 ApcStateIndex :UChar
+0x166 ApcQueueable :UChar
..
1.SavedApcState的意义:
A进程中的T线程中的所有APC函数,要访问的内存地址都是A进程的;
但是线程可以挂靠到其他进程,通过修改Cr3(该位B进程的页目录基址),就可以访问B进程地址空间,就是进程挂靠;
当T线程挂靠到B进程时,现在的APC队列任然是原来的APC,现在读取到的是B进程的地址空间;
为了避免麻烦,但T线程挂靠到B进程时,会将ApcState中的值暂时存储到SavedApcState中,等到放回A进程时,在恢复APC队列;
2.挂靠环境下ApcState的意义:在挂靠环境下插入Apc线程,插入位置还是ApcState,因为原来的APC已经备份到SavedApcState里了
3. ApcStatePoirter
这个函数是一个指针数组,长度为2;为了寻址方便;
正常情况下:
ApcStatePoirter[0]ApcState
ApcStatePoirter[1]SavedApcState
挂靠情况下:
ApcStatePoirter[0] SavedApcState
ApcStatePoirter [1] ApcState
4. ApcStateInder
用来标识当前线程处于什么状态
0 正常状态 1 挂靠状态
5.ApcStatePoired和ApcStateInder组合寻址
正常情况下,向ApcState里插入Apc
ApcStatePointer[0]ApcState ApcStateInder=0
挂靠情况下,向ApcState插入Apc
ApcStatePointer [1]ApcState ApcStateInder=1
总结
无论什么环境下,ApcStatePointer[ApcStateInder]都指向ApcState,ApcState则总表示线程当前使用的apc状态
6. ApcQueueable
用于表示是否可以向线程的APC队列插入APC
如果当前线程正在执行退出的代码时,会将这个值设置为0;如果执行插入APC代码(KeInserQueueApc),如果值为0,则插入失败;
Alertable属性说明(是否运行被APC吵醒)
kd>dt _KTHREAD
ntdll!_KTHREAD
+0x164 Alertable :UCher
当调用普通的SleepEx或者WaitForSingleObjectEx时,是不会修改Alertable的; 只有当Alertable是1时,才能使UserApcPenging=1,在唤醒APC;
那什么时候才会是1呢?
是只有当时用户层导致的等待状态
内核APC
执行点:1,线程切换时; 2,系统调用,中断,或者异常时由0环返回3环时
注2:只有存在用户APC要执行时,才能执行内核APC;
当要执行用户APC时,要先执行内核APC;
KiDeliverApc函数
无论要执行用户APC函数内核APC,都会执行这个函数;
执行流程:
总结:内核APC在线程切换时就会执行,只要插入内核APC就会立即执行;
在执行用户APC之前会先执行内核APC;
内核APC在内核空间执行,不需要换栈;