天天看点

驱动篇——常规的0环与3环通信

驱动篇之常规的0环与3环通信,详细介绍常规的0环与3环通信。

  此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。

你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。

  看此教程之前,问个问题,你明确学驱动的目的了吗?你的开发环境准备好了吗?上一节的内容学会了吗? 没有的话就不要继续了,请重新学习前面驱动篇的教程内容继续。

🔒 华丽的分割线 🔒

本次答案均为参考,可以与我的答案不一致,但必须成功通过。

1️⃣ 遍历内核模块,输出模块名称,基址以及大小。

🔒 点击查看答案 🔒

  此题目不难,就是一个循环双向链表的遍历,代码见下面的折叠,效果如下:

驱动篇——常规的0环与3环通信

🔒 点击查看代码 🔒

2️⃣ 编写一个函数,找到一个未导出的函数,并调用。(例子:找到<code>PspTerminateProcess</code>,通过调用这个函数结束记事本进程)

  根据<code>PE</code>的知识,我们可以通过基址+偏移的方式定位该函数,这个是最简洁的方式。当然可以通过特征码的方式,不过效率低,特征码找不好还不准确。

  我们先在<code>WinDbg</code>找找这个函数在哪里:

  这个函数是在内核文件导出,分页不同,导出的函数偏移可能不同,下面是在<code>2-9-9-12</code>分页模式下做的实验,如果在<code>10-10-12</code>分页可能函数的位置不同:

  我们只需要获取函数偏移,获取基地址,加起来即是函数地址,然后调用即可,代码见折叠,必要位置具有注释。

  好了,我们尝试一下能不能终止进程,先在<code>WinDbg</code>找到<code>EPROCESS</code>结构体的地址:

  <code>89cb7918</code>就是我们需要的地址,修改调用<code>PspTerminateProcess</code>的第一个参数的数值,然后编译。在虚拟机进行注册启动驱动效果如下:

驱动篇——常规的0环与3环通信

  由于这个函数很底层,可以干掉很多流氓软件,甚至杀软都不放过。比如火绒(已将该情况上报给火绒官方,乱搞后果自负):

驱动篇——常规的0环与3环通信

3️⃣ 通过断链实现隐藏驱动模块。

  此题目不难,就是一个链表断链,效果如下:

驱动篇——常规的0环与3环通信

  <code>PCHunter</code>这个<code>ARK</code>工具仍能发现我们的模块,指明为隐藏驱动。但是你用普通的<code>API</code>试试,你绝对发现不了它。

  我们在开发窗口程序的时候,消息被封装成一个结构体:<code>MSG</code>。在内核开发时,消息被封装成另外一个结构体:<code>IRP</code>,英文全称:<code>I/O Request Package</code>。在窗口程序中,能够接收消息的只能是窗口对象。在内核中,能够接收<code>IRP</code>消息的只能是设备对象。示意图如下所示:

驱动篇——常规的0环与3环通信

  为了实现3环程序与驱动程序正常的通信功能,微软提供了一系列的<code>API</code>。我们可以通过它来实现常规的通信。我们的硬盘、键盘、显卡想要工作,在<code>Windows</code>平台都需要用此实现通信,来实现想要的功能。下面我来介绍具体流程。

  如果<code>MSG</code>需要传递,就必须创建一个窗体,因为只有窗体才有消息队列这个东西,才嗯那个接收消息。如果想要驱动实现通信,就必须有一个设备对象。我们可以用下面的代码实现创建设备:

  既然设备对象创建好了,我们需要规定一个“协议”,就是3环程序与驱动交互的协议。具体有如下几个方式:

  缓冲区方式读写(<code>DO_BUFFERED_IO</code>) :操作系统将应用程序提供缓冲区的数据复制到内核模式下的地址中。

  直接方式读写<code>(DO_DIRECT_IO</code>) :操作系统会将用户模式下的缓冲区锁住。然后操作系统将这段缓冲区在内核模式地址再次映射一遍。这样,用户模式的缓冲区和内核模式的缓冲区指向的是同一区域的物理内存。缺点就是要单独占用物理页面。

  其他方式读写(在调用<code>IoCreateDevice</code>创建设备后对<code>pDevObj-&gt;Flags</code>即不设置<code>DO_BUFFERED_IO</code>也不设置<code>DO_DIRECT_IO</code>此时就是其他方式。在使用其他方式读写设备时,派遣函数直接读写应用程序提供的缓冲区地址。在驱动程序中,直接操作应用程序的缓冲区地址是很危险的。只有驱动程序与应用程序运行在相同线程上下文的情况下,才能使用这种方式。如果<code>CPU</code>中的任务切换了,即<code>CR3</code>切换掉了,在高<code>2GB</code>的驱动仍在使用该方式读取低<code>2GB</code>内存,导致读到的数据和实际不符,导致错误,故强烈不推荐此方式。

  用代码设置交互数据的方式举例如下:

  设备对象创建好了,通信方式也约定好了,但3环的程序仍找不到你的驱动对象。设备名称的作用是给内核对象用的,如果要在3环访问,必须要有符号链接。其实就是一个别名,没有这个别名,在3环不可见。用代码实现如下:

  有些细节需要特别注意:内核模式下,符号链接是以<code>\??\</code>开头的,如C盘就是<code>\??\C:</code>。而在用户模式下,则是以<code>\\.\</code>开头的,如C盘就是<code>\\.\C:</code>。

  前面的代码都写好的,驱动与3环的通信的基础就搭建好了。但是,如果真正实现通信,还得需要注册派遣函数。

驱动篇——常规的0环与3环通信

  如上图所示,我们在编写<code>Win32</code>窗体程序时。假设我在窗体点击了鼠标,操作系统就会产生一个消息,用<code>MSG</code>这个结构体封装一下,派发给窗体对象。目标窗体对象接受到后发现它是鼠标单击消息。窗体对象中注册了很多回调函数:鼠标点击回调、鼠标双击回调、键盘键按下回调等等。然后进一步处理是单击,就调用单击回调函数。同理,我们在3环调用<code>CreateFile</code>函数,操作系统就会产生一个<code>IRP</code>派发给设备对象,目标设备对象处理方式和窗体消息没啥差别。接下来我们看看<code>IRP</code>的类型:

  当应用层通过<code>CreateFile</code>、<code>ReadFile</code>、<code>WriteFile</code>、<code>CloseHandle</code>等函数打开、从设备读取数据、向设备写入数据、关闭设备的时候,会使操作系统分别产生出<code>IRP_MJ_CREATE</code>、<code>IRP_MJ_READ</code>、<code>IRP_MJ_WRITE</code>、<code>IRP_MJ_CLOSE</code>等不同的<code>IRP</code>。值得注意的是,我们之前使用<code>CreateFile</code>这个东西只是为了创建文件,其实它的本质是与设备对象创建访问,我们3环程序想要通过符号链接与驱动建立通讯,就必须通过这个函数。

  当然<code>IRP</code>不止上面的这几种,我们再给出常见的<code>IRP</code>:

IRP类型

来源

IRP_MJ_DEVICE_CONTROL

使用 DeviceControl 函数时产生

IRP_MJ_POWER

在操作系统处理电源消息时产生

IRP_MJ_SHUTDOWN

关闭系统前时产生

  我们最常用的<code>IRP</code>有<code>IRP_MJ_DEVICE_CONTROL</code>、<code>IRP_MJ_CREATE</code>和<code>IRP_MJ_CLOSE</code>,以实现交互、创建访问、关闭访问的功能。

  了解了上面的东西,我们如何注册派遣函数呢?我们再看一下<code>DRIVER_OBJECT</code>这个东西:

  有没有注意到<code>MajorFunction</code>这个成员,它是一个数组,具有28个,我们的派遣函数都会在这里面,如何注册我们用如下代码形式:

  回调函数都有自己的格式,派遣函数也不例外,它的格式如下:

本节的答案将会在下一节进行讲解,务必把本节练习做完后看下一个讲解内容。不要偷懒,实验是学习本教程的捷径。

  俗话说得好,光说不练假把式,如下是本节相关的练习。如果练习没做好,就不要看下一节教程了,越到后面,不做练习的话容易夹生了,开始还明白,后来就真的一点都不明白了。本节练习不多,请保质保量的完成。

1️⃣ 实现一个工具,利用未导出的函数<code>PspTerminateProcess</code>杀死软件(驱动的加载可不用代码实现,使用本教程工具进行加载)。

  驱动篇——总结与提升

驱动篇——常规的0环与3环通信
驱动篇——常规的0环与3环通信

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可

本文来自博客园,作者:寂静的羽夏 ,一个热爱计算机技术的菜鸟

转载请注明原文链接:https://www.cnblogs.com/wingsummer/p/15516178.html

驱动篇——常规的0环与3环通信