zoukankan      html  css  js  c++  java
  • 驱动开发之 用DeviceIoControl实现应用程序与驱动程序通信

    Ring3测试程序:http://blog.csdn.net/zj510/article/details/8216321

    1.readfile和writefile可以实现应用程序与驱动程序通信,另外一个Win32 API 是DeviceIoControl。

    应用程序自定义一中IO控制码,然后调用DeviceIoControl函数,IO管理器会产生一个MajorFunction 为IRP_MJ_DEVICE_CONTROL,MinorFunction 为自己定义的控制码的IRP,系统就调用相应的处理IRP_MJ_DEVICE_CONTROL的派遣函数,你在派遣函数中判断MinorFunction ,是自定义的控制码你就进行相应的处理。

    2.首先介绍一下DeviceIoControl函数

     1 BOOL WINAPI DeviceIoControl(
     2   _In_         HANDLE hDevice,      //已经打开的设备句柄
     3   _In_         DWORD dwIoControlCode,//自定义的控制码,稍后介绍怎么定义
     4   _In_opt_     LPVOID lpInBuffer,    //输入缓冲区
     5   _In_         DWORD nInBufferSize,  //输入缓冲区的大小
     6   _Out_opt_    LPVOID lpOutBuffer,   //输出缓冲区
     7   _In_         DWORD nOutBufferSize, //输出缓冲区的大小
     8   _Out_opt_    LPDWORD lpBytesReturned, //实际返回的字节数,对应驱动程序中pIrp->IoStatus.Information。
     9   _Inout_opt_  LPOVERLAPPED lpOverlapped //重叠操作结构指针。同步设为NULL,DeviceIoControl将进行阻塞调用;否则,应在编程时按异步操作设计
    10 );

    例如:

    1 UCHAR InputBuffer[10];
    2 UCHAR OutputBuffer[10];
    3 
    4 //将输入缓冲区全部置成0XBB
    5 memset(InputBuffer,0xBB,10);
    6 DWORD dwOutput;
    7 //输入缓冲区作为输入,输出缓冲区作为输出
    8 BOOL bRet = DeviceIoControl(hDevice, IOCTL_TEST, InputBuffer, 10, OutputBuffer, 10, &dwOutput, (LPOVERLAPPED)NULL);

    3.定义IO控制码

    #define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)

    IOCTL_Device_Function:生成的IRP的MinorFunction 

    DeviceType:设备对象的类型。设备类型可参考:http://blog.csdn.net/liyun123gx/article/details/38058965

    Function :自定义的IO控制码。自己定义时取0x800到0xFFF,因为0x0到0x7FF是微软保留的。

    Method :数据的操作模式。

                  METHOD_BUFFERED:缓冲区模式

                  METHOD_IN_DIRECT:直接写模式

                  METHOD_OUT_DIRECT:直接读模式

                  METHOD_NEITHER :Neither模式

    Access:访问权限,可取值有:

                FILE_ANY_ACCESS:表明用户拥有所有的权限

                FILE_READ_DATA:表明权限为只读

                FILE_WRITE_DATA:表明权限为可写

                也可以 FILE_WRITE_DATA | FILE_READ_DATA:表明权限为可读可写,但还没达到FILE_ANY_ACCESS的权限。

    例如:#define IOCTL_TEST     CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

    4.下面介绍不同的数据操作模式

    (1).METHOD_BUFFERED:缓冲区模式

    用户提供的输入缓冲区的内容被复制到IRP中的pIrp->AssociatedIrp.SystemBuffer内存地址,复制的长度是DeviceIoControl指定的输入字节数。

    驱动程序输出数据时,还可以向pIrp->AssociatedIrp.SystemBuffer中写入,操作系统会将此地址的数据复制到DeviceIoControl的输出缓冲区。

    复制的字节数通过设置pIrp->IoStatus.Information来指定。

    派遣函数中通过下面代码得到输入缓冲区输出缓冲的大小以及IOCTL

    1 //得到当前堆栈
    2 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
    3 //得到输入缓冲区大小
    4 ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
    5 //得到输出缓冲区大小
    6 ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
    7 //得到IOCTL码
    8 ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;

    通过操作pIrp->AssociatedIrp.SystemBuffer来进行数据的输入输出

     1 UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
     2 for (ULONG i=0;i<cbin;i++)
     3 {
     4     KdPrint(("%X
    ",InputBuffer[i]));
     5 }
     6 
     7 //操作输出缓冲区,输出缓冲区和输入缓冲区是一个缓冲区
     8 UCHAR* OutputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
     9 memset(OutputBuffer,0xAA,cbout);
    10 //设置实际操作输出缓冲区长度
    11  pIrp->IoStatus.Information = cbout;        

    (2) METHOD_IN_DIRECT与METHOD_OUT_DIRECT  直接内存模式

    与缓冲模式相同,用户提供的输入缓冲区的内容被复制到IRP中的pIrp->AssociatedIrp.SystemBuffer内存地址,复制的长度是DeviceIoControl指定的输入字节数。

    直接内存模式中,操作系统会将DeviceIoControl指定的输出缓冲区锁定,然后在内核模式地址下重新映射一段地址。

    派遣函数中IRP中的pIrp->MdlAddress记录DeviceIoControl指定的输出缓冲区。派遣函数应该使用MmGetSystemAddressForMdlSafe将这段内存映射到内核模式下的内存地址。

    得到输入输出缓冲区的大小以及IOCTL的方式与缓冲区模式相同。

    另外需要注意CTL_CODE设置的权限问题,若以只读方式打开设备,METHOD_IN_DIRECT的IOCTL操作会失败。

    派遣函数中处理直接内存模式:

     1 //显示输入缓冲区数据
     2 UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
     3 for (ULONG i=0;i<cbin;i++)
     4 {
     5     KdPrint(("%X
    ",InputBuffer[i]));
     6 }
     7 //pIrp->MdlAddress为DeviceIoControl输出缓冲区地址相同
     8 KdPrint(("User Address:0X%08X
    ",MmGetMdlVirtualAddress(pIrp->MdlAddress)));
     9 UCHAR* OutputBuffer = (UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
    10 //InputBuffer被映射到内核模式下的内存地址,必定在0X80000000-0XFFFFFFFF之间
    11 memset(OutputBuffer,0xAA,cbout);

    (3)METHOD_NEITHER :Neither模式

    因为此模式直接访问用户模式地址,这是很危险的,所以此模式很少被用到。

    使用用户模式地址必须保证调用DeviceIoControl 的线程与派遣函数运行在同一个线程上下文中。

    派遣函数得到输入缓冲区的方式与前两种不同,此模式是通过IO堆栈的stack->Parameters.DeviceIoControl.Type3InputBuffer;得到输入缓冲区。

    驱动通过pIrp->UserBuffer得到输出缓冲区。

    得到输入输出缓冲区的长度与IOCTL的方式与前两种相同。

    由于驱动程序的派遣函数不能保证传递进来的用户地址是合法地址,所以要对传入的用户模式地址进行可读写判断。这就需要ProbeForRead函数和ProbeForWrite函数与_try _execpt 结合使用。

    下面是驱动派遣函数中Neither模式

     1 //显示输入缓冲区数据
     2 UCHAR* UserInputBuffer = (UCHAR*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
     3 KdPrint(("UserInputBuffer:0X%0X
    ",UserInputBuffer));
     4 
     5 //得到用户模式地址
     6 PVOID UserOutputBuffer = pIrp->UserBuffer;
     7 KdPrint(("UserOutputBuffer:0X%0X
    ",UserOutputBuffer));
     8 
     9 __try
    10 {
    11     KdPrint(("Enter __try block
    "));
    12 
    13     //判断指针是否可读
    14     ProbeForRead(UserInputBuffer,cbin,4);
    15     //显示输入缓冲区内容
    16     for (ULONG i=0;i<cbin;i++)
    17     {
    18         KdPrint(("%X
    ",UserInputBuffer[i]));
    19     }
    20 
    21     //判断指针是否可写
    22     ProbeForWrite(UserOutputBuffer,cbout,4);
    23 
    24     //操作输出缓冲区
    25     memset(UserOutputBuffer,0xAA,cbout);
    26 
    27     //如果在上面引发异常,所以以后语句不会被执行!
    28     pIrp->IoStatus.Information = cbout;
    29 
    30     KdPrint(("Leave __try block
    "));
    31 }
    32 __except(EXCEPTION_EXECUTE_HANDLER)
    33 {
    34     KdPrint(("Catch the exception
    "));
    35     KdPrint(("The program will keep going
    "));
    36     status = STATUS_UNSUCCESSFUL;
    37 }
    38 
    39 pIrp->IoStatus.Information = cbout;
  • 相关阅读:
    你所不知道的mfc…mfc项目索引 &mfc调优指南 &mfc vc添加添加子功能指南
    Cu 大彻大悟内存管理 mm (update 0410)
    [转]Linux iostat监测IO状态
    linux virtual memory layout by moniskiller upload [读书笔记]
    河畔找到的 面经笔经
    【转】Linux本地磁盘(硬盘)介绍
    读写UTF8、Unicode文件
    codesmith执行时提示“调用的目标发生了异常”的处理过程经验。
    DB2表信息以及字段信息的表
    iBatis.NET获取resultMap相关数据
  • 原文地址:https://www.cnblogs.com/luzhiyuan/p/4385358.html
Copyright © 2011-2022 走看看