[分享] DeviceIoControl解读

[复制链接]
  • TA的每日心情
    开心
    2014-4-2 21:30
  • 签到天数: 3 天

    [LV.2]九品芝麻官

    534

    主题

    400

    回帖

    7742

    积分

    Intohard Team

    Rank: 7Rank: 7Rank: 7

    积分
    7742
    发表于 2012-4-20 09:19:13 | 显示全部楼层 |阅读模式
    设备驱动程序可以被当作内核模式函数包来看待,I/O控制代码就是用来指定访问其中的哪个函数的。DeviceIoControl函数的dwIoControlCode参数就是这个代码,它指出了我们需要进行的操作,以及如何进行操作。
    控制代码是32位数字型常量,可以CTL_CODE宏来定义,它们定义在winioctl.inc和ntddk.inc文件中。

    控制代码中各数据位字段的含义如下:
    ◎ DeviceType--设备类型(16bit)指出了设备的类型,微软保留了0-7FFFh的取值,剩下的8000h-0FFFFh供开发商定义新的内核模式驱动程序。我们可以在\include\w2k\ntddk.inc文件中找到一组FILE_DEVICE_XXX符号常量,这些值都是微软保留的值,我们可以使用其中的FILE_DEVICE_UNKNOWN。当然你也可以定义另外一个FILE_DEVICE_XXX值
    ◎ Access--存取代码(2bit)指明应用程序存取设备的方式,由于这个字段只有2位,所以只有4种可能性:
    · FILE_ANY_ACCESS (0)--最大的存取权限,就是什么操作都可以
    · FILE_READ_ACCESS (1)--读权限,设备将数据传递到指定的缓冲区
    · FILE_WRITE_ACCESS (2)--写权限,可以从内存中向设备传递数据
    · FILE_READ_ACCESS or FILE_WRITE_ACCESS (3)--读写权限,设备和内存缓冲区之间可以互相传递数据
    ◎ Function--功能代码(12bit)用来描述要进行的操作,我们可以用800h-0FFFh来定义自己的I/O控制代码,0-7FFh之间的值是被微软保留的,用来定义公用的I/O控制代码
    ◎ Method--缓冲模式(2bit)表示I/O管理器如何对输入和输出的数据进行缓冲,这个字段的长度是2位,所以有4种可能性:
    · METHOD_BUFFERED (0)--对I/O进行缓冲
    · METHOD_IN_DIRECT (1)--对输入不进行缓冲
    · METHOD_OUT_DIRECT (2)--对输出不进行缓冲
    · METHOD_NEITHER (3)--都不缓冲

    缓冲模式的管理我们会在后面进行更详细的讨论,当前最重要的是,虽然进行缓冲会带来一些额外的内存开销,但却是最安全的,因为系统已经做好了相关的全部工作。在传输的数据小于一页(4Kb)的时候,驱动程序通常使用缓冲方式的I/O,因为对大量小块内存进行内存锁定带来的开销也是很大的。在 VirtToPhys驱动程序中,我们使用带缓冲的方式。
    读者可以手工去定义I/O控制代码,但是使用CTL_CODE宏会方便得多,它提供了创建IOCTL值的算法,具体如下:

    CTL_CODE MACRO DeviceType:= <0> , Function:= <0> , Method:= <0> , Access:= <0>
    EXITM %(((DeviceType) SHL 16) OR ((Access) SHL 14) OR ((Function) SHL 2) OR (Method))
    ENDM

    CTL_CODE宏在winioctl.inc文件和ntddk.inc文件中都有定义。

    在应用程序里定义的CTL_CODE和驱动中IRP_MJ_DEVICE_CONTROL的函数里控制码对应即可。在驱动程序里取出码值进行对比操作,如:
       dwIoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
        switch (dwIoControlCode)
        {
         // 我们约定,缓冲区共两个DWORD,第一个DWORD为端口,第二个DWORD为数据
         // 一般做法是专门定义一个结构,此处简单化处理了
         case IOCTL_MYPORT_READ_BYTE:   // 从端口读字节
          pvIOBuffer[1] = _inp(pvIOBuffer[0]);
          Irp->IoStatus.Information = 8;   // 输出长度为8
          break;
         case IOCTL_MYPORT_WRITE_BYTE:   // 写字节到端口
          _outp(pvIOBuffer[0], pvIOBuffer[1]);
          break;
         default:   // 不支持的IOCTL
          Irp->IoStatus.Status = STATUS_INVALID_PARAME



    应用程序和驱动程序的通信过程是:应用程序使用CreateFile函数打开设备,然后用DeviceIoControl与驱动程序进行通信,包括读和写两种操作。还可以用ReadFile读数据用WriteFile写数据。操作完毕时用CloseHandle关闭设备。我们比较常用的就是用DeviceIoControl对设备进行读写操作。先看看DeviceIoControl是怎么定义的:

    BOOL DeviceIoControl( HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped );
    Parameters(参数)
    hDevice (CreateFile返回的设备句柄)
    [in] Handle to the device that is to perform the operation. To obtain a device handle, call the CreateFile function.
    dwIoControlCode (应用程序调用驱动程序的控制命令,就是IOCTL_XXX IOCTLs )
    [in] IOCTL for the operation. This value identifies the specific operation to perform and the type of device on which to perform the operation. There are no specific values defined for the dwIoControlCode parameter. However, you can define custom IOCTL_XXX IOCTLs with the CTL_CODE macro. You can then advertise these IOCTLs and an application can use these IOCTLs with DeviceIoControl to perform the driver-specific functions.
    lpInBuffer (应用程序传递给驱动程序的数据缓冲区地址)
    [in] Long pointer to a buffer that contains the data required to perform the operation. Set to NULL if the dwIoControlCode parameter specifies an operation that does not require input data.
    nInBufferSize (应用程序传递给驱动程序的数据缓冲区大小,字节数)
    [in] Size, in bytes, of the buffer pointed to by lpInBuffer.
    lpOutBuffer (驱动程序返回给应用程序的数据缓冲区地址)
    [out] Long pointer to a buffer that receives the output data for the operation. Set to NULL if the dwIoControlCode parameter specifies an operation that does not produce output data.
    nOutBufferSize (驱动程序返回给应用程序的数据缓冲区大小,字节数)
    [out] Size, in bytes, of the buffer pointed to by lpOutBuffer.
    lpBytesReturned (驱动程序实际返回给应用程序的数据字节数地址)
    [out] Long pointer to a variable that receives the size, in bytes, of the data stored in lpOutBuffer. The DeviceIoControl function may unnecessarily use this parameter. For example, if an operation does not produce data for lpOutBuffer and lpOutBuffer is NULL, the value of lpBytesReturned is meaningless.
    lpOverlapped (重叠操作结构)
    [in] Ignored; set to NULL.
    Return Values(返回值)
    Nonzero indicates success. Zero indicates failure. To obtain extended error information, call the GetLastError function. (非0成功,0失败)

    具体使用我们看看列子:

    1,向设备传递数据,我们定义一个函数来实现
    1. bool CDeviceOperDlg::SendKeyData(HANDLE handle, BYTE *bData, int iSize)
    2. {
    3. ULONG nOutput;
    4. BYTE bTemp[512];

    5. //将数据放置到发送数组
    6. memset(bTemp,0,sizeof(bTemp));
    7. memcpy(bTemp,&bData[0],iSize);
    8. //向设备发送
    9. if (!DeviceIoControl(handle,        
    10.        ATST2004_IOCTL_WRITE,     //根据具体的设备有相关的定义
    11.        bTemp,                                        //向设备传递的数据地址
    12.        iSize,                                            //数据大小,字节数
    13.        NULL,                                          //没有返回的数据,置为NULL
    14.        0,                                                  //没有返回的数据,置为0

    15.        &nOutput,
    16.        NULL)
    17.     )
    18. {
    19.    return false;
    20. }

    21. return true;
    22. }
    复制代码
    2,从设备读取数据
    1. bool CDeviceOperDlg::ReviceKeyData(HANDLE handle, BYTE *bData, int iSize)
    2. {

    3. ULONG nOutput;
    4. BYTE bTemp[512];
    5. //数组清零
    6. memset(bTemp,0,sizeof(bTemp));
    7. //向设备发送
    8. if (!DeviceIoControl(handle,
    9.        ATST2004_IOCTL_READ,           //根据具体的设备有相关的定义
    10.        NULL,                                              //没有向设备传递的数据,置为NULL
    11.        0,                                                      //没有向设备传递的数据,置为NULL
    12.        bTemp,                                            //读取设备的数据返回地址
    13.        iSize,                                                //读取数据的字节数
    14.        &nOutput,
    15.        NULL)
    16.     )
    17. {
    18.    return false;
    19. }
    20. //放置到公用数组
    21. memcpy(&bData[0],&bTemp[0],iSize);
    22. return true;
    23. }
    复制代码

    该用户从未签到

    0

    主题

    14

    回帖

    48

    积分

    [INTOHARD]工兵

    Rank: 1

    积分
    48
    发表于 2012-6-28 00:07:23 | 显示全部楼层
    应用起来好难啊,我为了写这个头发都掉完了
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    快速回复 返回顶部 返回列表