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中缓冲区方式发送的内核这边, 内核这个做个响应. 没有什么难以理解的地方. 直接上代码!

DeviceIoControlIo.rar

用户态:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/*
	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 ), &#39;1&#39; );
	bRet = DeviceIoControl( hFile, IOCTL_CONTROL_BUFFERED, byBuf, sizeof( byBuf ), \
	                        byBuf2, sizeof( byBuf2 ), &amp;dwByteRead, NULL );

	if ( bRet ) {
		printf( "BUFFERED  返回缓冲区长度%d, 数据:" );
		for ( i = 0; i< dwByteRead; i++ ) {
			printf( "%c", byBuf2[i] );
		}
		printf( "\n" );
	}

	RtlFillMemory( byBuf, sizeof( byBuf ), &#39;2&#39; );

	bRet = DeviceIoControl( hFile, IOCTL_CONTROL_DIRECT, byBuf, sizeof( byBuf ),
	                        byBuf2, sizeof( byBuf2 ), &amp;dwByteRead, NULL );

	if ( bRet ) {

		printf( "DIRECT  返回缓冲区长度%d, 数据:" );
		for ( i = 0; i< dwByteRead; i++ ) {
			printf( "%c", byBuf2[i] );
		}
		printf( "\n" );
	}

	RtlFillMemory( byBuf, sizeof( byBuf ), &#39;3&#39; );

	bRet = DeviceIoControl( hFile, IOCTL_CONTROL_NEITHER, byBuf, sizeof( byBuf ),
	                        byBuf2, sizeof( byBuf2 ), &amp;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;
}

内核态:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*
	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( &amp;pDeviceExt->USzSysLinkName );
		KdPrint( ( "删除%wZ设备成功!\n", &amp;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, &#39;b&#39; );
		}
		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, &#39;c&#39; );

		}__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 ), &amp;USzDeviceName,
	                         FILE_DEVICE_UNKNOWN, 0, TRUE, &amp;pDeviceObj );

	if ( !NT_SUCCESS( Status ) ) {
		KdPrint( ( "创建设备失败!\n" ) );
		return Status;
	}

	Status = IoCreateSymbolicLink( &amp;USzSysLinkName, &amp;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] = &amp;DispatchRoutine;
	}
	pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = &amp;DispatchDeviceControl;
	pDriverObj->DriverUnload = &amp;DriverUnload;

	return Status;
}