APC本质
APC全称Asynchronous Procedure Call,中文名异步过程调用。
程序是以进程为载体的,其执行体是进程中的线程。一个线程在运行的过程中,因为其占有的CPU,其它进程或者线程是无法占用CPU的,所以从理论上来讲,这个线程是无法被杀死,挂起和恢复的。但在实际的软件开发中,以上的操作都是可以实现,这是因为操作系统被设计为多任务,在普通线程的运行之上有一个管理者,所有普通的线程都在这个管理者的监督下运行的。
前面所说了,一个线程是占有CPU的,那么它是怎么受到监督的。这就要从线程的运行机制说起。
线程在运行的过程中,实际上不仅要执行其线程内的代码,也会在某种时机下运行一些不属于线程正常的代码。而这种代码就表现的线程的动作或者状态控制。如一个线程要挂起,要被杀死,都是通过执行这种代码而实现的的自己挂起或者自杀。但通过外在来看,线程受到了我们的控制。
上面所说的不属于线程正常的代码,就叫APC.
APC以函数的形式提供。外在的管理者线程或者其它线程如果想要改变该线程的行为,就需要提供APC函数。如果该线程在运行过程中发现了APC函数,那么就执行它。
我们知道在内核中,线程的数据结构_KTHREAD
如下:
本人的电脑为windows 10,版本为1607
线程与APC队列
APC是基于线程的异步调用,所以可以用来执行处于某特定线程环境才能做的操作。
例如,文件IO操作,某个线程正在读取一个文件,此时可以使用APC向特定线程插入一个中断APC,IRQL=1,在这个APC中可以完成已经读写成功的操作。
我们通过Windbg查看该结构体
2: kd> .symfix
2: kd> dt _KTHREAD
nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x018 SListFaultAddress : Ptr64 Void
+0x020 QuantumTarget : Uint8B
+0x028 InitialStack : Ptr64 Void
+0x030 StackLimit : Ptr64 Void
+0x038 StackBase : Ptr64 Void
+0x040 ThreadLock : Uint8B
+0x048 CycleTime : Uint8B
+0x050 CurrentRunTime : Uint4B
+0x054 ExpectedRunTime : Uint4B
+0x058 KernelStack : Ptr64 Void
+0x060 StateSaveArea : Ptr64 _XSAVE_FORMAT
+0x068 SchedulingGroup : Ptr64 _KSCHEDULING_GROUP
+0x070 WaitRegister : _KWAIT_STATUS_REGISTER
+0x071 Running : UChar
+0x072 Alerted : [2] UChar
+0x074 AutoBoostActive : Pos 0, 1 Bit
+0x074 ReadyTransition : Pos 1, 1 Bit
+0x074 WaitNext : Pos 2, 1 Bit
+0x074 SystemAffinityActive : Pos 3, 1 Bit
+0x074 Alertable : Pos 4, 1 Bit
+0x074 UserStackWalkActive : Pos 5, 1 Bit
+0x074 ApcInterruptRequest : Pos 6, 1 Bit
+0x074 QuantumEndMigrate : Pos 7, 1 Bit
+0x074 UmsDirectedSwitchEnable : Pos 8, 1 Bit
+0x074 TimerActive : Pos 9, 1 Bit
+0x074 SystemThread : Pos 10, 1 Bit
+0x074 ProcessDetachActive : Pos 11, 1 Bit
+0x074 CalloutActive : Pos 12, 1 Bit
+0x074 ScbReadyQueue : Pos 13, 1 Bit
+0x074 ApcQueueable : Pos 14, 1 Bit
+0x074 ReservedStackInUse : Pos 15, 1 Bit
+0x074 UmsPerformingSyscall : Pos 16, 1 Bit
+0x074 TimerSuspended : Pos 17, 1 Bit
+0x074 SuspendedWaitMode : Pos 18, 1 Bit
+0x074 SuspendSchedulerApcWait : Pos 19, 1 Bit
+0x074 Reserved : Pos 20, 12 Bits
+0x074 MiscFlags : Int4B
+0x078 AutoAlignment : Pos 0, 1 Bit
+0x078 DisableBoost : Pos 1, 1 Bit
+0x078 ThreadFlagsSpare0 : Pos 2, 1 Bit
+0x078 AlertedByThreadId : Pos 3, 1 Bit
+0x078 QuantumDonation : Pos 4, 1 Bit
+0x078 EnableStackSwap : Pos 5, 1 Bit
+0x078 GuiThread : Pos 6, 1 Bit
+0x078 DisableQuantum : Pos 7, 1 Bit
+0x078 ChargeOnlySchedulingGroup : Pos 8, 1 Bit
+0x078 DeferPreemption : Pos 9, 1 Bit
+0x078 QueueDeferPreemption : Pos 10, 1 Bit
+0x078 ForceDeferSchedule : Pos 11, 1 Bit
+0x078 SharedReadyQueueAffinity : Pos 12, 1 Bit
+0x078 FreezeCount : Pos 13, 1 Bit
+0x078 TerminationApcRequest : Pos 14, 1 Bit
+0x078 AutoBoostEntriesExhausted : Pos 15, 1 Bit
+0x078 KernelStackResident : Pos 16, 1 Bit
+0x078 CommitFailTerminateRequest : Pos 17, 1 Bit
+0x078 ProcessStackCountDecremented : Pos 18, 1 Bit
+0x078 RestrictedGuiThread : Pos 19, 1 Bit
+0x078 ThreadFlagsSpare : Pos 20, 4 Bits
+0x078 EtwStackTraceApcInserted : Pos 24, 8 Bits
+0x078 ThreadFlags : Int4B
+0x07c Tag : UChar
+0x07d SystemHeteroCpuPolicy : UChar
+0x07e UserHeteroCpuPolicy : Pos 0, 7 Bits
+0x07e ExplicitSystemHeteroCpuPolicy : Pos 7, 1 Bit
+0x07f Spare0 : UChar
+0x080 SystemCallNumber : Uint4B
+0x084 Spare10 : Uint4B
+0x088 FirstArgument : Ptr64 Void
+0x090 TrapFrame : Ptr64 _KTRAP_FRAME
+0x098 ApcState : _KAPC_STATE
+0x098 ApcStateFill : [43] UChar
+0x0c3 Priority : Char
+0x0c4 UserIdealProcessor : Uint4B
+0x0c8 WaitStatus : Int8B
+0x0d0 WaitBlockList : Ptr64 _KWAIT_BLOCK
+0x0d8 WaitListEntry : _LIST_ENTRY
+0x0d8 SwapListEntry : _SINGLE_LIST_ENTRY
+0x0e8 Queue : Ptr64 _DISPATCHER_HEADER
+0x0f0 Teb : Ptr64 Void
+0x0f8 RelativeTimerBias : Uint8B
+0x100 Timer : _KTIMER
+0x140 WaitBlock : [4] _KWAIT_BLOCK
+0x140 WaitBlockFill4 : [20] UChar
+0x154 ContextSwitches : Uint4B
+0x140 WaitBlockFill5 : [68] UChar
+0x184 State : UChar
+0x185 Spare13 : Char
+0x186 WaitIrql : UChar
+0x187 WaitMode : Char
+0x140 WaitBlockFill6 : [116] UChar
+0x1b4 WaitTime : Uint4B
+0x140 WaitBlockFill7 : [164] UChar
+0x1e4 KernelApcDisable : Int2B
+0x1e6 SpecialApcDisable : Int2B
+0x1e4 CombinedApcDisable : Uint4B
+0x140 WaitBlockFill8 : [40] UChar
+0x168 ThreadCounters : Ptr64 _KTHREAD_COUNTERS
+0x140 WaitBlockFill9 : [88] UChar
+0x198 XStateSave : Ptr64 _XSTATE_SAVE
+0x140 WaitBlockFill10 : [136] UChar
+0x1c8 Win32Thread : Ptr64 Void
+0x140 WaitBlockFill11 : [176] UChar
+0x1f0 Ucb : Ptr64 _UMS_CONTROL_BLOCK
+0x1f8 Uch : Ptr64 _KUMS_CONTEXT_HEADER
+0x200 Spare21 : Ptr64 Void
+0x208 QueueListEntry : _LIST_ENTRY
+0x218 NextProcessor : Uint4B
+0x218 NextProcessorNumber : Pos 0, 31 Bits
+0x218 SharedReadyQueue : Pos 31, 1 Bit
+0x21c QueuePriority : Int4B
+0x220 Process : Ptr64 _KPROCESS
+0x228 UserAffinity : _GROUP_AFFINITY
+0x228 UserAffinityFill : [10] UChar
+0x232 PreviousMode : Char
+0x233 BasePriority : Char
+0x234 PriorityDecrement : Char
+0x234 ForegroundBoost : Pos 0, 4 Bits
+0x234 UnusualBoost : Pos 4, 4 Bits
+0x235 Preempted : UChar
+0x236 AdjustReason : UChar
+0x237 AdjustIncrement : Char
+0x238 AffinityVersion : Uint8B
+0x240 Affinity : _GROUP_AFFINITY
+0x240 AffinityFill : [10] UChar
+0x24a ApcStateIndex : UChar
+0x24b WaitBlockCount : UChar
+0x24c IdealProcessor : Uint4B
+0x250 NpxState : Uint8B
+0x258 SavedApcState : _KAPC_STATE
+0x258 SavedApcStateFill : [43] UChar
+0x283 WaitReason : UChar
+0x284 SuspendCount : Char
+0x285 Saturation : Char
+0x286 SListFaultCount : Uint2B
+0x288 SchedulerApc : _KAPC
+0x288 SchedulerApcFill0 : [1] UChar
+0x289 ResourceIndex : UChar
+0x288 SchedulerApcFill1 : [3] UChar
+0x28b QuantumReset : UChar
+0x288 SchedulerApcFill2 : [4] UChar
+0x28c KernelTime : Uint4B
+0x288 SchedulerApcFill3 : [64] UChar
+0x2c8 WaitPrcb : Ptr64 _KPRCB
+0x288 SchedulerApcFill4 : [72] UChar
+0x2d0 LegoData : Ptr64 Void
+0x288 SchedulerApcFill5 : [83] UChar
+0x2db CallbackNestingLevel : UChar
+0x2dc UserTime : Uint4B
+0x2e0 SuspendEvent : _KEVENT
+0x2f8 ThreadListEntry : _LIST_ENTRY
+0x308 MutantListHead : _LIST_ENTRY
+0x318 AbEntrySummary : UChar
+0x319 AbWaitEntryCount : UChar
+0x31a Spare20 : Uint2B
+0x31c SecureThreadCookie : Uint4B
+0x320 LockEntries : [6] _KLOCK_ENTRY
+0x560 PropagateBoostsEntry : _SINGLE_LIST_ENTRY
+0x568 IoSelfBoostsEntry : _SINGLE_LIST_ENTRY
+0x570 PriorityFloorCounts : [16] UChar
+0x580 PriorityFloorSummary : Uint4B
+0x584 AbCompletedIoBoostCount : Int4B
+0x588 AbCompletedIoQoSBoostCount : Int4B
+0x58c KeReferenceCount : Int2B
+0x58e AbOrphanedEntrySummary : UChar
+0x58f AbOwnedEntryCount : UChar
+0x590 ForegroundLossTime : Uint4B
+0x598 GlobalForegroundListEntry : _LIST_ENTRY
+0x598 ForegroundDpcStackListEntry : _SINGLE_LIST_ENTRY
+0x5a0 InGlobalForegroundList : Uint8B
+0x5a8 ReadOperationCount : Int8B
+0x5b0 WriteOperationCount : Int8B
+0x5b8 OtherOperationCount : Int8B
+0x5c0 ReadTransferCount : Int8B
+0x5c8 WriteTransferCount : Int8B
+0x5d0 OtherTransferCount : Int8B
+0x5d8 QueuedScb : Ptr64 _KSCB
在线构体0x98处有一个成员ApcState,其类型为_KAPC_STATE
2: kd> dt _KAPC_STATE
nt!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY
+0x020 Process : Ptr64 _KPROCESS
+0x028 InProgressFlags : UChar
+0x028 KernelApcInProgress : Pos 0, 1 Bit
+0x028 SpecialApcInProgress : Pos 1, 1 Bit
+0x029 KernelApcPending : UChar
+0x02a UserApcPending : UChar
- ApcListHead : [2] LIST_ENTRY :2个APC队列 用户APC和内核APC
- Process : Ptr64 KPROCESS :线程所属或者所挂靠的进程
- KernelApcInProgress:内核APC是否正在执行
- KernelApcPending:否有正在等待执行的内核APC
- UserApcPending:是否有正在等待执行的用户APC
通过结构体可以看到ApcListHead有2个数组链表,分别用于用户层R3的APC函数队列和用于内核层R0层的APC函数队列。
我们根据代码执行的空间类型,可以提供2种APC队列,分别为应用层和内核层。