在使用插User APC注入DLL时,经常面临一个问题,那就是线程必须是处于Alertable模式才能注入成功。但一直对这个Alertable的含义不甚清楚,今天总算是把这个梗消化了。

微软对Alertable与APC的执行关系有详细的描述:

https://msdn.microsoft.com/en-us/library/ms810047.aspx

其中有一段是这样说的:

也就是说,正常情况下,用户模式的APC是不会打断用户态程序的执行流的。除非,线程是Alertable——可唤醒的。

So, what is Alertable?

想象一个应用场景:

客户端程序每隔5分钟就和服务端进行一次通信,实现“心跳”,最简单的就是使用Sleep(5*60*1000)。那么这样一来,这5分钟内,线程就沉睡了,如果这个时候有比较紧急的网络IO事件发生怎么办呢?线程还在沉睡中,因为5分钟时间还未到,所以无法及时处理这些事件。如何解决这个问题呢?那就是使用SleepEx替换Sleep。这个函数比起Sleep就多了一个参数Alertable,表示该线程是“可唤醒的”,就是说,线程虽然等待时间未到,但如果发生一些事件,线程也会及时去处理。这些事件就是:IO完成例程需要执行或者线程有APC需要交付。

内核中线程数据结构KTHREAD中的Alertable成员就是表示该线程是不是可唤醒的。这个成员会在什么时候被赋值呢?参见WRK有三个宏会设置该值:

InitializeDelayExecution()

InitializeWaitSingle()

InitializeWaitMultiple()

这三个宏分别被

SleepEx()---->KeDelayExecutionThread()

WaitForSingleObject()---->KeWaitForSingleObject()

WaitForMultipleObjects()---->KeWaitForMultipleObjects()

调用。

当上述调用发生时,线程Alertable被置为TRUE。同时,还会通过宏TestForAlertPending设置KTHREAD的另外一个成员:UserApcPending,当Alertable为TRUE,并且User APC队列不为空,那么该值将被置为TRUE。

当从内核模式返回时,DISPATCH_USER_APC在交付用户模式APC前会判断这个标志,如果为FALSE,则不会交付User APC。

这也就是为什么当线程为Alertale的时候,插入的User APC才会得到执行。

还有一个问题:在进程启动时使用驱动进行APC注入DLL的时候,并没有去考虑这个UserApcPending标记,那为什么APC就一定能得到执行呢?

这是因为,在XP中,进程初始化重要工作LdrInitializeThunk本身就是使用APC进行执行的,所以在PspUserThreadStartup中对UserApcPending设置为了TRUE,这样保证了初始的时候User APC能成功交付。

即使在Win7中,LdrInitializeThunk不再使用APC进行派遣,LdrInitializeThunk在执行完成后会使用NtTestAlert,同样会设置UserApcPending为TRUE。从而在返回用户模式时,User APC同样能交付。

但每次交付用户模式的时候,UserApcPending会被重置,所以线程启动之后就不再能保证插APC能得到执行了。除非使用前面说到的那些Alertable为TRUE的等待函数,再次设置了UserApcPending。

线程的Alertable与User APC的更多相关文章

  1. 深入学习APC

    一.前言 在NT中,有两种类型的APCs:用户模式和内核模式.用户APCs运行在用户模式下目标线程当前上下文中,并且需要从目标线程得到许可来运行.特别是,用户模式的APCs需要目标线程处在alerta ...

  2. 注入理解之APC注入

    近期学习做了一个各种注入的MFC程序,把一些心得和体会每天分享一些 APC(Asynchronous procedure call)异步程序调用,在NT中,有两种类型的APCs:用户模式和内核模式.用 ...

  3. [7] Windows内核情景分析---线程同步

    基于同步对象的等待.唤醒机制: 一个线程可以等待一个对象或多个对象而进入等待状态(也叫睡眠状态),另一个线程可以触发那个等待对象,唤醒在那个对象上等待的所有线程. 一个线程可以等待一个对象或多个对象, ...

  4. APC注入(Ring3)

    首先简单介绍一下APC队列和Alertable. 看看MSDN上的一段介绍(https://msdn.microsoft.com/en-us/library/ms810047.aspx): The s ...

  5. 异步机制 - APC

    APC : An asynchronous procedure call,异步过程调用,是微软提供的一种在线程上下文中执行代码的机制.当向一个线程插入一个USER APC时,如果线程进入alertab ...

  6. windows:shellcode 远程线程hook/注入(一)

    https://www.cnblogs.com/theseventhson/p/13199381.html 上次分享了通过APC注入方式,让目标线程运行shellcode.这么做有个前提条件:目标线程 ...

  7. windows:shellcode 代码远程APC注入和加载

    https://www.cnblogs.com/theseventhson/p/13197776.html  上一章介绍了通用的shellcode加载器,这个加载器自己调用virtualAlloc分配 ...

  8. APC 篇—— APC 挂入

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  9. 深入Windows APC

      本篇原文为 Depths of Windows APC ,如果有良好的英文基础,可以点击该链接进行阅读.本文为我个人:寂静的羽夏(wingsummer) 中文翻译,非机翻,著作权归原作者 Rbmm ...

随机推荐

  1. python , angular js 学习记录【3】

    1.Alembic是SQLAlchemy作者编写的Python数据库迁移工具.用它实现模型类和数据库的同步更新.(安装以及操作步骤 使用Alembic迁移数据库) 使用Alembic添加数据库字段操作 ...

  2. Android 获得AndroidManifest文件里自定义的meta标签内容

    try { ApplicationInfo appInfo= this.getPackageManager().getApplicationInfo(getPackageName(),PackageM ...

  3. Java控制Appium server start/stop

    相信很多人都会遇到这种场景,在进行appium自动化的时候用Windows OS,不好实现后台运行,每次启动Appium server: 使用Appium GUI版手动点击 就是在cmd line 启 ...

  4. 线性表Linearlist

    顺序存储,链式存储,索引存储,散列存储 基本运算 SLIST         1.置空表 void SetNull(&L)     2.求长度 int Length(L)     3.取元素 ...

  5. [Sass]占位符 %placeholder

    [Sass]占位符 %placeholder Sass 中的占位符 %placeholder 功能是一个很强大,很实用的一个功能,这也是我非常喜欢的功能.他可以取代以前 CSS 中的基类造成的代码冗余 ...

  6. iOS10 推送必看(基础篇)

    虽然这篇文章比较长,也不好理解,但是还是建议大家收藏,以后用到的时候,可以看看,有耐心的还是读一读. 这篇文章开始,我会跟大家好好讲讲,苹果新发布的iOS10的所有通知类. 一.创建本地通知事例详解: ...

  7. vue2.0环境搭建

    1.安装node.js(官网) 2.安装淘宝镜像  npm install -g cnpm --registry=https://registry.npm.taobao.org 3.安装webpack ...

  8. [笔记]linux下环境变量配置字段

    一般修改/etc/profile文件 java: export JAVA_HOME=/usr/lib/java/jdk1.8.0_92export CLASSPATH=.:$JAVA_HOME/lib ...

  9. mysql基本信息收集

    1.下载安装 percona-toolkit 工具包http://www.percona.com/downloads/percona-toolkit/LATEST/tarball/2.运行下面两个工具 ...

  10. 用 IIS 实现请求转发

    最近部门要开发一个简单的APP,部分数据是现有项目已经存在的,为了方便维护,希望只提供一个交互的入口,并且协议的规则不变. 基于这个需求,有两套解决方案: 1.用代码将现有的api封装一层,对请求数据 ...