APC异步过程调用
+ -

APC本质

2023-08-02 94 0

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队列,分别为应用层和内核层。

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驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

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

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