应用层与内核的几种通信方式

应用程序与驱动程序据我所知,细分可以分6种,ReadFile,WirteFile方式的缓冲区设备读写,直接方式读写,和其他方式读写。Io设备控制操作(即DeviceControl)的缓冲内存模式IOCTL,直接内存方式的IOCTL,其他内存方式的IOCTL!当然还有一种就是创建文件,然后文件读写也应该算是一种通信吧,这里不讨论这个!

1,缓冲区方式设备读写:

在创建Device后,须要指定方式为Device的Flags有DO_BUFFERED_IO!通过应用层Api函数ReadFile,WriteFile,等函数,ntoskrnl.exe创建Irp后,ReadFile和WriteFile参数的缓冲区就在irp->AssociatedIrp.Systembuffer,同时要求读写的偏移量,和长度都在PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp) 数据类型中的stack->Parameters.Read.Length,stack->Parameters.Read.ByteOffset(该类型为Large_Interge类型),

2,直接方式读写

在创建Device后,须要指定方式为Device的Flags有DO_DIRECT_IO!通过应用层APi函数ReadFile,WriteFile等函数,ntoskrnl.exe创建的Irp后,ReadFile和WriteFile参数的缓冲区将被锁住,然后操作系统将这段缓冲区在内核模式地址再次映射一遍,这样应用层的缓冲区和内存层的就指向同一个物理内存!而内核模式用MDL数据结构记录这段内存,这个虚拟内存大小在MmGetByteCount(pIrp->MdlAddress),首地址在MmGetMdlVirtualAddress(pIrp->MdlAddress);偏移量为MmGetMdlByteOffset(pIrp->MdlAddress)(这里的偏移量不是文件读写的偏移量,而是在MDL中的偏移量)然后文件的长度还是stack->Parameters.Read.Length,这个值和MmGetByteCount(pIrp->MdlAddress)是一样的,要不然就出错了,而真正的读写偏移量还是在stack->Parameters.Read.ByteOffset!这里无论是读还是写,都要得到MDL在内核模式下的映射,因此还要用MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority)将其转化为内核模式,然后可以读写该地址,就会转化到应用层相应的内存!!

3其他方式读写

这种方式很少用到,在创建Device后,Flags既不标志DO_BUFFERED_IO也不标志DO_DIRECT_IO,ReadFile和WriteFile提供的缓冲区内存地址,可以再IRP的pIrp->UserBuffer字段得到,而长度和偏移量还是在stack->Paameters.Read中,但是用这种方法须要注意的是ReadFile可能把空指针地址或者非法地址传递给驱动程序,因此驱动程序使用用户模式地址钱须要检查是否可读或者可写,可以用ProbeForWrite或者ProbeForWrite函数和try模块。

这里有个问题困扰着我,就是应用层用GetFileSize函数时用第一种方法处理该Irp时结果是正确的,就是冲pIrp->AssociatedIrp.SystemBuffer;得到PFILE_STANDARD_INFORMATION结构指针,然后讲该结构的EndOfFile设置为你自己文件的长度,然后结束该IRP,GetFileSize就能得到正确的文件长度,但是用方法三就是的时候用GetFileSize,我同样用pIrp->UserBufer得到PFILE_STANDARD_INFORMATION结构指针,然后设置后,在GetFileSize里面去得不到正确的长度!!这个是为什么?还有就是该Irp的CurrentStackLocation-stack,该字段里面的stack->Parameters.QueryFile.Length指的是什么,显示的都是24,无论文长度为多少,还是用哪种方法都市24,很郁闷!!!

下面是方式都是用IO设备控制操作的方法基本上与上面3中相对应!

对于IO设备控制操作,不知道为什么可以用设置DO_DIRECT_IO后就能对三种方式都适用,而且stack->Parameters.Read.Length/Write.Length 都是对应应用层的DeviceIoControl函数中的第6个参数也就是输出缓冲区!DeviceIoControl函数的输入输出缓冲区长度都再stack->Parameters.DeviceioControl.InputBufferLength/OutputBufferLength中

4缓冲内存IOCTL,在DeviceIoControl函数第二个参数的时候,使用CTL_CODE来产生该常数,其中Method字段设置为METHOD_BUFFERED,在内核模式中输入缓冲区很输出缓冲区都为pIrp->AssociatedIrp.SystemBuffer,

5直接方式IOCTL,在DeviceIoControl函数第二个参数的时候,使用CTL_CODE来产生该常数,其中Method字段设置为METHOD_IN_DIRECT/METHOD_OUT_DIRECT(最好使用第一个,因为第一个对所有的打开设备方式都通用)!对于第4中的区别是输入缓冲区还在pIrp->AssocatedIrp.Systembuffer中,但是输出缓冲区却是pIrp->MdlAddress,因此在内核对输出的写应该写在MmGetSystemAddressForMdlSafe(pIrp->MdlAddress)中,

6其他方式IOCTL,在DeviceIoControl函数第二个参数的时候,使用CTL_CODE来产生该常数,其中Method字段设置为MEHTOD_NEITHER,输入缓冲区为stack->Parameters.DeviceIoControl.Tyep3InputBuffer;输出缓冲区为pIrp->Userbuffer

posted @ 2018-05-18 20:08  黑马网仔  阅读(2327)  评论(0编辑  收藏  举报