与驱动程序通信的函数,除了ReadFile和WriteFile函数还有DeviceIoControl函数,而且DeviceIoControl函数那是相当的彪悍。因为它可以自定义控制码,你只要在IRP_MJ_DEVICE_CONTROL对应的派遣函数中读取控制码,然后针对控制码,你就可以实现自定义的功能了。
函数原型:
BOOL WINAPI DeviceIoControl(
__in HANDLEhDevice,
__in DWORDdwIoControlCode,
__in_opt LPVOID lpInBuffer,
__in DWORDnInBufferSize,
__out_opt LPVOID lpOutBuffer,
__in DWORDnOutBufferSize,
__out_opt LPDWORD lpBytesReturned,
__inout_opt LPOVERLAPPED lpOverlapped
);
其中lpBytesReturned的值来自于IRP结构中的pIRP->IoStatus.Information。DeviceIoControl的第二个参数就是控制码,控制码是一个32为无符号整型,需要符合DDK的规定。
控制代码中各数据位字段的含义如下:
◎ DeviceType--设备类型(31-16bit)指 出了设备的类型,微软保留了0-7FFFh的取值,剩下的8000h-0FFFFh 供开发商定义新的内核模式驱动程序。我们可以在\include\w2k\ntddk.inc文件中找到一组FILE_DEVICE_XXX 符号常量,这些值都是微软保留的 值,我们可以使用其中的FILE_DEVICE_UNKNOWN。当然你也可以定义另外一个FILE_DEVICE_XXX值
◎ Access--存取代码(15-14bit)指明应用程序存取设备的方式,由于这个字段只有2位,所以只有4种可能性:
· FILE_ANY_ACCESS (0)--最大的存取权限,就是什么操作都可以
· FILE_READ_ACCESS (1)--读权限,设备将数据传递到指定的缓冲区
· FILE_WRITE_ACCESS (2)--写权限,可以从内存中向设备传递数据
· FILE_READ_ACCESS or FILE_WRITE_ACCESS (3)--读写权限,设备和内存缓冲区之间可以互相传递数据
◎ Function--功能代码(13-2bit)用来描述要进行的操作,我们可以用800h-0FFFh来定义自己的I/O控制代码,
0-7FFh之间的值是被微软保留的,用来定义公用的I/O控制代码
◎ Method--缓冲模式(0-1bit)表示I/O管理器如何对输入和输出的数据进行缓冲,这个字段的长度是2位,所以有
4种可能性:
·METHOD_BUFFERED (0)--对I/O进行缓冲
·METHOD_IN_DIRECT (1)--对输入不进行缓冲
·METHOD_OUT_DIRECT (2)--对输出不进行缓冲
·METHOD_NEITHER (3)--都不缓冲
缓冲区模式虽然会损失点性能,但是其安全性好。
下面将分别讲述这几种模式。
缓冲内存模式(对应代码中的IOCTL_TEST1)
首先要将控制码中的Method设置为METHOD_BUFFERED。
往驱动中Input数据:在Win32 APIDeviceIoControl函数的内部,用户提供的输入缓冲区的内容被复制到IRP的pIRP->AssociatedIrp.SystemBuffer的内存地址,复制的字节是有DeviceControl指定的输入字节数。从驱动中Output数据:派遣函数可以向pIRP->AssociatedIrp.SystemBuffer写入数据,被当做是设备输出的数据。操作系统会将AssociatedIrp.SystemBuffer的数据再次复制到DeviceIoControl提供的输出缓冲区,复制的字节数有pIrp->IoStatus.Information指定,DeviceIoControl也可以通过参数lpBytesReturned得到复制的字节数。
原理就是这样了,理论上就可以实现读和写的双向操作了。
直接内存模式(对应代码中的IOCTL_TEST2)
首先将Method设置为METHOD_IN_DIRECT 或METHOD_OUT_DIRECT ,这两者的不同只是体现在打开设备的权限上,当以只读权限打开设备时,METHOD_IN_DIRECT 就可以顺利操作,而METHOD_OUT_DIRECT 就会失败。如果以读写权限打开时,两者都可以执行成功。
往驱动中Input数据:这部分和上面的缓冲内存模式一样,输入缓冲区的数据复制到pIrp->AssociateIrp.SystemBuffer内存地址,复制的字节数是按照DeviceIoControl指定的。
从驱动中Output数据:操作系统会为DeviceIoControl指定的输出缓冲区锁定,然后在内核模式地址下重新映射到一段地址。在派遣函数中可以先获取DeviceIoControl指定的输出缓冲区(lpOutBufferb被记录在pIrp->AssociateIrp.SystemBuffer),然后再通过MmGetSystemAddressForMdlSafe获取其在核地址中的映射值。
其他内存模式(对应代码中的IOCTL_TEST3)
个人觉得这种方式挺麻烦的而且少被用到,由于它是直接访问用户模式地址,要求调用DeviceIoControl的线程和派遣函数运行在同一个线程设备上下文中,自己有个印象就行了。
首先将指定的Method参数设置为METHOD_NEITHER。
往驱动中Input数据:通过I/O堆栈的Parameters.DeviceIoControl.Type3InputBuffer得到DeviceIoControl提供的输入缓冲区地址,Parameters.DeviceIoControl.InputBufferLength得到其长度。由于不能保证传递过来的地址合法,所以需要先要结果ProbeRead函数进行判断。
从驱动中Output数据:通过pIrp->UserBuffer得到DeviceIoControl函数提供的输出缓冲区地址,再通过Parameters.DeviceIoControl.OutputBufferLength得到输出缓冲区大小。同样的要用ProbeWrite函数先进行判断。
下面给出一个实例代码,来自于张帆的《Windows驱动开发详解》
首先是控制码设置:
- #define IOCTL_TEST1 CTL_CODE(\
-
- FILE_DEVICE_UNKNOWN, \
-
- 0x800, \
-
- METHOD_BUFFERED, \
-
- FILE_ANY_ACCESS)
-
-
-
- #define IOCTL_TEST2 CTL_CODE(\
-
- FILE_DEVICE_UNKNOWN, \
-
- 0x801, \
-
- METHOD_IN_DIRECT, \
-
- FILE_ANY_ACCESS)
-
-
-
- #define IOCTL_TEST3 CTL_CODE(\
-
- FILE_DEVICE_UNKNOWN, \
-
- 0x802, \
-
- METHOD_NEITHER, \
-
- FILE_ANY_ACCESS)
-
-
-
- 再是IRP_MJ_DEVICE_CONTROL派遣函数:
-
- NTSTATUSIOCTRLDRIVER_DispatchDeviceControl(
-
- IN PDEVICE_OBJECT DeviceObject,
-
- IN PIRP pIrp
-
- )
-
- {
-
- NTSTATUS status = STATUS_SUCCESS;
-
-
-
- PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
-
-
-
- ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
-
-
-
- ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
-
-
-
- ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
-
- ULONG info = 0;
-
- switch(code)
-
- {
-
- case IOCTL_TEST1:
-
- {
-
- KdPrint(("zhui:IOCTL_TEST1\n"));
-
-
-
- UCHAR* InputBuffer =(UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
-
- for (ULONGi=0;i
-
- {
-
- KdPrint(("zhui:%X\n",InputBuffer[i]));
-
- }
-
-
-
-
-
- UCHAR* OutputBuffer =(UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
-
- memset(OutputBuffer,0xAA,cbout);
-
-
-
- info = cbout;
-
- break;
-
- }
-
- case IOCTL_TEST2:
-
- {
-
- KdPrint(("zhui:IOCTL_TEST2\n"));
-
-
-
- UCHAR* InputBuffer =(UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
-
- for (ULONGi=0;i
-
- {
-
- KdPrint(("zhui:%X\n",InputBuffer[i]));
-
- }
-
-
-
-
-
- KdPrint(("zhui:UserAddress:0X%08X\n",MmGetMdlVirtualAddress(pIrp->MdlAddress)));
-
-
-
- UCHAR* OutputBuffer =(UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
-
-
-
- memset(OutputBuffer,0xAA,cbout);
-
-
-
- info = cbout;
-
- break;
-
- }
-
- case IOCTL_TEST3:
-
- {
-
- KdPrint(("zhui:IOCTL_TEST3\n"));
-
-
-
- UCHAR* UserInputBuffer= (UCHAR*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
-
- KdPrint(("zhui:UserInputBuffer:0X%0X\n",UserInputBuffer));
-
-
-
-
-
- PVOID UserOutputBuffer= pIrp->UserBuffer;
-
-
-
- KdPrint(("zhui:UserOutputBuffer:0X%0X\n",UserOutputBuffer));
-
-
-
- __try
-
- {
-
- KdPrint(("zhui:Enter __try block\n"));
-
-
-
-
-
- ProbeForRead(UserInputBuffer,cbin,4);
-
-
-
- for (ULONGi=0;i
-
- {
-
- KdPrint(("zhui:%X\n",UserInputBuffer[i]));
-
- }
-
-
-
-
-
- ProbeForWrite(UserOutputBuffer,cbout,4);
-
-
-
-
-
- memset(UserOutputBuffer,0xAA,cbout);
-
-
-
-
-
- info = cbout;
-
-
-
- KdPrint(("zhui:Leave __try block\n"));
-
- }
-
- __except(EXCEPTION_EXECUTE_HANDLER)
-
- {
-
- KdPrint(("zhui:Catch the exception\n"));
-
- KdPrint(("zhui:The program will keep going\n"));
-
- status =STATUS_UNSUCCESSFUL;
-
- }
-
-
-
- info = cbout;
-
- break;
-
- }
-
- default:
-
- status =STATUS_INVALID_DEVICE_REQUEST;
-
- break;
-
- }
-
- pIrp->IoStatus.Status = status;
-
- pIrp->IoStatus.Information = info;
-
- IoCompleteRequest(pIrp, IO_NO_INCREMENT);
-
- return status;
-
- }
测试的main函数:
- int main()
-
- {
-
- HANDLE hDevice =
-
- CreateFile("\\\\.\\HelloDDK",
-
- GENERIC_READ | GENERIC_WRITE,
-
- 0,
-
- NULL,
-
- OPEN_EXISTING,
-
- FILE_ATTRIBUTE_NORMAL,
-
- NULL);
-
-
-
- if (hDevice == INVALID_HANDLE_VALUE)
-
- {
-
- printf("Failed to obtainfile handle to device: "
-
- "%s with Win32error code: %d\n",
-
- "MyWDMDevice", GetLastError() );
-
- return 1;
-
- }
-
-
-
- UCHAR InputBuffer[10];
-
- UCHAR OutputBuffer[10];
-
-
-
- memset(InputBuffer,0xBB,10);
-
- DWORD dwOutput;
-
-
-
-
-
- BOOL bRet;
-
- bRet = DeviceIoControl(hDevice, IOCTL_TEST1, InputBuffer, 10,&OutputBuffer, 10, &dwOutput, NULL);
-
- if (bRet)
-
- {
-
- printf("Output buffer:%dbytes\n",dwOutput);
-
- for (inti=0;i<(int)dwOutput;i++)
-
- {
-
- printf("%02X",OutputBuffer[i]);
-
- }
-
- printf("\n");
-
- }
-
-
-
- bRet = DeviceIoControl(hDevice, IOCTL_TEST2, InputBuffer, 10,&OutputBuffer, 10, &dwOutput, NULL);
-
- if (bRet)
-
- {
-
- printf("Output buffer:%dbytes\n",dwOutput);
-
- for (inti=0;i<(int)dwOutput;i++)
-
- {
-
- printf("%02X",OutputBuffer[i]);
-
- }
-
- printf("\n");
-
- }
-
-
-
- bRet = DeviceIoControl(hDevice, IOCTL_TEST3, InputBuffer, 10,&OutputBuffer, 10, &dwOutput, NULL);
-
- if (bRet)
-
- {
-
- printf("Output buffer:%dbytes\n",dwOutput);
-
- for (int i=0;i<(int)dwOutput;i++)
-
- {
-
- printf("%02X",OutputBuffer[i]);
-
- }
-
- printf("\n");
-
- }
-
-
-
- CloseHandle(hDevice);
-
-
-
- return 0;
-
- }
首先是控制码设置:
- #define IOCTL_TEST1 CTL_CODE(\
- FILE_DEVICE_UNKNOWN, \
- 0x800, \
- METHOD_BUFFERED, \
- FILE_ANY_ACCESS)
-
- #define IOCTL_TEST2 CTL_CODE(\
- FILE_DEVICE_UNKNOWN, \
- 0x801, \
- METHOD_IN_DIRECT, \
- FILE_ANY_ACCESS)
-
- #define IOCTL_TEST3 CTL_CODE(\
- FILE_DEVICE_UNKNOWN, \
- 0x802, \
- METHOD_NEITHER, \
- FILE_ANY_ACCESS)
再是IRP_MJ_DEVICE_CONTROL派遣函数:
- NTSTATUS IOCTRLDRIVER_DispatchDeviceControl(
- IN PDEVICE_OBJECT DeviceObject,
- IN PIRP pIrp
- )
- {
- NTSTATUS status = STATUS_SUCCESS;
-
- PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
-
- ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
-
- ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
-
- ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
- ULONG info = 0;
- switch(code)
- {
- case IOCTL_TEST1:
- {
- KdPrint(("zhui:IOCTL_TEST1\n"));
-
- UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
- for (ULONG i=0;i
- {
- KdPrint(("zhui:%X\n",InputBuffer[i]));
- }
-
-
- UCHAR* OutputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
- memset(OutputBuffer,0xAA,cbout);
-
- info = cbout;
- break;
- }
- case IOCTL_TEST2:
- {
- KdPrint(("zhui:IOCTL_TEST2\n"));
-
- UCHAR* InputBuffer = (UCHAR*)pIrp->AssociatedIrp.SystemBuffer;
- for (ULONG i=0;i
- {
- KdPrint(("zhui:%X\n",InputBuffer[i]));
- }
-
-
- KdPrint(("zhui:User Address:0X%08X\n",MmGetMdlVirtualAddress(pIrp->MdlAddress)));
-
- UCHAR* OutputBuffer = (UCHAR*)MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
-
- memset(OutputBuffer,0xAA,cbout);
-
- info = cbout;
- break;
- }
- case IOCTL_TEST3:
- {
- KdPrint(("zhui:IOCTL_TEST3\n"));
-
- UCHAR* UserInputBuffer = (UCHAR*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
- KdPrint(("zhui:UserInputBuffer:0X%0X\n",UserInputBuffer));
-
-
- PVOID UserOutputBuffer = pIrp->UserBuffer;
-
- KdPrint(("zhui:UserOutputBuffer:0X%0X\n",UserOutputBuffer));
-
- __try
- {
- KdPrint(("zhui:Enter __try block\n"));
-
-
- ProbeForRead(UserInputBuffer,cbin,4);
-
- for (ULONG i=0;i
- {
- KdPrint(("zhui:%X\n",UserInputBuffer[i]));
- }
-
-
- ProbeForWrite(UserOutputBuffer,cbout,4);
-
-
- memset(UserOutputBuffer,0xAA,cbout);
-
-
- info = cbout;
-
- KdPrint(("zhui:Leave __try block\n"));
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- KdPrint(("zhui:Catch the exception\n"));
- KdPrint(("zhui:The program will keep going\n"));
- status = STATUS_UNSUCCESSFUL;
- }
-
- info = cbout;
- break;
- }
- default:
- status = STATUS_INVALID_DEVICE_REQUEST;
- break;
- }
- pIrp->IoStatus.Status = status;
- pIrp->IoStatus.Information = info;
- IoCompleteRequest(pIrp, IO_NO_INCREMENT);
- return status;
- }
测试的main函数:
- int main()
- {
- HANDLE hDevice =
- CreateFile("\\\\.\\HelloDDK",
- GENERIC_READ | GENERIC_WRITE,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL );
-
- if (hDevice == INVALID_HANDLE_VALUE)
- {
- printf("Failed to obtain file handle to device: "
- "%s with Win32 error code: %d\n",
- "MyWDMDevice", GetLastError() );
- return 1;
- }
-
- UCHAR InputBuffer[10];
- UCHAR OutputBuffer[10];
-
- memset(InputBuffer,0xBB,10);
- DWORD dwOutput;
-
-
- BOOL bRet;
- bRet = DeviceIoControl(hDevice, IOCTL_TEST1, InputBuffer, 10, &OutputBuffer, 10, &dwOutput, NULL);
- if (bRet)
- {
- printf("Output buffer:%d bytes\n",dwOutput);
- for (int i=0;i<(int)dwOutput;i++)
- {
- printf("%02X ",OutputBuffer[i]);
- }
- printf("\n");
- }
-
- bRet = DeviceIoControl(hDevice, IOCTL_TEST2, InputBuffer, 10, &OutputBuffer, 10, &dwOutput, NULL);
- if (bRet)
- {
- printf("Output buffer:%d bytes\n",dwOutput);
- for (int i=0;i<(int)dwOutput;i++)
- {
- printf("%02X ",OutputBuffer[i]);
- }
- printf("\n");
- }
-
- bRet = DeviceIoControl(hDevice, IOCTL_TEST3, InputBuffer, 10, &OutputBuffer, 10, &dwOutput, NULL);
- if (bRet)
- {
- printf("Output buffer:%d bytes\n",dwOutput);
- for (int i=0;i<(int)dwOutput;i++)
- {
- printf("%02X ",OutputBuffer[i]);
- }
- printf("\n");
- }
-
- CloseHandle(hDevice);
-
- return 0;
- }
转:http://blog.csdn.net/zhang_hui_cs/article/details/7527307