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

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. Kernel 内核调试

    本机环境 Win7 + VMware 14 Pro 1.安装Qemu,Ubuntu包管理器中的二进制版本比较老 […]...

  2. x64内核HOOK技术之拦截进程.拦截线程.拦截模块

    x64内核HOOK技术之拦截进程.拦截线程.拦截模块             x64内核HOOK技术之拦截进程 […]...

  3. 羽夏看Linux内核——简述

    写在前面此系列是本人一个字一个字码出来的,包括示例和实验截图。如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事...

  4. 跟厂长学PHP7内核(七):常见变量类型的基本结构

    上篇文章讲述了变量的存储结构zval,今天我们就来学习一下几个常见变量类型的基本结构。 一、类型一览 zval […]...

  5. 羽夏看Linux内核——引导启动(上)

    写在前面此系列是本人一个字一个字码出来的,包括示例和实验截图。如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事...

  6. 跟厂长学PHP内核(四):生命周期之开始前的躁动

    上一章我们对PHP的源码目录结构有了初步了解,本章我们继续从生命周期的维度对PHP进行剖析。 一、概览 生命周 […]...

  7. linux内核

    内核编程常常看起来像是黑魔法,而在亚瑟 C 克拉克的眼中,它八成就是了。Linux内核和它的用户空间是大不相同的:抛开漫不经心,你必须小心翼翼,因为你编程中的一个bug就会影响到整个系统。浮点运算做起来可不容易,堆栈固定而狭小,而你写的代...

  8. Linux内核模块(.ko文件)

    目录Linux内核模块简介模块的基本概念一个最简单的Linux内核模块printk与printflsmod命令modprobe命令Linux内核模块程序结构模块加载函数模块卸载函数模块参数导出符号模块声明与描述模块的使用计数模块的编译参考...

随机推荐

  1. 俄罗斯方块(一):简版

    编程语言:python(3.6.4) 主要应用的模块:pygame   首先列出我的核心思路: 1,图像由“核 […]...

  2. Pyinstaller 打包程序为可执行文件exe

    Pyiinstaller打包 pyinstaller是python的一个第三方模块,使用它可以将pythnon […]...

  3. 阿里云Redis加速Typecho博客访问

    写在开始   一不小心,博主趁着阿里云搞活动,一口气把Redis服务续费了3年(到期时间:2021-05-03 […]...

  4. 把手机当扫描枪用

    下载链接 1.Android下载无线条码扫描器安卓版安装 2.下载PC端SeriesScan软件 3.pc端打 […]...

  5. OpenCV学习笔记(二) cv::Mat – eric.xing

    OpenCV学习笔记(二) cv::Mat Mat的 特性 、属性 与 地址计算方法。 部分内容转自:Open […]...

  6. Python数据分析之pandas学习

    Python中的pandas模块进行数据分析。 接下来pandas介绍中将学习到如下8块内容:1、数据结构简介 […]...

  7. Bitter.NotifyOpenPaltform : HTTP 异步消息接收调度中心–开源贡献 之 一:简介

    现在互联网的系统越来越趋向于复杂,从单体系统到现在的微服务体系演变。公司与公司的分工也越来越明确。 大数据公司 […]...

  8. Python新手入门基础

    认识 Python 人生苦短,我用 Python —— Life is short, you need Pyt […]...

展开目录

目录导航