线程是不能被杀掉、挂起、恢复的,线程在执行的时候自己占着CPU,别人怎么可能控制它呢?
如果不调用API,屏蔽中断,并保证代码不出现异常,线程将永久占用CPU,何谈控制呢?所以说线程如果想死,一定是自己执行代码把自己杀死,不存在它杀的情况
如果想改变一个线程的行为怎么办呢?
可以给她提供一个函数,让它自己去调用,这个函数就是APC(异步过程调用)
KAPC
kd> dt _KAPC
nt!_KAPC
+0x000 Type //类型 APC类型为0x12
+0x002 Size //本结构体的大小 0x30
+0x004 Spare0 //未使用
+0x008 Thread //目标线程
+0x00c ApcListEntry //APC队列挂的位置
+0x014 KernelRoutine //指向一个函数(调用ExFreePoolWithTag 释放APC)
+0x018 RundownRoutine//略
+0x01c NormalRoutine //用户APC总入口 或者 真正的内核apc函数
+0x020 NormalContext //内核APC:NULL 用户APC:真正的APC函数
+0x024 SystemArgument1//APC函数的参数
+0x028 SystemArgument2//APC函数的参数
+0x02c ApcStateIndex //挂哪个队列,有四个值:0 1 2 3
+0x02d ApcMode //内核APC 用户APC
+0x02e Inserted //表示本apc是否已挂入队列 挂入前:0 挂入后 1
Type :类型。在Windows里,任何一种内核对象都有一个编号,这个编号用来标识你是属于哪一种类型,APC本身也是一种内核对象,它也有一个编号,是0x12
Size:这个成员指的是当前的KAPC的结构体的大小
Thread:每一个线程都有自己的APC队列,这个成员指定了APC属于哪一个线程
ApcListEntry:APC队列挂的位置,是一个双向链表,通过这个双向链表可以找到下一个APC
KernelRoutine:指向一个函数(调用ExFreePoolWithTag 释放APC)。当我们的APC执行完毕以后,当前的KAPC本身的这块内存,会由KernelRoutine指定的函数来释放
NormalRoutine:如果当前是内核APC,通过这个值找到的就是真正的内核APC函数;如果当前的APC是用户APC,那么这个位置指向的是用户APC总入口,通过这个总入口可以找到所有用户提供的APC函数
NormalContext:如果当前是内核APC,通过这个值为空;如果当前的APC是用户APC,那么这个值指向的是真正的用户APC函数
SystemArgument1 SystemArgument2 APC函数的参数
ApcStateIndex:当前的APC要挂到哪个队列
ApcMode:当前的APC是用户APC还是内核APC
Inserted:当前的APC结构体是否已经插入到APC队列
KAPC_STATE
ntdll!_KTHREAD
+0x034 ApcState : _KAPC_STATE
+0x138 ApcStatePointer : [2] Ptr32 _KAPC_STATE
+0x14c SavedApcState : _KAPC_STATE
+0x165 ApcStateIndex : UChar
+0x166 ApcQueueable : UChar ;表示是否可以向线程的APC队列中插入APC
.
.....
kd> dt _KAPC_STATE
ntdll!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY ;2个APC队列,用户APC和内核APC
+0x010 Process : Ptr32 _KPROCESS ;线程所属或者挂靠的进程
+0x014 KernelApcInProgress : UChar ;内核APC是否正在执行
+0x015 KernelApcPending : UChar ;是否有正在等待执行的内核APC
+0x016 UserApcPending : UChar ;是否有正在等待执行的用户APC
KiServiceExit函数 : 这个函数是系统调用、异常或中断返回用户空间的必经之路
KiDeliverApc函数 : 负责执行APC函数
备用APC队列
SavedApcState
当T线程挂靠B进程后,APC队列中存储的却仍然是原来的APC!具体点说,比如某个APC函数要读取一个地址0x12345678的数据,如果此时进行读取,读到的将是B进程的地址空间,这样逻辑就错误了!
为了避免混乱,在T线程挂靠B进程的时候,会将ApcState中的值暂时存储到SavedApcState ,等回到原来的进程A,再将APC队列恢复。所以SavedApcState 称为备用APC队列
A进程的T线程挂靠B进程,A是T的所属进程,B是T的挂靠进程
ApcState B进程相关的APC函数
SavedApcState A进程相关的APC函数
ApcStatePointer
正常情况下 :
ApcStatePointer [0] 指向ApcState
ApcStatePointer [1] 指向SavedApcState
挂靠情况下 :
ApcStatePointer [0] 指向SavedApcState
ApcStatePointer [1] 指向ApcState
ApcStateIndex
当前线程处于什么状态
0 : 正常状态
1 : 挂靠状态
ApcStatePointer[ApcStateIndex] 指向的一定是APC队列(ApcState)
ApcQueueable
是否可以向线程的APC队列中插入APC
当线程正在执行退出代码时,会将这个值设置为0,如果执行插入APC的代码(KeInsertQueueApc),在插入函数中会判断这个值的状态,如果为0,则插入失败