博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ring0 与 ring3 层之间的交互
阅读量:5155 次
发布时间:2019-06-13

本文共 6691 字,大约阅读时间需要 22 分钟。

  在进行Windows的ring0层开发时,必不可免的要与 ring3 层进行交互。进行数据间的相互传输。可用的方法有DeviceIoCntrol,ReadFile。我平常都是用的DeviceIoControl在ring3 与 ring0 层进行的数据传输。今天就写写DeviceIoControl 和 ring0通过事件通知 ring3!

 

首先加载驱动之后,在ring3层调用CreateFile() 打开ring0层生成的LinkName,获得设备对象的句柄。然后调用DeviceIoControl(),由Io Mannger 构建IRP包下发

BOOL WINAPI DeviceIoControl(

__in HANDLE hDevice,      //设备对象的句柄
__in DWORD dwIoControlCode,    //IO控制码
__in_opt LPVOID lpInBuffer,     //由ring3下发到ring0的缓冲区
__in DWORD nInBufferSize,     //缓冲区大小
__out_opt LPVOID lpOutBuffer,   //由ring3下发ring0用来返回数据的缓冲区
__in DWORD nOutBufferSize,      //输出缓冲区大小
__out_opt LPDWORD lpBytesReturned,     //   传输的数据大小
__inout_opt LPOVERLAPPED lpOverlapped     //重叠结构,同步/异步
);

 

首先ring3与ring0的交互有三种方法,

                                                METHOD_BUFFERED:缓冲区模式

SystemBuffer  : 由 系统在ring3 和 ring0之间进行数据的拷贝

          在ring0层,DriverObject->MajorFunction[IRP_MJ_DEVICEIOCONTROL] 中设置的例程中:

1 PIO_STACK_LOCATION   IrpSp;2 IrpSp = IoGetCurrentIrpStackLocation(Irp);    //获得Irp堆栈3 InputBuffer = Irp->AssociatedIrp.SystemBuffer;   //InputBuffer , 由IoManager复制4 OutputBuffer = Irp->AssociatedIrp.SystemBuffer;  //由Iomanager 复制5 InputSize = IrpSp->Parameters.DeviceIoControl.InputBufferLength;6 OutputSize = IrpS->Parameters.DeviceIoControl.OutputBufferLength;

   在完成Io处理以后,对于Irp->IoStatus.Information = OutputSize;   //告知IoManager 要返回复制的内存大小

 

                                   METHOD_IN_DIRECT与METHOD_OUT_DIRECT  直接内存模式

Dricet in/out   :用MDL锁定物理页,在ring3,ring0之间直接读写,一般使用METHOD_IN_DIRECT 

与缓冲模式相同,用户提供的输入缓冲区的内容被复制到IRP中的pIrp->AssociatedIrp.SystemBuffer内存地址,复制的长度是DeviceIoControl指定的输入字节数。

直接内存模式中,操作系统会将DeviceIoControl指定的输出缓冲区的物理页锁定,然后在内核模式地址下重新映射一段地址。适合大量数据的交流,而且相对于METHOD_NEITHER更安全。

1 #define CTL_CODE( DeviceType, Function, Method, Access ) (                 \2     ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) )3 #define CTL_MDL \4     CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_IN_DIRECT,FILE_ANY_ACCESS)

在DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]的例程中获取InputBuffer和OutBuffer

1 InputBuffer = Irp->AssociatedIrp.SystemBuffer;2 OutputBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress,NormalPagePriority);3 InputSize = pIrp->Parameters.DeviceIoControl.InputBufferLength;4 OutputSize = pIrp->Parameters.DeviceIoControl.OutputBufferLength;5 ulIoContorlCode = pIrp->Parameters.DeviceIoControl.IoControlCode;

完成处理之后

1     Irp->IoStatus.Status = STATUS_SUCCESS;2     Irp->IoStatus.Information = OutputSize;3     IoCompleteRequest(Irp,IO_NO_INCREMENT);

 

               METHOD_NEITHER :Neither模式

UserBuffer    : 什么都不是的方法,lpInBuffer 由IoManager 进行拷贝传输,OutBuffer直接访问用户地址空间,较危险,线程切换可能会影响UserBuffer。例如,在我们的进程A中下发的Io控制码,OutBuffer的地址在进程A中比如是0x0018ff44,此时在ring0层中通过 OutBuffer = Irp->UserBuffer得到的地址也是0x0018ff44。而ring0层的地址空间是整个系统公有的。如果在ring0层访问OutBuffer时,此时恰好CPU的用户空间切换到进程B,而ring0依然去读写0x0018ff44的地址的时候,很有可能会崩溃。因为在此时的0x0018ff44是属于进程B的地址空间,而我们不知道这个地址空间是否映射了物理页,如果没有,则会崩溃。

#define CTL_CODE( DeviceType, Function, Method, Access ) (                 \    ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) )#define CTL_MDL \    CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_NEITHER,FILE_ANY_ACCESS)
1 PIO_STACK_LOCATION  IrpSp;2 IrpSp = IoGetCurrentIrpStackLocation(Irp);3 pvInputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;4 pvOutputBuffer = Irp->UserBuffer;    //UserBuffer5 ulOutputLen    = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;6 ProbeForWrite(pvOutputBuffer,ulOutputLen,sizeof(CHAR));    //对用户地址空间进行读写操作,判断是否可写  ProbeForRead()  只能针对用户地址空间7 ulIoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;

   上面写了DeviceIoControl()的几种模式,接下来总结下ring0通过事件通知ring3。比如说,ring0的一个监控程序,指定的事件发生时通知ring3的应用程序,这个时候就要用到事件通知了。利用命名事件来进行通知,而命名事件的创立也有两种途径,一种是在ring0创建事件,在ring3获得事件句柄,等待事件。另一种就是ring3创建事件,由ring0获得事件句柄。

先写第一种在ring0创建事件,由ring3等待。

命名事件的名字一定是在\\BaseNamedObjects\\的目录下,用 WinObj 在指定目录下就可以看到创建的命名事件

#define EVENT_NAME   L"\\BaseNamedObjects\\NotifyEvent"
1 PKEVENT EventObject = NULL;2 HANDLE hEvent = NULL;3 UNICODE_STRING  uniEventName;4 RtlInitUnicodeString(&uniEventName,EVENT_NAME);5 EventObject = IoCreateNotificationEvent(&uniEventName,&hEvent);   //创建命名事件,初始态为Signaled State  KeClearEvent(EventObject);      //变成Unsignaled State  

WDK 对于IoCreateNotificationEvent() 的说明

PKEVENT     //指向事件内核对象  IoCreateNotificationEvent(    IN PUNICODE_STRING  EventName,     //事件名称    OUT PHANDLE  EventHandle      //事件句柄    );

在ring3层调用OpenEvent() 函数对事件进行打开,MSDN对OpenEvent()说明如下

HANDLE WINAPI OpenEvent(  __in  DWORD dwDesiredAccess,  __in  BOOL bInheritHandle,  __in  LPCTSTR lpName);

自己把英文文档翻译一下:

dwDesiredAccess:说明访问方式,如果是对象的安全属性不允许的访问方式就会失败。

          DELETE (0x00010000L)       需要删除对象

          READ_CONTROL (0x00020000L)

          SYNCHRONIZE (0x00100000L)     同步访问事件对象,允许线程等待事件对象成为Signaled State

          WRITE_DAC (0x00040000L)

          WRITE_OWNER (0x00080000L)

lpName:命名事件的名称   这里就是"Global\\NotifyEvent"     命名事件属于整个系统

HEVENT hEvent = OpenEvent(SYNCHRONIZE,FALSE,L"Global\\NotifyEvent");

然后就可以在ring3的线程中进行等待

WaitForSingleObject(hEvent,INFINITE)==WAIT_OBJECT_0      //线程阻塞

 

在ring0 对事件进行触发

KeSetEvent(EventObject,0,FALSE);   //使事件变成Signaled State ,ring3层的WaitForSingleObject()线程得到响应,继续执行
KeClearEvent(EventObject);       //将事件恢复Unsignaled State

在ring0的UnloadDriver()例程中对事件句柄进行关闭,引用计数减1

ZwClose(hEvent);

 由ring0创建命名事件在ring3进行操作,对ring0资源的占用较大。一般选择在ring3创建事件对象,在ring0进行操作。

//MSDN 对于CreateEvent() 的说明 HANDLE WINAPI CreateEvent(  __in_opt  LPSECURITY_ATTRIBUTES lpEventAttributes,  __in      BOOL bManualReset,  __in      BOOL bInitialState,  __in_opt  LPCTSTR lpName);
HANDLE hEvent = CreateEvent(NULL,FALSE,FALSE,NULL)   //创建匿名事件  自动重置

然后将事件句柄下发到ring0    //DeviceIoControl()

在ring0通过事件句柄获得事件对象     ObRefrenceObjectByHandle() 

NTSTATUS   ObReferenceObjectByHandle(    IN HANDLE  Handle,    IN ACCESS_MASK  DesiredAccess,    IN POBJECT_TYPE  ObjectType  OPTIONAL,    IN KPROCESSOR_MODE  AccessMode,    OUT PVOID  *Object,    OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL    );
PKEVENT EventObject ;NTSTATUS   Status = STATUS_SUCCESS;//应用层创建的事件句柄得到事件对象,对事件对象用完之后记得解引用  ObDrefrenceObject(),便于系统对对象资源进行回收Status =ObReferenceObjectByHandle(hEvent,            SYNCHRONIZE,            *ExEventObjectType,              KernelMode,            &EventObject,            NULL);
//WDK  KeSetEvent LONG     KeSetEvent(    IN PRKEVENT  Event,    IN KPRIORITY  Increment,    IN BOOLEAN  Wait    );

在ring0调用KeSetEvent() 使事件变成Signaled State ,ring3 等待事件响应的线程就会向下执行;

在ring0 调用KeWaitForSigleObject() 对事件对象进行等待

// WDK NTSTATUS   KeWaitForSingleObject(    IN PVOID  Object,    IN KWAIT_REASON  WaitReason,    IN KPROCESSOR_MODE  WaitMode,    IN BOOLEAN  Alertable,    IN PLARGE_INTEGER  Timeout OPTIONAL    //为NULL 相当于ring3 的 INFINITE   永久等待    );

 

Status = KeWaitForSingleObject(EventObject,                Executive, KernelMode, FALSE, NULL);

转载于:https://www.cnblogs.com/lanrenxinxin/p/4372915.html

你可能感兴趣的文章
mysql触发器
查看>>
淌淌淌
查看>>
win10每次开机都显示“你的硬件设置已更改,请重启电脑……”的解决办法
查看>>
C++有关 const & 内敛 & 友元&静态成员那些事
查看>>
函数积累
查看>>
Swift 入门之简单语法(六)
查看>>
〖Python〗-- IO多路复用
查看>>
栈(括号匹配)
查看>>
Java学习 · 初识 面向对象深入一
查看>>
源代码如何管理
查看>>
vue怎么将一个组件引入另一个组件?
查看>>
bzoj1040: [ZJOI2008]骑士
查看>>
LeetCode 74. Search a 2D Matrix(搜索二维矩阵)
查看>>
利用SignalR来同步更新Winfrom
查看>>
反射机制
查看>>
CocoaPod
查看>>
BZOJ 1251: 序列终结者 [splay]
查看>>
5G边缘网络虚拟化的利器:vCPE和SD-WAN
查看>>
MATLAB基础入门笔记
查看>>
【UVA】434-Matty&#39;s Blocks
查看>>