ring0与ring3通信方式

修改自: https://blog.csdn.net/wzsy/article/details/54929726

控制码方式详解: https://www.cnblogs.com/lsh123/p/7354573.html

0、应用程序与驱动程序通信方式据我所知,细分可以分4种,ReadFile,WirteFile方式的缓冲区设备读写,直接方式读写,和其他方式读写,Io设备控制操作.

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)将其转化为内核模式,然后可以读写该地址,就会转化到应用层相应的内存!!

补充一点, pIrp->MdlAddress存储的就是用户态下的相关信息, MmGetMdlVirtualAddress 返回的就是用户模式下的虚拟内存地址, 驱动不能直接使用用户态下的地址, 要使用的话需要调用 MmGetSystemAddressForMdlSafe获取函数的返回地址

3、其他方式读写

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

4、Io设备控制方式(IOCTL)

这种通信方式是一种更灵活的通信方式, IRP类型是IRP_MJ_DEVICE_CONTROL, , 这个是作为以上3种数据传输方式的补充, 因为这种方式既可以跟前三种组合使用, 也可以提供其他的控制方式. 我们可以通过定义控制码的方式来提供ring3控制ring0的接口, 每一种接口通过控制码来标识, 如果不需要传输数据, 仅用一个控制码足够了, 如果还需要传输数据, 则需要与上面提到的3种基本通信方式进行组合使用. 

原文地址:https://www.cnblogs.com/talenth/p/9626077.html