01 APC

线程是不能被杀掉、挂起、恢复的,线程在执行的时候自己占着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,则插入失败

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值