内核APC用户APC详解

news/2024/11/14 13:26:57/

内核APC

线程切换

SwapContext 判断是否有内核APC KiSwapThreadKiDeliverApc 执行内核APC函数

定位到SwapContext函数,然后查看KernelApcPending的值是否为空,不为空则跳转,这里只是进行判断,我们往上跟

然后回到KiSwapContext

再往上走得到KiSwapThread

这里判断后进行跳转

然后调用KiDeliverApc

系统调用、中断或者异常

当要执行用户APC之前,先要执行内核APC,这里找到KiServiceExit,有一个比较检验UserApcPending的值是否有APC请求

然后调用KiDeliverApc

内核层APC执行

KiDeliverApc

继续往里面跟,判断内核APC的链表是否为空,若不为空则跳转

NormalRoutine

跳转后判断NormalRoutine里面存储的是内核APC的地址还是APC的的总入口,然后再跳转

如果为空向下执行则会调用KernelRoutine对APC进行销毁

跳转过后执行真正的内核APC函数NormalRoutine

内核APC执行流程

KiDeliverApc函数执行流程1) 判断第一个链表是否为空2) 判断KTHREAD.ApcState.KernelApcInProgress是否为13) 判断是否禁用内核APC(KTHREAD.KernelApcDisable是否为1)4) 将当前KAPC结构体从链表中摘除5) 执行KAPC.KernelRoutine指定的函数 释放KAPC结构体占用的空间6) 将KTHREAD.ApcState.KernelApcInProgress设置为1 标识正在执行内核APC7) 执行真正的内核APC函数(KAPC.NormalRoutine)8) 执行完毕 将KernelApcInProgress改为0  9) 循环

用户APC

当产生系统调用、中断或者异常,线程在返回用户空间前都会调用KiServiceExit函数,在KiServiceExit会判断是否有要执行的用户APC,如果有则调用KiDeliverApc函数(第一个参数为1)进行处理。

处理用户APC要比内核APC复杂的多,因为,用户APC函数要在用户空间执行的,这里涉及到大量换栈的操作:

当线程从用户层进入内核层时,要保留原来的运行环境,比如各种寄存器,栈的位置等等 (_Trap_Frame),然后切换成内核的堆栈,如果正常返回,恢复堆栈环境即可。

但如果有用户APC要执行的话,就意味着线程要提前返回到用户空间去执行,而且返回的位置不是线程进入内核时的位置,而是返回到其他的位置,每处理一个用户APC都会涉及到:

内核-->用户空间-->再回到内核空间

KiDeliverApc

1) 判断用户APC链表是否为空2) 判断第一个参数是为1  3) 判断ApcState.UserApcPending是否为14) 将ApcState.UserApcPending设置为05) 链表操作 将当前APC从用户队列中拆除6) 调用函数(KAPC.KernelRoutine)释放KAPC结构体内存空间7) 调用KiInitializeUserApc函数

线程进0环时,原来的运行环境(寄存器栈顶等)保存到_Trap_Frame结构体中,如果要提前返回3环去处理用户APC,就必须要修改_Trap_Frame结构体:

比如:进0环时的位置存储在EIP中,现在要提前返回,而且返回的并不是原来的位置,那就意味着必须要修改EIP为新的返回位置。还有堆栈ESP,也要修改为处理APC需要的堆栈。那原来的值怎么办呢?处理完APC后该如何返回原来的位置呢?

KiInitializeUserApc要做的第一件事就是备份:

将原来_Trap_Frame的值备份到一个新的结构体中(CONTEXT),这个功能由其子函数KeContextFromKframes来完成,代码如下

首先判断参数是否为1,当参数为1的时候处理用户APC

然后进行一系列的操作

KiInitializeUserApc

接着转到KiInitializeUserApc函数

CONTEXTTrapFrame传入KeContextFromKframes

这里接着往下看,这里得到C4

C4对应的Esp存储的是3环原来的栈顶

然后以4字节对齐将3环堆栈减去0x2DC个字节,这里是因为要将CONTEXT结构和KAPC的4个参数传给3环

原本三环的ESP如图所示

CONTEXT结构体的大小为0x2CC,KAPC的4个参数的大小为0x10,所以减去0x2DC

这一部分代码主要是将CONTEXT结构复制到3环的堆栈

当windows把CONTEXT结构复制到堆栈之后,准备用户层执行环境,首先修改SS、DS、ES、FS、GS和EFLAGS寄存器

然后修改esp到3环堆栈

KiUserApcDispatcher

然后修改eip,这里永远返回一个固定的位置,但是这个位置在每次系统启动的时候都不相同,存放在3环的ntdll里的KiUserApcDispatcher参数里面

然后到ntdll里面定位到KiUserApcDispatcher,首先得到指向CONTEXT结构的指针,然后pop eax得到NormalRoutine结构,这里当APC是内核APC的时候存储的是真正的APC地址,当APC是用户APC的时候存储的是指向用户APC的总入口

当我们调用QueueUserAPC,并没有指定NormalRoutine结构,只指定了NormalContextSystemArgument1,那么这个参数在QueueUserAPC内部指定,在kernel32.dllBaseDispatchAPC,用来调用真正的用户APC函数

再继续往下跟,调用了ZwContinue

1) 返回内核,如果还有用户APC,重复上面的执行过程。2) 如果没有需要执行的用户APC,会将CONTEXT赋值给Trap_Frame结构体。就像从来没有修改过一样。ZwContinue后面的代码不会执行,线程从哪里进0环仍然会从哪里回去。

使用0x20的调用号利用调用门回到0环

用户APC执行流程

总结:

1.内核APC在线程切换时执行,不需要换栈,比较简单,一个循环执行完毕。

2.用户APC在系统调用、中断或异常返回3环前会进行判断,如果有要执行的用户APC,再执行。

3.用户APC执行前会先执行内核APC。


http://www.ppmy.cn/news/593107.html

相关文章

APCS

介绍 APCS,ARM 过程调用标准(ARM Procedure Call Standard),提供了紧凑的编写例程的一种机制,定义的例程可以与其他例程交织在一起。最显著的一点是对这些例程来自哪里没有明确的限制。它们可以编译自 C、 Pascal、也可以是用汇编语言写成的。…

apc和apcu

apc apc(alternative php cache) apc的功能分为两部分 1. opcode缓存 2. 数据缓存,可以存储k/v对,类似memcache apc的问题 在php5.3.* 之后的版本自带php_opcache,不再需要apc的opcode缓存功能apc的3.1.14版本在php5.5版本上有严重的内…

APC机制详解

文章目录 APC的本质APC队列APC结构APC相关函数KiServiceExitKiDeliveApc 备用APC队列ApcState的含义挂靠环境下的ApcState的含义其他APC相关成员ApcStatePointerApcStateIndexApcStatePointer与ApcStateIndex组合寻址ApcQueueable APC挂入过程KAPC结构挂入流程KeInitializeApcA…

先进控制技术(APC) 改善企业效益-智能工厂的基石

现代复杂的工业生产过程,通过实施先进控制,可以大大提高工业生产过程操作和控制的稳定性,改善工业生产过程的动态性能,减少关键变量的运行波动幅度,使其更接近于优化目标值,从而将工业生产过程推向更接近装置约束边界条件下运行,最终达到增强工业生产过程的稳定性和安全…

什么是APC模型

一、APC模型的由来 在社会科学领域,社会科学家们总是很关心各种各样的社会变迁,例如在过去几十年间人们的健康程度(如某疾病的患病率)有着什么样的变化趋势,人们的社会态度(例如性别观念)又是怎…

先进过程控制之一:浅说APC

先进过程控制(APC)技术作为在生产装置级的信息化应用,在优化装置的控制水平和提高生产过程的管理水平的同时,还为企业创造了可观的经济效益。 1、什么是APC 先进过程控制,简称APC,并不是什么新概念。它仅…

[Errno 2] No such file or directory的解决方法

https://blog.csdn.net/johinieli/article/details/70855058

现代笑话二则:1、美女与帅哥;2、恐龙与王子。

1、我因工作需要短途出差,到长途汽车站买好车票后,站在路边等车,身边一个等车美女,看着不宽的马路对面的一个帅哥嘻嘻笑,马路对面的那个帅哥也看见这边的美女对他笑,他便美滋滋的对着这边的美女摆POSE&…