DeviceIOControl与驱动层 - 缓冲区模式
IO交互模式中的DeviceIOControl与驱动层交互有三种:缓冲区模式、直接访问模式、其他模式,这里本人学习的是缓冲区访问模式,原理如图:
驱动中最好不要直接访问用户模式下的内存地址,使用缓冲区方式可以避免程序员访问内存模式下的内存地址。
Win32API DeviceIoControl的内部,用户提供的输入缓冲区的内容呗复制到IRP中的pIrp->AssociateIrp.SystemBuffer内存地址,复制的字节数是由DeviceIoControl指定的输入字节数。
派遣函数可以读取pIrp->AssociateIrp.SystemBuffer的内存地址,从而获得应用程序提供的输入缓冲数据。另外,派遣函数还可以写入pIrp->AssociateIrp.SystemBuffer的内存地址,被当做设备输出的数据。操作系统会将这个地址的数据再次复制到DeviceIoControl提供的输出缓冲区。复制的字节数有pIrp->IoStatus.Information指定。DeviceIoControl也可以通过它的第七个参数(lpBytesReturned)得到这个操作字节数。
派遣函数先通过IoGetCurrentIrpStackLocation函数->得到当前IO堆栈(IO_STACK_LOCATION)->派遣函数通过stack->Parameters.DeviceIoControl.InputBufferLength得到输入缓冲区大小->通过stack->Parameters.DeviceIoControl.OutputBufferLength得到输出缓冲区大小->最后通过stack->Parameters.DeviceIoControl.IoControlCode得到IOCTL
在派遣函数中通过C语言中的switch处理不同的IOCTL
代码:
BufferedIO.c
1 #include "BufferedIO.h" 2 3 NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath) 4 { 5 NTSTATUS Status = STATUS_SUCCESS; 6 PDEVICE_OBJECT DeviceObject = NULL; 7 UNICODE_STRING DeviceObjectName;//设备对象名称 8 UNICODE_STRING DeviceLinkName; //设备连接名称 9 int i = 0; 10 11 12 DriverObject->DriverUnload = DriverUnload; 13 14 //1.创建设备对象名称 15 RtlInitUnicodeString(&DeviceObjectName, DEVICE_OBJECT_NAME); 16 17 //2.创建设备对象 18 Status = IoCreateDevice( 19 DriverObject,//驱动程序对象. 20 NULL, //指定驱动程序为设备扩展对象而定义的结构体的大小. 21 &DeviceObjectName, //(可选的参数)指向一个以零结尾的包含Unicode字符串的缓冲区, 那是这个设备的名称, 该字符串必须是一个完整的设备路径名. 22 FILE_DEVICE_UNKNOWN, //指定一个由一个系统定义的FILE_DEVICE_XXX常量, 表明了这个设备的类型 23 0, //一个或多个系统定义的常量, 连接在一起, 提供有关驱动程序的设备其他信息. 24 FALSE, //设备是独占的,独占的话设置为TRUE,非独占设置为FALSE.一般FALSE 25 &DeviceObject 26 );//成功时返回STATUS_SUCCESS 27 28 if (!NT_SUCCESS(Status)) 29 { 30 return Status; 31 } 32 33 //3.创建设备连接名称 34 RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME); 35 36 //4.将设备连接名称与设备名称关联 37 Status = IoCreateSymbolicLink( 38 &DeviceLinkName, 39 &DeviceObjectName 40 );//创建一个设备链接。 41 //驱动程序虽然有了设备名称,但是这种设备名称只能在内核态可见, 42 //而对于应用程序是不可见的,因此,驱动需要要暴露一个符号链接, 43 //该链接指向真正的设备名称 44 45 if (!NT_SUCCESS(Status)) 46 { 47 IoDeleteDevice(DeviceObject); 48 return Status; 49 } 50 51 //Ring3请求->设备对象-> 驱动对象找到派遣历程 52 53 //5.设置符合我们代码的派遣历程 54 for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) 55 { 56 //MinorFunction ring3 与ring0 的协议 57 58 DriverObject->MajorFunction[i] = PassThroughDispatch; 59 } 60 61 //IRP_MJ_DEVICE_CONTROL DeviceIOControl函数产生 62 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ControlThroughDispatch; 63 64 65 return Status; 66 } 67 68 //基本派遣历程 69 NTSTATUS PassThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp) 70 { 71 Irp->IoStatus.Status = STATUS_SUCCESS; //LastError() 72 Irp->IoStatus.Information = 0; //ReturnLength 73 IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给Io管理器 74 return STATUS_SUCCESS; 75 } 76 77 NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp) 78 { 79 NTSTATUS Status; 80 ULONG_PTR Informaiton = 0; 81 PVOID InputData = NULL; 82 ULONG InputDataLength = 0; 83 PVOID OutputData = NULL; 84 ULONG OutputDataLength = 0; 85 ULONG IoControlCode = 0; 86 87 //1.得到当前IO堆栈(IO_STACK_LOCATION) 88 PIO_STACK_LOCATION IoStackLocation = IoGetCurrentIrpStackLocation(Irp); //Irp堆栈 89 90 //2.看笔记或者代码中的图 而且在Ring0层中都是SystemBuffer 91 InputData = Irp->AssociatedIrp.SystemBuffer; 92 OutputData = Irp->AssociatedIrp.SystemBuffer; 93 94 //得到输入缓冲区大小 95 InputDataLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength; 96 //输出缓冲区大小 97 OutputDataLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength; 98 99 100 //3.得到IOCTL 101 IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode; 102 103 //在派遣函数中通过Cswitch处理不同的IOCTL 104 switch (IoControlCode) 105 { 106 case CTL_HELLO: 107 { 108 if (InputData != NULL && InputDataLength > 0) 109 { 110 DbgPrint("%s\r\n", InputData); 111 } 112 if (OutputData != NULL&&OutputDataLength >= strlen("Ring0->Ring3") + 1) 113 { 114 memcpy(OutputData, "Ring0->Ring3", strlen("Ring0->Ring3") + 1); 115 Status = STATUS_SUCCESS; 116 Informaiton = strlen("Ring0->Ring3") + 1; 117 } 118 else 119 { 120 Status = STATUS_INSUFFICIENT_RESOURCES; //内存不够 121 Informaiton = 0; 122 } 123 124 break; 125 } 126 default: 127 break; 128 } 129 130 131 Irp->IoStatus.Status = Status; //Ring3 GetLastError(); 132 Irp->IoStatus.Information = Informaiton; 133 IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给Io管理器 134 135 return Status; 136 } 137 138 VOID DriverUnload(PDRIVER_OBJECT DriverObject) 139 { 140 UNICODE_STRING DeviceLinkName; 141 PDEVICE_OBJECT v1 = NULL; 142 PDEVICE_OBJECT DeleteDeviceObject = NULL; 143 144 RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME); 145 IoDeleteSymbolicLink(&DeviceLinkName); 146 147 RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME); 148 IoDeleteSymbolicLink(&DeviceLinkName); 149 150 DeleteDeviceObject = DriverObject->DeviceObject; 151 while (DeleteDeviceObject != NULL) 152 { 153 v1 = DeleteDeviceObject->NextDevice; 154 IoDeleteDevice(DeleteDeviceObject); 155 DeleteDeviceObject = v1; 156 } 157 158 159 DbgPrint("DriverUnload()\r\n"); 160 } 161 162 /* 163 typedef struct _DRIVER_OBJECT { 164 CSHORT Type; 165 CSHORT Size; 166 167 // 168 // The following links all of the devices created by a single driver 169 // together on a list, and the Flags word provides an extensible flag 170 // location for driver objects. 171 // 172 173 PDEVICE_OBJECT DeviceObject;// 174 ULONG Flags; 175 176 // 177 // The following section describes where the driver is loaded. The count 178 // field is used to count the number of times the driver has had its 179 // registered reinitialization routine invoked. 180 // 181 182 PVOID DriverStart; 183 ULONG DriverSize; 184 PVOID DriverSection; 185 PDRIVER_EXTENSION DriverExtension; 186 187 // 188 // The driver name field is used by the error log thread 189 // determine the name of the driver that an I/O request is/was bound. 190 // 191 192 UNICODE_STRING DriverName; 193 194 // 195 // The following section is for registry support. Thise is a pointer 196 // to the path to the hardware information in the registry 197 // 198 199 PUNICODE_STRING HardwareDatabase; 200 201 // 202 // The following section contains the optional pointer to an array of 203 // alternate entry points to a driver for "fast I/O" support. Fast I/O 204 // is performed by invoking the driver routine directly with separate 205 // parameters, rather than using the standard IRP call mechanism. Note 206 // that these functions may only be used for synchronous I/O, and when 207 // the file is cached. 208 // 209 210 PFAST_IO_DISPATCH FastIoDispatch; 211 212 // 213 // The following section describes the entry points to this particular 214 // driver. Note that the major function dispatch table must be the last 215 // field in the object so that it remains extensible. 216 // 217 218 PDRIVER_INITIALIZE DriverInit; 219 PDRIVER_STARTIO DriverStartIo; 220 PDRIVER_UNLOAD DriverUnload; 221 PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; 222 223 } DRIVER_OBJECT; 224 225 */ 226 227 /* 228 typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _DEVICE_OBJECT { 229 CSHORT Type; 230 USHORT Size; 231 LONG ReferenceCount; 232 struct _DRIVER_OBJECT *DriverObject; 233 struct _DEVICE_OBJECT *NextDevice; // 234 struct _DEVICE_OBJECT *AttachedDevice; 235 struct _IRP *CurrentIrp; 236 PIO_TIMER Timer; 237 ULONG Flags; // See above: DO_... 238 ULONG Characteristics; // See ntioapi: FILE_... 239 __volatile PVPB Vpb; 240 PVOID DeviceExtension; 241 DEVICE_TYPE DeviceType; 242 CCHAR StackSize; 243 union { 244 LIST_ENTRY ListEntry; 245 WAIT_CONTEXT_BLOCK Wcb; 246 } Queue; 247 ULONG AlignmentRequirement; 248 KDEVICE_QUEUE DeviceQueue; 249 KDPC Dpc; 250 251 // 252 // The following field is for exclusive use by the filesystem to keep 253 // track of the number of Fsp threads currently using the device 254 // 255 256 ULONG ActiveThreadCount; 257 PSECURITY_DESCRIPTOR SecurityDescriptor; 258 KEVENT DeviceLock; 259 260 USHORT SectorSize; 261 USHORT Spare1; 262 263 struct _DEVOBJ_EXTENSION *DeviceObjectExtension; 264 PVOID Reserved; 265 266 } DEVICE_OBJECT; 267 */
BufferIO.h
1 #include <ntifs.h> 2 3 4 //设备与设备之间通信 5 #define DEVICE_OBJECT_NAME L"\\Device\\BufferedIODeviceObjectName" 6 7 //设备与Ring3之间通信 8 #define DEVICE_LINK_NAME L"\\DosDevices\\BufferedIODeviceLinkName" 9 10 //Ring3 Ring0 握手协议 CTL_HELLO 11 #define CTL_HELLO \ 12 CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS) 13 14 15 16 17 NTSTATUS PassThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp); 18 NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp); 19 20 21 22 23 VOID DriverUnload(PDRIVER_OBJECT DriverObject);
缓冲区IO(Ring3).cpp
1 // 缓冲区IO(Ring3).cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <windows.h> 6 7 #define DEVICE_LINK_NAME L"\\\\.\\BufferedIODeviceLinkName"//Ring3格式 8 9 #define CTL_HELLO \ 10 CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS) 11 12 13 /* 14 DeviceIoControl内部会使操作系统创建一个 15 IRP_MJ_DEVICE_CONTROL类型的IRP,然后操作系统将这个IRP转发到派遣函数 16 17 我们用DeviceIoControl定义除了读写之外的其他操作,让Ring3程序与Ring0程序通信 18 19 e.g:自定义一种IO控制码,然后用DeviceIoControl将这个控制码和请求一起传递给驱动程序 20 派遣函数中,分别对不同的IO控制码进行处理 21 22 BOOL WINAPI DeviceIoControl( 23 _In_ HANDLE hDevice, //已经打开的设备 24 _In_ DWORD dwIoControlCode,//控制码 25 _In_opt_ LPVOID lpInBuffer, //输入缓冲区 26 _In_ DWORD nInBufferSize, //输入缓冲区大小 27 _Out_opt_ LPVOID lpOutBuffer, //输出缓冲区 28 _In_ DWORD nOutBufferSize, //输出缓冲区大小 29 _Out_opt_ LPDWORD lpBytesReturned,//实际返回字节数 30 _Inout_opt_ LPOVERLAPPED lpOverlapped //是否OverLapp 31 ); 32 33 34 */ 35 36 int main() 37 { 38 39 HANDLE DeviceHandle = CreateFile( 40 DEVICE_LINK_NAME, 41 GENERIC_READ | GENERIC_WRITE, 42 FILE_SHARE_READ | FILE_SHARE_WRITE, 43 NULL, 44 OPEN_EXISTING, 45 FILE_ATTRIBUTE_NORMAL, 46 NULL); 47 48 if (DeviceHandle == INVALID_HANDLE_VALUE) 49 { 50 return 0; 51 } 52 53 char BufferData = NULL; 54 DWORD ReturnLength = 0; 55 56 //操作系统内部是操作系统创建IRP_MJ_DEVICE_CONTROL类型的IRP 57 //Ring3->Ring0 58 BOOL IsOk = DeviceIoControl( 59 DeviceHandle, //已经打开的设备 60 CTL_HELLO,//控制码 61 "Ring3->Ring0",//输入缓冲区 62 strlen("Ring3->Ring0") + 1,//输入缓冲区大小 63 (LPVOID)BufferData,//输出缓冲区 64 0,//输出缓冲区大小 65 &ReturnLength,//实际返回字节数 66 NULL//是否OVERLAP操作 67 ); 68 69 if (IsOk == FALSE) 70 { 71 //上面的nOutBufferSize = 0 所以必定发生错误 72 73 int LastError = GetLastError(); 74 75 if (LastError == ERROR_NO_SYSTEM_RESOURCES) 76 { 77 char BufferData[MAX_PATH] = { 0 }; 78 79 //Ring3请求->设备对象-> 驱动对象找到派遣历程 80 81 IsOk = DeviceIoControl( 82 DeviceHandle, 83 CTL_HELLO, 84 "Ring3->Ring0", 85 strlen("Ring3->Ring0") + 1, 86 (LPVOID)BufferData, 87 MAX_PATH, 88 &ReturnLength, 89 NULL); 90 91 if (IsOk == TRUE) 92 { 93 printf("%s\r\n", BufferData); 94 } 95 } 96 } 97 98 if (DeviceHandle != NULL) 99 { 100 CloseHandle(DeviceHandle); 101 DeviceHandle = NULL; 102 } 103 printf("Input AnyKey To Exit\r\n"); 104 105 getchar(); 106 107 return 0; 108 }