滴水逆向apc机制

线程是不能被“杀掉”、“挂起”、“恢复”的,线程在执行的时候自己占据着CPU,别人怎么可能控制它呢?

举个极端的例子:如果不调用API,屏蔽中断,并保证代码不出现异常,线程将永久占用CPU,何谈控制呢?所以说线程如果想“死",一定是自己执行代码把自己杀死,不存在“他杀”这种情况!

那如果想改变一个线程的行为该怎么办呢?

可以给他提供一个函数,让它自己去调用,这个函数就是APC (Asyncroneus Procedure Call),即异步过程调用。
在这里插入图片描述

nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x018 CycleTime : Uint8B
+0x020 QuantumTarget : Uint8B
+0x028 InitialStack : Ptr64 Void
+0x030 StackLimit : Ptr64 Void
+0x038 KernelStack : Ptr64 Void
+0x040 ThreadLock : Uint8B
+0x048 WaitRegister : _KWAIT_STATUS_REGISTER
+0x049 Running : UChar
+0x04a Alerted : [2] UChar
+0x04c KernelStackResident : Pos 0, 1 Bit
+0x04c ReadyTransition : Pos 1, 1 Bit
+0x04c ProcessReadyQueue : Pos 2, 1 Bit
+0x04c WaitNext : Pos 3, 1 Bit
+0x04c SystemAffinityActive : Pos 4, 1 Bit
+0x04c Alertable : Pos 5, 1 Bit
+0x04c GdiFlushActive : Pos 6, 1 Bit
+0x04c UserStackWalkActive : Pos 7, 1 Bit
+0x04c ApcInterruptRequest : Pos 8, 1 Bit
+0x04c ForceDeferSchedule : Pos 9, 1 Bit
+0x04c QuantumEndMigrate : Pos 10, 1 Bit
+0x04c UmsDirectedSwitchEnable : Pos 11, 1 Bit
+0x04c TimerActive : Pos 12, 1 Bit
+0x04c SystemThread : Pos 13, 1 Bit
+0x04c Reserved : Pos 14, 18 Bits
+0x04c MiscFlags : Int4B
+0x050 ApcState : _KAPC_STATE //apc结构体
+0x050 ApcStateFill : [43] UChar

nt!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY //两个链表,内核和用户,apc函数挂到链表里,函数地址大于8,内核空间函数
+0x020 Process : Ptr64 _KPROCESS //当前线程属于进程,当挂靠时,为挂靠进程
+0x028 KernelApcInProgress : UChar //内核apc程序是否正在执行
+0x029 KernelApcPending : UChar //是否存在内核apc函数,存在为1,不存在为0
+0x02a UserApcPending : UChar //是否存在用户apc函数,存在为1,不存在为0

在这里插入图片描述
nt!_KAPC
+0x000 Type : UChar //类型
+0x001 SpareByte0 : UChar
+0x002 Size : UChar //结构体大小
+0x003 SpareByte1 : UChar
+0x004 SpareLong0 : Uint4B
+0x008 Thread : Ptr64 _KTHREAD //目标线程
+0x010 ApcListEntry : _LIST_ENTRY APC队列挂的位置
+0x020 KernelRoutine : Ptr64 void //指向一个函数(调用ExFreePoolWithTag释放当前apc所占用的内存),
+0x028 RundownRoutine : Ptr64 void
+0x030 NormalRoutine : Ptr64 void //希望当前线程所执行的函数用户APC总入口 或者真正的内核APC函数
+0x038 NormalContext : Ptr64 Void 内核apc:NULL 用户apc:真正的APC函数
+0x040 SystemArgument1 : Ptr64 Void APC函数的参数
+0x048 SystemArgument2 : Ptr64 Void APC函数的参数
+0x050 ApcStateIndex : Char 挂哪个队列,有四个值:0 1 2 3
+0x051 ApcMode : Char 内核APC 用户APC
+0x052 Inserted : UChar 表示本apc是否已挂入队列 挂入前:0 挂入后:1

通过_KAPC+0x30指向希望执行的函数在哪里,把_KAPC存入nt!_KAPC_STATE+0x000 ApcListHead 中
在这里插入图片描述
KiServiceExit中判断KTHREAD.ApcState.UserApcPending是否为0,如果为0说明当前线程中没有用户APC函数
不管有没有用户APC,KiDeliverApc都会首先处理内核Apc,所以不需要判断有没有内核Apc
在这里插入图片描述

执行APC

1、执行点:线程切换
在这里插入图片描述
2、执行点:系统调用、中断或异常(_KiSereviceExit)
当要执行用户APC之前,先要执行内核APC
KiServiceExit函数:这个函数是系统调用、异常或中断返回用户空间的必经之路
在这里插入图片描述
判断userapcpending是不是为0,为0意味着当前线程没有需要执行的用户apc,不为0意味有需要执行的用户apc

KiDeliverApc函数:负责执行apc函数。内核apc一定会处理,并为用户空间apc的执行进行准备

备用apc队列

在这里插入图片描述
在这里插入图片描述
nt!_KTHREAD

+0x050 ApcState : _KAPC_STATE //apc结构体
+0x100 ApcQueueable : Pos 5, 1 Bit //表示是否可以向线程的apc队列中插入apc,当线程正在执行退出代码时,会将这个值设置为0,如果此时执行插入apc代码(KeInsertQueueApc),在插入函数中会判断这个值的状态,为0则插入失败

+0x1f0 ApcStateIndex : UChar //标识当前线程处于什么状态 0正常状态 1挂靠状态

+0x230 ApcStatePointer : [2] Ptr64 _KAPC_STATE

+0x240 SavedApcState : _KAPC_STATE

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

apc挂入过程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
NTQueueUserApc已经为KAPC结构体分配了一块内存,传给KeInitializeApc函数
在这里插入图片描述
TargetEnviroument对应_KAPC中的ApcStateIndex
在这里插入图片描述
在这里插入图片描述
当为用户层Apc时,会跳转到下面执行
在这里插入图片描述
判断线程是否为等待状态,如果不是跳转,返回为1,但并不把UserAPCPending改为1,虽然Apc已写入队列,但Apc有可能不执行。因为执行用户Apc时会判断UserAPCPending是否为1。
在这里插入图片描述
在这里插入图片描述
**

内核Apc执行过程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

用户Apc执行过程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值