设备读写的第三种方式是Neither方式,在PDevObj中不设置Flags.这种方式很少被用到。
1.在Neither方式中,派遣函数直接读写应用程序提供的缓冲区地址。你很可能看到有人这样说过:决不(或几乎从不)直接引用用户模式的内存地址。因为缓冲区内存是用户模式的内存地址,驱动程序如果直接引用这段内存是十分危险的。原因是windows操作系统是多任务的,它可能随时切换到别的进程。如果驱动程序访问这段内存时操作系统已经切换到其他进程,那么驱动程序访问的内存地址必定是错误的,系统会毫不留情的出现死亡蓝屏(BSOD blue screen of death)。
这种方式只有驱动程序和应用程序运行在相同的线程上下文时,才能被使用。所以呢,驱动程序在使用用户模式地址前一定要检测这段内存是否可读或可写。这就需要用到ProbeForRead函数和ProbeForWrite函数。
VOID
ProbeForRead(
IN CONST VOID *Address,
IN SIZE_T Length,
IN ULONG Alignment
);
VOID
ProbeForWrite(
IN CONST VOID *Address,
IN SIZE_T Length,
IN ULONG Alignment
);
ProbeForRead函数和ProbeForWrite函数与try_except 块配合来检验内存是否可读写。
在派遣函数中通过IRP的pIrp->UserBuffer字段得到用户模式提供的缓冲区的内存地址。
stack_>Parameters.Read.Length字段得到读取的字节数。
下面是IRP_MJ_READ的派遣函数:
NTSTATUS DispatchRead(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp)
{
KdPrint(("Enter DispatchRead\n"));
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;
//得到当前堆栈
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
//得到读的长度
ULONG ulReadLength = stack->Parameters.Read.Length;
//得到读的偏移量
ULONG ulReadOffset = (ULONG)stack->Parameters.Read.ByteOffset.QuadPart;
//得到用户模式地址
PVOID user_address = pIrp->UserBuffer;
KdPrint(("user_address:0X%0X\n",user_address));
__try
{
KdPrint(("Enter __try block\n"));
//判断空指针是否可写,显然会导致异常
ProbeForWrite(user_address,ulReadLength,4);
memset(user_address,0xAA,ulReadLength);
//由于在上面引发异常,所以以后语句不会被执行!
KdPrint(("Leave __try block\n"));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint(("Catch the exception\n"));
KdPrint(("The program will keep going\n"));
status = STATUS_UNSUCCESSFUL;
}
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = ulReadLength; // bytes xfered
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("Leave DispatchRead\n"));
return status;
}
另外两种设备读取方式
缓冲区方式:http://blog.csdn.net/liyun123gx/article/details/38042125
直接读取方式:http://blog.csdn.net/liyun123gx/article/details/38043849