zoukankan      html  css  js  c++  java
  • 内核知识第五讲.驱动框架编写,以及3环和0环通信.

             内核知识第五讲.驱动框架编写,以及3环和0环通信.

    一丶了解内核驱动加载方式

    内核加载方式有两种方式.

    1.动态加载方式.

    2.静态加载方式

    动态加载方式:

      动态态加载方式则是调用3环API 进行代码加载.

    详情请点击 : 内核驱动加载工具的编写.

    静态加载方式

      静态的加载方式则是利用 后缀为.inf  的文件进行加载.

    有关.inf的语法,可以百度或者通过学习WDK中提供的源码例子进行学习.

    动态加载一般很常用.

    二丶驱动框架的介绍.

    在讲解内核驱动框架的是否,我们要先了解设备是什么.  设备和驱动之间的数据关系是什么.

    1.什么是驱动.什么是设备

    驱动:   驱动则是用来操作设备的. 

    设备:   设备则是我们常说的外设. 比如键盘.  显示器.  鼠标等等..

    其中.任何一个驱动操作设备.  都应该提供公共的方法.

    打开.  关闭.  读. 写. 控制....

    如图: 

      用户操作设备的是否. 这个时候会通过内核驱动.提供的 回调方法.(也就是公共的接口)进而来操作设备.

     2.驱动和设备之间的关系.

    驱动和设备之间的关系是一对多的关系.

    驱动可以操作很多设备.

    比如我们的键盘驱动有一个. 但是可以操作的键盘则有很多个. 你键盘坏了.换了很多.但是驱动则没换过.

    所以如果是数据关系的时候.   驱动作为外键放到设备表中.

    例如以下:

      

    设备

    驱动

    A键盘

    标准驱动

    B键盘

    标准驱动

    有了数据关系.那么就可以讲解驱动框架了.

    3.驱动框架的介绍.

    驱动对象.设备对象.

    在驱动中有这两个概念. 

    驱动对象:  简单来说就是一个结构体,存储了驱动的各种信息.

    设备对象: 简单来说也是一个结构体,存储的是设备的各种信息.

    但依据上面的数据关系来说. 设备对象中肯定会存储驱动对象结构体的指针.  驱动对象做外键存储到设备对象中.

    设备对象结构体:

    typedef struct _DRIVER_OBJECT {
        CSHORT Type;          //类型
        CSHORT Size;          //当前结构体大小.内核中任何一个结构体都是这两个成员开头.
    
        //
        // The following links all of the devices created by a single driver
        // together on a list, and the Flags word provides an extensible flag
        // location for driver objects.
        //
    
        PDEVICE_OBJECT DeviceObject;//设备对象指针,存疑?  不是说数据关系是  设备表中有驱动对象吗. 怎么驱动对象表中有设备对象指针.???????
        ULONG Flags;          //通讯协议以及方式.
    
        //
        // The following section describes where the driver is loaded.  The count
        // field is used to count the number of times the driver has had its
        // registered reinitialization routine invoked.
        //
    
        PVOID DriverStart;
        ULONG DriverSize;
        PVOID DriverSection;
        PDRIVER_EXTENSION DriverExtension;
    
        //
        // The driver name field is used by the error log thread
        // determine the name of the driver that an I/O request is/was bound.
        //
    
        UNICODE_STRING DriverName;
    
        //
        // The following section is for registry support.  Thise is a pointer
        // to the path to the hardware information in the registry
        //
    
        PUNICODE_STRING HardwareDatabase;
    
        //
        // The following section contains the optional pointer to an array of
        // alternate entry points to a driver for "fast I/O" support.  Fast I/O
        // is performed by invoking the driver routine directly with separate
        // parameters, rather than using the standard IRP call mechanism.  Note
        // that these functions may only be used for synchronous I/O, and when
        // the file is cached.
        //
    
        PFAST_IO_DISPATCH FastIoDispatch;
    
        //
        // The following section describes the entry points to this particular
        // driver.  Note that the major function dispatch table must be the last
        // field in the object so that it remains extensible.
        //
    
        PDRIVER_INITIALIZE DriverInit;
        PDRIVER_STARTIO DriverStartIo;
        PDRIVER_UNLOAD DriverUnload;
        PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];  //提供的公共方法的接口. 创建.打开,关闭.读写控制... 里面存放的是函数指针.单用户操作设备的是否.则会调用这些回调函数指针.
    
    } DRIVER_OBJECT;
    typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT; 

    存疑部分:

      上面标红的部分. 不是说表关系应该是上面那种吗.?如下图所示

    设备

    驱动

    A键盘

    标准驱动

    B键盘

    标准驱动

    可为何设计为这样.

    原因:

      我们的内核驱动可以操作设备. 但是我们要知道有多少设备怎么办. 所以这里给了一个设备对象的指针. 而不是我们说的数据关系.

    而在设备对象中.存储的则是我们的驱动对象指针.

    而这里的指针.则是一个链表形式的. 为了方便遍历.

    例如:

      

    typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _DEVICE_OBJECT {
        CSHORT Type;
        USHORT Size;
        LONG ReferenceCount;
        struct _DRIVER_OBJECT *DriverObject;  //驱动对象做外键存储
        struct _DEVICE_OBJECT *NextDevice;      //链表.
        struct _DEVICE_OBJECT *AttachedDevice;
        struct _IRP *CurrentIrp;
        PIO_TIMER Timer;
        ULONG Flags;                                // See above:  DO_...
        ULONG Characteristics;                      // See ntioapi:  FILE_...
        __volatile PVPB Vpb;
        PVOID DeviceExtension;
        DEVICE_TYPE DeviceType;
        CCHAR StackSize;
        union {
            LIST_ENTRY ListEntry;
            WAIT_CONTEXT_BLOCK Wcb;
        } Queue;
        ULONG AlignmentRequirement;
        KDEVICE_QUEUE DeviceQueue;
        KDPC Dpc;
    
        //
        //  The following field is for exclusive use by the filesystem to keep
        //  track of the number of Fsp threads currently using the device
        //
    
        ULONG ActiveThreadCount;
        PSECURITY_DESCRIPTOR SecurityDescriptor;
        KEVENT DeviceLock;
    
        USHORT SectorSize;
        USHORT Spare1;
    
        struct _DEVOBJ_EXTENSION  *DeviceObjectExtension;
        PVOID  Reserved;
    
    } DEVICE_OBJECT;
    
    typedef struct _DEVICE_OBJECT *PDEVICE_OBJECT; 

    三丶编写驱动框架.

    上面我们已经简单的了解了驱动对象.设备对象是什么了.那么现在开始编写驱动框架

    步骤

    1.首先注册设备回调函数.当用户对设备进行操作的是否.驱动会调用这个回调函数进行操作.

    2.创建设备.创建虚拟的设备给用户使用.

    3.指定通讯方式. 什么意思?比如ring3中操作设备进行读写的时候 如果用ReadFile读取.那么你们的通讯方式是否是字符串

    4.创建符号连接.

      符号连接: 我们知道.在操作系统下有C盘.D盘一说.但是在驱动下面.则没有怎么一说.只有卷一说.所以我们要绑定一下.

    PS: 鉴于篇幅原因.只写重点.如果想要完整的驱动框架. 请下载资料进行查看.

    1.注册回调函数.

      pDriverObject->MajorFunction[IRP_MJ_CREATE] = 创建的回调函数指针;
      pDriverObject->MajorFunction[IRP_MJ_CLOSE] =  关闭的回调函数指针;
      pDriverObject->MajorFunction[IRP_MJ_READ] =   读取的回调函数指针;
      pDriverObject->MajorFunction[IRP_MJ_WRITE] =  写入的回调函数指针;
      pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = 控制的回调函数指针;

    回调函数的写法.

    NTSTATUS 自定义的函数名(__in struct _DEVICE_OBJECT *DeviceObject,
                          __inout struct _IRP *Irp)
    {
     
      .........
      return STATUS_SUCCESS;
    }

    2.创建虚拟设备.

    创建设备等等.都属于IO操作.

    IO操作创建设备的API

    NTSTATUS 
      IoCreateDevice(
        IN PDRIVER_OBJECT  DriverObject,      //调用者驱动对象的指针.一般是驱动入口的参数
        IN ULONG  DeviceExtensionSize,           //设备扩展大小
        IN PUNICODE_STRING  DeviceName  OPTIONAL, //设备对象名称,注意不是我们ring3下的路径.
        IN DEVICE_TYPE  DeviceType,         //我们的设备类型
        IN ULONG  DeviceCharacteristics,      //驱动的附加信息.
        IN BOOLEAN  Exclusive,            //创建的设备是否其余的可以使用,是否独占
        OUT PDEVICE_OBJECT  *DeviceObject        //传出参数,设备的信息.
        );

    注意红点标注:

      在内核中并没有路径一说.所以这个路径是特殊的.

    UNICODE_STRING 内核中新的字符串格式.其实是一个结构体.系统提供了操作这种结构体的API

    我们拼接一个路径

    UNICODE_STRING  uStrDeviceName;

    RtlInitUnicodeString(&uStrDeviceName,L"\Device\xxx名字即可");

    注意,创建设备的时候.我们前边需要加上 Device.  这里因为是转义字符.所以加了好多\

    拼接好则可以给了.

    status = IoCreateDevice(pDriverObject, 
                              0, 
                              &ustrDeviceName,          //设备路径
                              FILE_DEVICE_UNKNOWN,//设备类型设置为不知道
                              FILE_DEVICE_SECURE_OPEN, 
                              FALSE,                           //是否独占
                              &pDevObj);
      if (status != STATUS_SUCCESS)
      {
        return status;
      }        

    3.设置通讯方式.

    pDevObj->Flags |= DO_BUFFERED_IO; //指定通讯方式,为缓冲区

    4.创建符号连接

    我们创建的符号连接.可以通过 Win0bj工具进行查看. 这个工具可以查看所有设备.但是只有0环才可以操作.

     status = IoCreateSymbolicLink(&g_ustrSymbolName, &ustrDeviceName);
      if (status != STATUS_SUCCESS)
      {
        //删除设备
        IoDeleteDevice(pDevObj);
        return status;
      }

    注意符号连接名字.我们也是初始化得出的.

    RtlInitUnicodeString(&g_ustrSymbolName, L"\DosDevices\xxxx名字");

    完整的框架已经写完.剩下的就是 三环和0环驱动通讯. 依赖我们写的框架.

    四丶三环和0环的通讯.

    三环操作设备的API就是 CreateFile ReadFile.....等等.不做介绍了.

    利用三环程序.操作我们的设备.从中读取内容.

    HANDLE hFile = CreateFile("\\?\符号连接名称", 
                 GENERIC_WRITE | GENERIC_READ, 
                 0, 
                 NULL, 
                 OPEN_EXISTING, 
                 FILE_ATTRIBUTE_NORMAL, 
                 NULL);

    打开我们的设备.注意文件名并不是路径.而是我们绑定的符号连接. 这里我们可以写成\?

    读取内容.

    char szBuf[10];
    ReadFile(hFile, szBuff, sizeof(szBuff), &dwBytes, NULL)

    请注意,当读取设备的是否.我们一开始注册的回调函数就会来. 这时候我们给它什么都可以了.

    但是此时需要讲解一下通讯协议.

    当读取的是否,回调函数会来. 然后操作系统会填写 struct _IRP 结构体.用来和我们的零环通信.

    typedef struct _IRP {
      .            //省略了两个成员,这两个成员一个是类型.一个是大小.
      .
      PMDL  MdlAddress;
      ULONG  Flags;
      union {
        struct _IRP  *MasterIrp;
        .
        .
        PVOID  SystemBuffer;//ring3下的缓冲区.操作系统会填写.我们给里面填写什么内容.那么ring3就读取到什么.
      } AssociatedIrp;
      .
      .
      IO_STATUS_BLOCK  IoStatus;
      KPROCESSOR_MODE  RequestorMode;
      BOOLEAN PendingReturned;
      .
      .
      BOOLEAN  Cancel;
      KIRQL  CancelIrql;
      .
      .
      PDRIVER_CANCEL  CancelRoutine;
      PVOID UserBuffer;
      union {
        struct {
        .
        .
        union {
          KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
          struct {
            PVOID  DriverContext[4];
          };
        };
        .
        .
        PETHREAD  Thread;
        .
        .
        LIST_ENTRY  ListEntry;
        .
        .
        } Overlay;
      .
      .
      } Tail;
    } IRP, *PIRP;

    我们有了缓冲区,但是不知道缓冲区的大小.这个是否需要通过IO函数.从当前栈中获取参数.

    IoGetCurrentIrpStackLocation(pIrp)

    返回当前IRP的栈.我们从返回值中获取参数即可.

    操作完成之后,我们完成IRP请求即可.这个IRP请求主要是为了多线程使用.有的时候可能在读取的是否.线程就切换了.

    ring0下的读取回调完整编写.

     //获取当前irp堆栈
      PIO_STACK_LOCATION pIrpStack = NULL;
      PVOID lpBuff = NULL;
      ULONG Length = 0;
    
      //PsGetCurrentThreadId();
      KdBreakPoint();
    
      __try
      {
        pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
        //check
    
        lpBuff = pIrp->AssociatedIrp.SystemBuffer;
        Length = pIrpStack->Parameters.Read.Length;
        
        KdPrint(("[FirstWDK] DispatchRead
    "));
        
        RtlStringCbCopyA(lpBuff, Length, "Hello World!");
        //check
    
    
        pIrp->IoStatus.Status = STATUS_SUCCESS;
        pIrp->IoStatus.Information = 6;
        
        //完成Irp请求
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
        //check
      }
      __except(1)
      {
      }
    
    
      return STATUS_SUCCESS;

    课堂3环和0环的完整代码资料:

      链接:https://pan.baidu.com/s/1edffGy 密码:vpo0

    原创不易,转载请注明出处.

  • 相关阅读:
    Atitit Server Side Include  ssi服务端包含规范 csi  esi
    Atitit 动态按钮图片背景颜色与文字组合解决方案
    Atitit 图像扫描器---基于扫描线
    Atitit usrqbg1821 Tls 线程本地存储(ThreadLocal Storage 规范标准化草案解决方案ThreadStatic
    Atitit usrqbg1834 html的逻辑化流程化 规范标准化解决方案
    Atitit  J2EE平台相关规范--39个  3.J2SE平台相关规范--42个
    Atitit jsr规范化分类 attilax总结
    Atitit attilax总结的对于attilax重要的jsr规范,以及需要增加的jsr规范
    Atitit usbQb212 oo 面向对象封装的标准化与规范解决方案java c# php js
    Atitit jsr规范有多少个  407个。Jsr规范大全
  • 原文地址:https://www.cnblogs.com/iBinary/p/8283642.html
Copyright © 2011-2022 走看看