VOID KeInitializeApc ( IN PKAPC Apc, //KAPC指针(在NtQueueApcThread中分配好空间但是还没有初始化的结构) IN PKTHREAD Thread, //目标线程 IN KAPC_ENVIRONMENT TargetEnvironment, //0 1 2 3四种状态(挂到哪一个APC队列中去) IN PKKERNEL_ROUTINE KernelRoutine, //销毁KAPC的函数地址 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL, IN PKNORMAL_ROUTINE NormalRoutine, //用户APC总入口或者内核apc函数 IN KPROCESSOR_MODE Mode, //要插入用户apc队列还是内核apc队列 IN PVOID Context //内核APC:NULL 用户APC:真正的APC函数 )
NTSYSAPI NTSTATUS NTAPI NtQueueApcThread( IN HANDLE ThreadHandle, IN PPS_APC_ROUTINE ApcRoutine, IN PVOID ApcArgument1, //NormalContext IN PVOID ApcArgument2, IN PVOID ApcArgument3 )
WINBASEAPI DWORD WINAPI QueueUserAPC( PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData ) /*++ Routine Description: This function is used to queue a user-mode APC to the specified thread. The APC will fire when the specified thread does an alertable wait. Arguments: pfnAPC - Supplies the address of the APC routine to execute when the APC fires. hHandle - Supplies a handle to a thread object. The caller must have THREAD_SET_CONTEXT access to the thread. dwData - Supplies a DWORD passed to the APC Return Value: TRUE - The operations was successful FALSE - The operation failed. GetLastError() is not defined. --*/ { NTSTATUS Status; PVOID Argument1 = (PVOID) pfnAPC; PVOID Argument2 = (PVOID) dwData; PVOID Argument3 = NULL; ACTIVATION_CONTEXT_BASIC_INFORMATION acbi = { 0 };
Status = RtlQueryInformationActivationContext( RTL_QUERY_INFORMATION_ACTIVATION_CONTEXT_FLAG_USE_ACTIVE_ACTIVATION_CONTEXT, NULL, 0, ActivationContextBasicInformation, &acbi, sizeof(acbi), NULL); if (!NT_SUCCESS(Status)) { DbgPrint("SXS: %s failing because RtlQueryInformationActivationContext() returned status %08lx\n", __FUNCTION__, Status); return FALSE; }
Argument3 = acbi.ActivationContext;
if (acbi.Flags & ACTIVATION_CONTEXT_FLAG_NO_INHERIT) { // We're not supposed to propogate the activation context; set it to a value to indicate such. Argument3 = INVALID_ACTIVATION_CONTEXT; }
Status = NtQueueApcThread( hThread, &BaseDispatchAPC, Argument1, Argument2, Argument3 );
if ( !NT_SUCCESS(Status) ) { return0; } return1; }
3.2 NtQueueApcThread
函数NtQueueApcThread:(0环)将用户模式 APC 排队到指定线程的APC队列中。当指定的线程被吵醒(alertable)时就会执行插入的APC函数。(This function is used to queue a user-mode APC to the specified thread. The APC will fire when the specified thread does an alertable wait.)
函数原型如下:
1 2 3 4 5 6 7
NTSYSAPI NTSTATUS NTAPI NtQueueApcThread( IN HANDLE ThreadHandle, //目标线程句柄,函数调用者必须有对目标线程THREAD_SET_CONTEXT的访问权限 IN PPS_APC_ROUTINE ApcRoutine, //KAPC->NormalRoutine,用户APC函数,当执行条件满足时将会被执行 IN PVOID ApcArgument1, //KAPC->NormalContext,3环APC函数 IN PVOID ApcArgument2, //KAPC->SystemArgument1 ,3环APC函数的参数 IN PVOID ApcArgument3 //KAPC->SystemArgument2 ,作用不明,BaseDispatchAPC 里用到了 )
NTSYSAPI NTSTATUS NTAPI NtQueueApcThread( IN HANDLE ThreadHandle, // 线程句柄,用来获取线程结构 ETHREAD IN PPS_APC_ROUTINE ApcRoutine, // Apc->NormalRoutine ,是所有用户APC的总入口 BaseDispatchAPC(3环函数) IN PVOID ApcArgument1, // Apc->NormalContext ,3环APC函数 IN PVOID ApcArgument2, // Apc->SystemArgument1 ,3环APC函数的参数 IN PVOID ApcArgument3 // Apc->SystemArgument2 ,作用不明,BaseDispatchAPC 里用到了 ) /*++ Routine Description: This function is used to queue a user-mode APC to the specified thread. The APC will fire when the specified thread does an alertable wait Arguments: ThreadHandle - Supplies a handle to a thread object. The caller must have THREAD_SET_CONTEXT access to the thread. ApcRoutine - Supplies the address of the APC routine to execute when the APC fires. ApcArgument1 - Supplies the first PVOID passed to the APC ApcArgument2 - Supplies the second PVOID passed to the APC ApcArgument3 - Supplies the third PVOID passed to the APC Return Value: Returns an NT Status code indicating success or failure of the API --*/ { PETHREAD Thread; NTSTATUS st; KPROCESSOR_MODE Mode; PKAPC Apc;
PAGED_CODE();
Mode = KeGetPreviousMode ();
// 获取 ETHREAD st = ObReferenceObjectByHandle (ThreadHandle, THREAD_SET_CONTEXT, PsThreadType, Mode, &Thread, NULL); if (NT_SUCCESS (st)) { st = STATUS_SUCCESS; if (IS_SYSTEM_THREAD (Thread)) { st = STATUS_INVALID_HANDLE; } else { // 申请 APC 内存 Apc = ExAllocatePoolWithQuotaTag (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE, sizeof(*Apc), 'pasP');
VOID __stdcall KeInitializeApc( IN PRKAPC Apc, // APC结构体指针 IN PRKTHREAD Thread, // 要插入APC的目标线程 IN KAPC_ENVIRONMENT Environment, // 四种环境状态,包括父进程,挂靠进程,当前进程(提供CR3的进程),插入时的当前进程 IN PKKERNEL_ROUTINE KernelRoutine, // 不管是用户APC还是内核APC,这个函数的共同作用是释放APC //内核APC可能会有额外的功能,如退出、挂起、恢复线程 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,// 如果是用户APC,这里是NULL;如果是要求退出线程的内核APC,这里是 PspExitApcRundown IN PKNORMAL_ROUTINE NormalRoutine OPTIONAL, // 如果是用户APC,这里是 BaseDispatchAPC(3环函数) // 如果是内核APC,这里就是内核APC函数 IN KPROCESSOR_MODE ApcMode OPTIONAL, // 用户模式 / 内核模式 IN PVOID NormalContext OPTIONAL // 如果是用户APC,这里就是3环提供的APC函数的参数 ) /*++ Routine Description: This function initializes a kernel APC object. The thread, kernel routine, and optionally a normal routine, processor mode, and normal context parameter are stored in the APC object. 分配空间,初始化KAPC结构体 Arguments: Apc - Supplies a pointer to a control object of type APC. Thread - Supplies a pointer to a dispatcher object of type thread. Environment - Supplies the environment in which the APC will execute. Valid values for this parameter are: OriginalApcEnvironment, AttachedApcEnvironment, CurrentApcEnvironment, or InsertApcEnvironment KernelRoutine - Supplies a pointer to a function that is to be executed at IRQL APC_LEVEL in kernel mode. RundownRoutine - Supplies an optional pointer to a function that is to be called if the APC is in a thread's APC queue when the thread terminates. NormalRoutine - Supplies an optional pointer to a function that is to be executed at IRQL 0 in the specified processor mode. If this parameter is not specified, then the ProcessorMode and NormalContext parameters are ignored. ApcMode - Supplies the processor mode in which the function specified by the NormalRoutine parameter is to be executed. NormalContext - Supplies a pointer to an arbitrary data structure which is to be passed to the function specified by the NormalRoutine parameter. Return Value: None. --*/ {
ASSERT(Environment <= InsertApcEnvironment);
// Initialize standard control object header. Apc->Type = ApcObject; // 0x12 内核对象类型 Apc->Size = sizeof(KAPC);
// Initialize the APC environment, thread address, kernel routine address, // rundown routine address, normal routine address, processor mode, and // normal context parameter. If the normal routine address is null, then // the processor mode is defaulted to KernelMode and the APC is a special // APC. Otherwise, the processor mode is taken from the argument list. //typedef enum _KAPC_ENVIRONMENT { // OriginalApcEnvironment, // 所属进程(创建线程的进程) // AttachedApcEnvironment, // 挂靠进程 // CurrentApcEnvironment, // 当前环境,提供CR3的进程(正常状态是所属进程,挂靠状态是挂靠进程) // InsertApcEnvironment // 插入APC时的环境 //} KAPC_ENVIRONMENT;
BOOLEAN KeInsertQueueApc( IN PRKAPC Apc, //要插入的APC对象 IN PVOID SystemArgument1, //APC函数的参数 IN PVOID SystemArgument2, IN KPRIORITY Increment //线程优先级增量 ) /*++ Routine Description: This function inserts an APC object into the APC queue specifed by the thread and processor mode fields of the APC object. If the APC object is already in an APC queue or APC queuing is disabled, then no operation is performed. Otherwise the APC object is inserted in the specified queue and appropriate scheduling decisions are made. 插入 APC 到指定线程的APC队列,用户态和内核态分别插入对应的 APC 队列。 如果 APC 对象已经在 APC 队列或者 APC 队列被禁用(例如线程正在退出), 则不执行操作。否则插入 APC 对象并调用相应的函数。 Arguments: Apc - Supplies a pointer to a control object of type APC. APC 结构 SystemArgument1, SystemArgument2 - Supply a set of two arguments that contain untyped data provided by the executive. 传给 APC 函数的参数。 Increment - Supplies the priority increment that is to be applied if queuing the APC causes a thread wait to be satisfied. 线程优先级增量 Return Value: If the APC object is already in an APC queue or APC queuing is disabled, then a value of FALSE is returned. Otherwise a value of TRUE is returned. --*/ { BOOLEAN Inserted; KLOCK_QUEUE_HANDLE LockHandle; KIRQL OldIrql; PRKTHREAD Thread;
// Raise IRQL to SYNCH_LEVEL, acquire the thread APC queue lock, and lock the dispatcher database. // 提升 IRQL 等级,申请 APC 锁,锁 dispatcher database Thread = Apc->Thread; KeAcquireInStackQueuedSpinLockRaiseToSynch(&Thread->ApcQueueLock, &LockHandle); KiLockDispatcherDatabaseAtSynchLevel();
// If APC queuing is disabled, then set inserted to FALSE. Else save // system parameter values in APC object, and attempt to queue APC. // 调用 KiInsertQueueApc if (Thread->ApcQueueable == FALSE) { Inserted = FALSE;
// Unlock the dispatcher database from SYNCH_LEVEL, unlock the thread APC // queue lock and lower IRQL to its previous value, and return whether the APC was inserted. KiUnlockDispatcherDatabaseFromSynchLevel(); KeReleaseInStackQueuedSpinLock(&LockHandle); return Inserted; }
BOOLEAN FASTCALL KiInsertQueueApc( IN PKAPC Apc, IN KPRIORITY Increment ) /*++ Routine Description: This function inserts an APC object into a thread's APC queue. The address of the thread object, the APC queue, and the type of APC are all derived from the APC object. If the APC object is already in an APC queue, then no opertion is performed and a function value of FALSE is returned. Else the APC is inserted in the specified APC queue, its inserted state is set to TRUE, and a function value of TRUE is returned. The APC will actually be delivered when proper enabling conditions exist. N.B. The thread APC queue lock and the dispatcher database lock must both be held when this routine is called. Arguments: Apc - Supplies a pointer to a control object of type APC. Increment - Supplies the priority increment that is to be applied if queuing the APC causes a thread wait to be satisfied. Return Value: If the APC object is already in an APC queue, then a value of FALSE is returned. Else a value of TRUE is returned. --*/ {
// // If the APC object is already in an APC queue, then set inserted to // FALSE. Else insert the APC object in the proper queue, set the APC // inserted state to TRUE, check to determine if the APC should be delivered // immediately, and set inserted to TRUE. // // For multiprocessor performance, the following code utilizes the fact // that kernel APC disable count is incremented before checking whether // the kernel APC queue is nonempty. // // See KeLeaveCriticalRegion(). //
Thread = Apc->Thread; if (Apc->Inserted) { Inserted = FALSE;
// // Insert the APC after all other special APC entries selected by // the processor mode if the normal routine value is NULL. Else // insert the APC object at the tail of the APC queue selected by // the processor mode unless the APC mode is user and the address // of the special APC routine is exit thread, in which case insert // the APC at the front of the list and set user APC pending. //
// // If the APC index from the APC object matches the APC Index of // the thread, then check to determine if the APC should interrupt // thread execution or sequence the thread out of a wait state. //
// // If the processor mode of the APC is kernel, then check if // the APC should either interrupt the thread or sequence the // thread out of a Waiting state. Else check if the APC should // sequence the thread out of an alertable Waiting state. //
if (ApcMode == KernelMode) { // 标记已插入内核APC Thread->ApcState.KernelApcPending = TRUE;
if (Thread->State == Running) { // 如果线程正在运行,则 APC 中断 KiRequestApcInterrupt(Thread->NextProcessor);