DeviceIoControl与驱动交互
DeviceIoControl的其实和ReadFile和WriteFile是一样的, 不过这个功能更强, 一次交互能够输入数据, 也可以输出数据. 更加暴力. DeviceIoControl内部创建的IRP是IRP_MJ_DEVICE_CONTROL类型的IRP, 然后操作系统会将这个IRP转发给驱动程序的分发函数中. 就是类似Win32下面的发送自定义消息了.. 但是又有点区别, 如果发送自定义消息, 如果跨进程, 那是不可以传递指针的, 但是和驱动那就无所谓了, 可以传递一块缓冲区过去, 多舒服.
其实这个也没有什么好说的,前面几篇已经试验过了ReadFile, 和WriteFile. 所以这个没有什么区别了.就是多了个定义I/O控制码的流程. I/O控制码也是非常简单的, 使用CTL_CODE宏来创建, 第一个参数是设备类型, 第二个参数就是控制码, 随便选一个大于0x800就可以. 然后就是缓冲区的操作方式了. 前面几篇我们已经见识过3种缓冲区操作方式了. 所以呢, 这个也不是什么问题了.. 最后一个是操作权限, 不要多想. 用FILE_ANY_ACCESS所有权限就OK..
其他就没有什么好说了. 这个代码的话就是用户层这边试验了3中缓冲区方式发送的内核这边, 内核这个做个响应. 没有什么难以理解的地方. 直接上代码!
http://www.joenchen.com/JoenTools/DeviceIoControlIo.rar
用户态:
/* Windows内核下DeviceIoControl与驱动进行交互 3环代码 编译方法参见makefile. TAB = 8 */ #include <windows.h> #include <stdio.h> #include <winioctl.h> #define SYS_LINK_NAME "\\\\.\\SysLinkJoenDevice" #define MAX_LENGTH 1024 // 设备类型 功能号 I/O访问内存使用方式 权限 #define IOCTL_CONTROL_BUFFERED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS ) #define IOCTL_CONTROL_DIRECT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_IN_DIRECT, FILE_ANY_ACCESS ) #define IOCTL_CONTROL_NEITHER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS ) #pragma comment(linker, "/Entry:Jmain") #pragma comment(linker, "/subsystem:console") //=========================================================================== int Jmain() { HANDLE hFile = 0; BYTE byBuf[10]; BYTE byBuf2[10]; BOOL bRet; DWORD dwByteRead, i; //打开设备 hFile = CreateFile( SYS_LINK_NAME, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ); if ( hFile == INVALID_HANDLE_VALUE ) { printf( "打开设备错误了!\n" ); return -1; } printf( "打开设备成功!\n" ); RtlFillMemory( byBuf, sizeof( byBuf ), '1' ); bRet = DeviceIoControl( hFile, IOCTL_CONTROL_BUFFERED, byBuf, sizeof( byBuf ), \ byBuf2, sizeof( byBuf2 ), &dwByteRead, NULL ); if ( bRet ) { printf( "BUFFERED 返回缓冲区长度%d, 数据:" ); for ( i = 0; i < dwByteRead; i++ ) { printf( "%c", byBuf2[i] ); } printf( "\n" ); } RtlFillMemory( byBuf, sizeof( byBuf ), '2' ); bRet = DeviceIoControl( hFile, IOCTL_CONTROL_DIRECT, byBuf, sizeof( byBuf ), byBuf2, sizeof( byBuf2 ), &dwByteRead, NULL ); if ( bRet ) { printf( "DIRECT 返回缓冲区长度%d, 数据:" ); for ( i = 0; i < dwByteRead; i++ ) { printf( "%c", byBuf2[i] ); } printf( "\n" ); } RtlFillMemory( byBuf, sizeof( byBuf ), '3' ); bRet = DeviceIoControl( hFile, IOCTL_CONTROL_NEITHER, byBuf, sizeof( byBuf ), byBuf2, sizeof( byBuf2 ), &dwByteRead, NULL ); if ( bRet ) { printf( "NEITHER 返回缓冲区长度%d, 数据:" ); for ( i = 0; i < dwByteRead; i++ ) { printf( "%c", byBuf2[i] ); } printf( "\n" ); } if ( hFile ) { CloseHandle( hFile ); } system( "pause" ); ExitProcess(0); return 0; }
内核态:
/* Windows内核下DeviceIoControl与驱动进行交互 0环代码 编译方法参见makefile. TAB = 8 */ #include <ntddk.h> #define DEVICE_NAME L"\\Device\\DevJoenDevice" #define SYS_LINK_NAME L"\\??\\SysLinkJoenDevice" #define MAX_LENGTH 1024 // 设备类型 功能号 I/O访问内存使用方式 权限 #define IOCTL_CONTROL_BUFFERED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS ) #define IOCTL_CONTROL_DIRECT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_IN_DIRECT, FILE_ANY_ACCESS ) #define IOCTL_CONTROL_NEITHER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS ) typedef struct tagDeviceExt { PDEVICE_OBJECT pDeviceObj; UNICODE_STRING USzDeviceName; UNICODE_STRING USzSysLinkName; }DEVICEEXT, *PDEVICEEXT; //=========================================================================== //驱动卸载例程 #pragma code_seg( "PAGE" ) VOID DriverUnload( PDRIVER_OBJECT pDriverObj ) { PDEVICEEXT pDeviceExt; PDEVICE_OBJECT pNextDeviceObj; pNextDeviceObj = pDriverObj->DeviceObject; while ( pNextDeviceObj != NULL ) { pDeviceExt = pNextDeviceObj->DeviceExtension; IoDeleteDevice( pDeviceExt->pDeviceObj ); IoDeleteSymbolicLink( &pDeviceExt->USzSysLinkName ); KdPrint( ( "删除%wZ设备成功!\n", &pDeviceExt->USzDeviceName ) ); pNextDeviceObj = pNextDeviceObj->NextDevice; } } //=========================================================================== //所有不关心的IRP处理例程 NTSTATUS DispatchRoutine( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) { pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest( pIrp, IO_NO_INCREMENT ); return STATUS_SUCCESS; } //=========================================================================== //设备控制请求 NTSTATUS DispatchDeviceControl( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) { ULONG Code, i; NTSTATUS Status; ULONG ulInBufLen; ULONG ulOutBufLen; ULONG ulOutInfo; UCHAR* InputBuffer = NULL; UCHAR* OutputBuffer = NULL; PIO_STACK_LOCATION Stack; Stack = IoGetCurrentIrpStackLocation( pIrp ); //输入缓冲区大小 ulInBufLen = Stack->Parameters.DeviceIoControl.InputBufferLength; //输出缓冲区大小 ulOutBufLen = Stack->Parameters.DeviceIoControl.OutputBufferLength; //IO控制码 Code = Stack->Parameters.DeviceIoControl.IoControlCode; Status = STATUS_SUCCESS; ulOutInfo = ulOutBufLen; switch ( Code ) { //=========================================================================== // 缓冲区IO, 很安全 case IOCTL_CONTROL_BUFFERED: InputBuffer = ( UCHAR* )pIrp->AssociatedIrp.SystemBuffer; if ( InputBuffer ) { KdPrint( ( "BUFFERED输入缓冲区数据:\n" ) ); for ( i = 0; i < ulInBufLen; i++ ) { KdPrint( ( "%X", InputBuffer[i] ) ); } KdPrint(( "\n" )); RtlFillMemory( pIrp->AssociatedIrp.SystemBuffer, ulOutBufLen, 'a' ); } break; //=========================================================================== // MDL类型的IO, MDL的输入缓冲区和缓冲区IO是一样的. case IOCTL_CONTROL_DIRECT: InputBuffer = ( UCHAR* )pIrp->AssociatedIrp.SystemBuffer; if ( InputBuffer ) { KdPrint( ( "IN_DIRECT/IO输入缓冲区的数据:\n" ) ); for ( i = 0; i < ulInBufLen; i++ ) { KdPrint( ( "%X", InputBuffer[i] ) ); } KdPrint( ( "\n" ) ); } KdPrint( ( "用户传入OutputBuffer地址: %p\n", MmGetMdlVirtualAddress( pIrp->MdlAddress ) ) ); //得到输出缓冲地址 OutputBuffer = MmGetSystemAddressForMdlSafe( pIrp->MdlAddress, NormalPagePriority ); if ( OutputBuffer ) { KdPrint( ( "映射到内核中的地址是: %p\n", OutputBuffer ) ); RtlFillMemory( OutputBuffer,ulOutBufLen, 'b' ); } break; //=========================================================================== // 其他方式的IO case IOCTL_CONTROL_NEITHER: //得到输入/输出缓冲区 InputBuffer = ( UCHAR* )Stack->Parameters.DeviceIoControl.Type3InputBuffer; OutputBuffer = ( UCHAR* )pIrp->UserBuffer; KdPrint( ( "NEITHER/IO用户模式传入的地址%p\n", InputBuffer ) ); __try { //测试用户层传递的内存可读性 ProbeForRead( InputBuffer, ulInBufLen, 4 ); for ( i = 0; i < ulInBufLen; i++ ) { KdPrint( ( "%X", InputBuffer[i] ) ); } KdPrint(( "\n" )); ProbeForWrite( OutputBuffer, ulOutBufLen, 4 ); RtlFillMemory( OutputBuffer, ulOutBufLen, 'c' ); }__except( EXCEPTION_EXECUTE_HANDLER ) { KdPrint( ( "Win32程序传入了一个无效的地址!\n" ) ); Status = STATUS_UNSUCCESSFUL; } break; //--------------------------------------------------------------------------- default: Status = STATUS_INVALID_VARIANT; } pIrp->IoStatus.Status = Status; pIrp->IoStatus.Information = ulOutInfo; IoCompleteRequest( pIrp, IO_NO_INCREMENT ); return Status; } //=========================================================================== //驱动程序入口 #pragma code_seg( "INIT" ) NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pUSzRegPath ) { ULONG i; NTSTATUS Status; PDEVICEEXT pDeviceExt; PDEVICE_OBJECT pDeviceObj; UNICODE_STRING USzDeviceName = RTL_CONSTANT_STRING( DEVICE_NAME ); UNICODE_STRING USzSysLinkName = RTL_CONSTANT_STRING( SYS_LINK_NAME ); Status = IoCreateDevice( pDriverObj, sizeof( DEVICEEXT ), &USzDeviceName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDeviceObj ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "创建设备失败!\n" ) ); return Status; } Status = IoCreateSymbolicLink( &USzSysLinkName, &USzDeviceName ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "创建符号链接!\n" ) ); return Status; } //采用直接IO方式 pDeviceObj->Flags |= DO_DIRECT_IO; pDeviceExt = pDeviceObj->DeviceExtension; pDeviceExt->pDeviceObj = pDeviceObj; pDeviceExt->USzDeviceName = USzDeviceName; pDeviceExt->USzSysLinkName = USzSysLinkName; for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++ ) { pDriverObj->MajorFunction[i] = &DispatchRoutine; } pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = &DispatchDeviceControl; pDriverObj->DriverUnload = &DriverUnload; return Status; }
发表评论
Warning: Undefined variable $user_ID in /www/wwwroot/joenchen.com/wp-content/themes/x-agan/comments.php on line 66
您必须登录 才能进行评论。