ring0 与 ring3 层之间的交互

  在进行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;    //UserBuffer
5 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);
时间: 2024-10-07 00:54:13

ring0 与 ring3 层之间的交互的相关文章

ring0和ring3的区别

现在探讨内核程序和应用程序之间的本质区别.除了能用WDK编写内核程序和阅读一部分Windows的内核代码之外,我们还需要了解它们的本质是什么,它们和我们熟悉的应用程序有什么区别. Intel的x86处理器是通过Ring级别来进行访问控制的,级别共分4层,从Ring0到Ring3(后面简称R0.R1.R2.R3).R0层拥有最高的权限,R3层拥有最低的权限.按照Intel原有的构想,应用程序工作在R3层,只能访问R3层的数据:操作系统工作在R0层,可以访问所有层的数据:而其他驱动程序位于R1.R2

Windows x86/ x64 Ring3层注入Dll总结

0x01.前言 提到Dll的注入,立马能够想到的方法就有很多,比如利用远程线程.Apc等等,这里我对Ring3层的Dll注入学习做一个总结吧. 我把注入的方法分成六类,分别是:1.创建新线程.2.设置线程上下背景文,修改寄存器.3.插入Apc队列.4.修改注册表.5.挂钩窗口消息.6.远程手动实现LoadLibrary. 那么下面就开始学习之旅吧! 0x02.预备工作 在涉及到注入的程序中,提升程序的权限自然是必不可少的,这里我提供了两个封装的函数,都可以用于提权.第一个是通过权限令牌来调整权限

病毒木马查杀实战第020篇:Ring3层主动防御之基本原理

本系列教程版权归"i春秋"所有,转载请标明出处.        本文配套视频教程,请访问"i春秋"(www.ichunqiu.com). 前言 如果说我们的计算机中安装有杀毒软件,那么当我们有意或无意地下载了一个恶意程序后,杀软一般都会弹出一个对话框提示我们,下载的程序很可能是恶意程序,建议删除之类的,或者杀软就不提示,直接删除了:或者当我们运行了某一个程序,包含有可疑操作,比如创建开机启动项,那么杀软一般也会对此进行提醒:或者当我们在计算机中插入U盘,杀软往往也会

AngularJs-指令和指令之间的交互(动感超人)

前言: 上节我们学习到了指令和控制器之间的交互,通过给指令添加动作,调用了控制器中的方法.本节我们学习指令和指令之间是如何交互的,我们通过一个小游戏来和大家一起学习,听大漠老师说这是国外的人写的demo,我们可以借鉴学习. 1,动感超人 上面的三个按钮,代表三个超人,在此想问下,哪些想看超人的朋友们是不是有种被骗了的感觉? 当我们的鼠标移动到哪个超人的身上的时候,就会输入这个超人所拥有的超能力(力量 + 敏捷 + 发光) <!DOCTYPE html> <html ng-app=&quo

Fragment与Activiy之间的交互

为了重用Fragment UI组件,我们应该把每一个fragment都构建成完全的自包含的.模块化的组件,定义他们自己的布局与行为.定义好这些模块化的Fragment后,就可以让他们关联activity,使他们与application的逻辑结合起来,实现全局的复合的UI. 通常fragment之间可能会需要交互,比如基于用户事件改变fragment的内容.所有fragment之间的交互需要通过他们关联的activity,两个fragment之间不应该直接交互. 定义一个接口 为了让fragmen

Android学习笔记(十六)——碎片之间进行交互(附源码)

碎片之间进行交互 点击下载源码 很多时候,一个活动中包含一个或者多个碎片,它们彼此协作,向用户展示一个一致的UI.在这种情况下,碎片之间能进行通信并交换数据十分重要. 1.使用上一篇中创建的同一个项目,在fragment.xml中添加TextView的标识id: android:id="@+id/lblFragment1" 2.在fragment2.xml中添加一个Button,用于与fragment1进行交互: <Button android:id="@+id/btn

Salesforce视图与控制器之间的交互

刚接触Salesforce,过程的确是比较艰难了,中文资料几乎没有,看英文资料学的效率却不高,不过看了一段时间的英文资料发现自己英语水平挺高不少啊,现在看都不用工具翻译,早知道就再次尝试报个6级,看下能过不,嘻嘻....Salesforce的开发也是MVC模式,asp.net的MVC就玩的比较多了,换个平台一下子没适应过来,不过原理都一样,接下来就介绍一下最近的学习成果吧,来看一下SF中MVC模式下视图与控制器之间的交互,先贴控制器和视图的代码,下面有详细讲解. apex视图代码如下: <ape

深入分析:Android中app之间的交互

在我们开发Android App应用的时候,有些需求需要我们启动其他的App来处理一些逻辑,例如我们需要根据一个地址来调用系统或者相关的地图Map App,这样我们不用在自己的App中编写相应的功能,而是通过Intent来发送一些请求,调用相关的应用来处理这些请求.并且我们称这种Intent为隐式的Intent:这种隐式的Intent是相对于显式的Intent来讲的.显式的Intent我们都比较熟悉,显式的Intent常常需要声明类的名称,而隐式的Intent我们需要声明一个Action,我们A

关于Ring3层的注册表监控

最近一直想做远程操作的注册表,将客户端的注册表发送到主控端,遇到两个问题: 1.不能每次点击TreeControl都是一次请求的发送,太浪费资源. 2.在客户端的注册表监控效果也不是很好.(驱动不稳定,只想用Ring3层) 第一个问题比较好解决,在主控端加一个缓存结构就Ok,但是第二个问题还有一些问题. 常用的注册表监控一般都会使用钩子,Hook有关注册表操作的函数.但是这种方法是针对进程 而言,如果要监控全局,就要对每个进程Inject,这基本不现实. 一个使用DETOUR库的RegQuery