内核知识第七讲,内核中设备常用的三种通信方式,以及控制回调的编写

iBinary 2018-01-16 原文

内核知识第七讲,内核中设备常用的三种通信方式,以及控制回调的编写

        内核知识第七讲,内核中设备常用的三种通信方式,以及控制回调的编写

一丶ring3和ring0下的三种通讯方式

ring3和ring0下有常用三种通信方式:

1.缓冲区通信方式

2.直接IO通信方式

3.其它通信方式

缓冲区通信方式

我们的ring3和ring0通讯的时候.ring3会给一个虚拟地址. 然后内核中的参数会通过IRP来获取.

其中有个缓冲区. 我们只要操作这个缓冲区.那么对应的就是操作了三环的缓冲区.

例如:

  

当我们三环和0环通信的时候, 3环如果选择的是缓冲区通信. 那么久类似于上图.  操作系统会在高2G申请一个额外的缓冲区.

然后ring3下的缓冲区拷贝到里面. 然后我们的内核程序操作这个缓冲区之后. 操作系统将这个缓冲区的数据重新写入到ring3下的虚拟缓冲区中.

优点:

  优点是安全.操作系统会创建一个中间层的缓冲区让我们进行操作.然后操作中间层就相当于操作ring3的缓冲区.

缺点:

  高2G内核中的内存是很宝贵的.如果我们交互的时候.传出的数据太大.那么就会消耗计算机内存资源.

如果想看完整流程图,请查看WDK的帮助文档.

2.直接IO通信方式

  为什么叫做直接通信方式. 原因是 ring3可以直接和ring0进行通讯了.不需要额外的缓冲区进行操作.

ring3的虚拟内存会通过内存映射的方式.映射到高2G的内存. 然后ring3的虚拟内存进行保护. 这样我们操作ring0的高2G缓冲区.就相当于操作ring3的缓冲区.

优点:

  如果数据量比较大.可以使用这种

缺点:

  不安全.如果我们的ring3不进行保护.那么通过C语言进行对ring3缓冲区越界访问.那么就相当于访问ring0的物理内存了.

不过幸好.内核中提供了宏让我们自己进行操作.

lpBuff = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);

需要注意的是我们这种通信方式获取的缓冲区不是 IRP中的 SystemBuf;

 

3.其它通信方式

其它通信方式,这是直接使用用户的虚拟内存,也就是IRP中的 userBuf;

 

二丶控制回调的编写

以前我们操作设备的时候. 都是通过Read或者Write去操作.

但是现在我们有控制了. 

这个时候我们要控制设备,就要编写控制码.

控制码:

  

CTL_CODE(FILE_DEVICE_UNKNOWN, (CODE_BASE+(code)), METHOD_BUFFERED, FILE_ANY_ACCESS)

VC++6.0给了一个宏.而NTDDK.h中也有这个. 如果你配置好了环境,那么你就要用VC中提供的了.

控制码的格式:

  

设备类型,控制码,通讯方式. 权限.

如果用我们的上面的宏,则填写即可.内部会自己进行移位运算.

完整代码:

  

 PIO_STACK_LOCATION pIrpStack = NULL;
  PVOID lpBuff = NULL;
  ULONG IoControlCode;            //获取控制的控制码
  ULONG InputBufferLength;         //用户输入的缓冲区,这个缓冲区一般做参数
  ULONG OutputBufferLength;        //ring0返回出去的缓冲区.一般做写出.
  NTSTATUS status = STATUS_UNSUCCESSFUL;//各种变量.暂时不用管.
  ULONG bytes = 0;

  KdBreakPoint();
  
  KdPrint(("[FirstWDK] DispatchControl PID:%d TID:%d\n", PsGetCurrentProcessId(), PsGetCurrentThreadId()));


  pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
  InputBufferLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;//获取长度
  OutputBufferLength = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;//获取输出缓冲区
  IoControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;//获取控制码
  lpBuff = pIrp->AssociatedIrp.SystemBuffer;

  switch(IoControlCode)
  {
  case MYCTL_GET_GDT_SIZE:      //自定义的控制码
    {
      break;
    }
  case MYCTL_GET_GDT:
    {
      PCONTORL_PARAMS pParams = (PCONTORL_PARAMS)lpBuff;
      char szGDT[6];

      if (InputBufferLength < sizeof(CONTORL_PARAMS))
        break;

      __asm
      {
        sgdt szGDT
      }
      KdPrint(("limit:%p  GDT:%p\n", *(short*)szGDT, *(int*)(szGDT + 2)));
      if (OutputBufferLength >= sizeof(szGDT))
      {
        RtlCopyMemory(lpBuff, szGDT, sizeof(szGDT));
        bytes = sizeof(szGDT);
        status = STATUS_SUCCESS;
      }
      break;
    }
  case MYCTL_GET_LDT:
    {
      break;
    }
  }

 
  
  pIrp->IoStatus.Status = status;
  pIrp->IoStatus.Information = bytes;
  IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  
  return status;
}

关于自定义的控制码.

这些本质来说就是我们的宏替换.

#define CODE_BASE 0x800
#define MY_CTL_CODE(code)  CTL_CODE(FILE_DEVICE_UNKNOWN, (CODE_BASE+(code)), METHOD_BUFFERED, FILE_ANY_ACCESS)

#define MYCTL_GET_GDT      MY_CTL_CODE(1)  
#define MYCTL_GET_GDT_SIZE MY_CTL_CODE(2) 
#define MYCTL_GET_LDT      MY_CTL_CODE(3) 

我们每次定义,都要写CTL_CODE.很麻烦.所以我们用心的宏替换一下即可.

PS:

  当控制码为缓冲区方式,直接方式.以及其它方式的时候.我们分别从IRP中获取的参数缓冲区是不同的.

1.当我们的控制码给定的是缓冲区通信方式

  如果是缓冲区通信方式,那么获得的就是IRP中的SystemBuf.   这个缓冲区可以当做ring3传递的参数.然后你往这个缓冲区写数据,则是传出的数据

2.当我们的控制码为直接IO的方式

  如果是直接IO的方式.那么你要二选一. 如果你指定了用户的输入缓冲区为直接IO方式,那么对应的输出缓冲区则是缓冲区方式,

  那么用户缓冲区的获得方式就是使用上面介绍直接方式的API进行获取了.

例如:

  

lpBuff = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress, NormalPagePriority);

3.如果我们的控制码为其它方式

  如果是其它方式,那么用户的缓冲区是 IRP中的UserBuf缓冲区.而输入缓冲区则是用IRP中的.Type3InputBuffer

缓冲区.

 

发表于 2018-01-16 22:40 iBinary 阅读() 评论() 编辑 收藏

 

版权声明:本文为iBinary原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/iBinary/p/8299232.html

内核知识第七讲,内核中设备常用的三种通信方式,以及控制回调的编写的更多相关文章

  1. 1、了解嵌入式系统、arm、内核、驱动、OS、MMU等专有名词

    嵌入式系统:嵌入到对象体中的专用计算机系统 嵌入式系统与通用计算机不同之处:硬件可裁剪 嵌入式系统的特点: 专 […]...

  2. CentOS 7 下编译Linux kernel 内核

    1. 准备编译环境 yum groupinstall "Development Tools"   2. 下载内 […]...

  3. 嵌入式 emmc 中 安装 烧录 内核 kernel,设备树 devicetree ,根文件系统 rootfs

      一般调试嵌入式开发板喜欢选择  利用 TFTP 传送  内核与 设备树,  利用 nfs 加载根文件系统。 […]...

  4. Linux内核源码分析之setup_arch (四)

    前言 Linux内核源码分析之setup_arch (三) 基本上把setup_arch主要的函数都分析了,由 […]...

  5. PHP内核之旅-1.生命周期

    1.SAPI接口 PHP具体应用的编程接口。 2.开始和结束   PHP开始执行以后会经过两个主要的阶段:   […]...

  6. 内核知识第十一讲,门,以及中断和中断异常.

    内核知识第十一讲,门,以及中断和中断异常.        内核知识第十一讲,门,以及中断和中断异常. 一丶TS […]...

  7. centos 内核

    CentOS7设置GRUB系统内核开机选单   方法一:(创建、查看、编辑、用命令设置) #根据/boot/目 […]...

  8. 从 Linux 内核角度探秘 JDK NIO 文件读写本质

    1. 前言笔者在 《从 Linux 内核角度看 IO 模型的演变》一文中曾对 Socket 文件在内核中的相关数据结构为大家做了详尽的阐述。又在此基础之上介绍了针对 socket 文件的相关操作及其对应在内核中的处理流程:并与 epol...

随机推荐

  1. 卸载MySQL(此文转自,仅作学术参考)

    第一步:在控制面板上,进入程序界面。把mysql的软件尽量卸载,不能卸载先暂时放着。 第二步:删除文件夹:pr […]...

  2. JavaWeb项目的目录结构解释(下):

    介绍 在上篇中我们谈到了在IDEA创建JavaWeb时的默认结构,没看的朋友的点这个连接看上篇的文章:http […]...

  3. myeclipse的SVN插件使用

    1、插件安装 下载Subversion的eclipse插件 http://subclipse.tigris.o […]...

  4. 浅析RT1170的功耗管理(1)–概述

      概述单片机的开发过程中,我们会遇到功耗模式管理的问题。通过功耗模式管理,我们可以实现在需要高性能的时候使用 […]...

  5. 基本使用——OkHttp3详细使用教程

    基本使用——OkHttp3详细使用教程 转 https://blog.csdn.net/xx326664162 […]...

  6. EGADS介绍(二)–时序模型和异常检测模型算法的核心思想

    EDADS系统包含了众多的时序模型和异常检测模型,这些模型的处理会输入很多参数,若仅使用默认的参数,那么时序模 […]...

  7. 别对我撒谎,神奇的“微表情”,你来测测看! – 听棠.NET

              无须确凿证据,无须测谎仪,甚至无须声音,只凭借表情的变化就可以判断一个人是否说谎,这是美国 […]...

  8. 【评价指标】详解F1-score与多分类MacroF1&MicroF1

    文章来自:一个宝藏微信公众号【机器学习炼丹术】 基本概念 首先,要背住的几个概念就是:accuracy,pre […]...

展开目录

目录导航