在进行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] 中设置的例程中:

 PIO_STACK_LOCATION   IrpSp;
IrpSp = IoGetCurrentIrpStackLocation(Irp); //获得Irp堆栈
InputBuffer = Irp->AssociatedIrp.SystemBuffer; //InputBuffer , 由IoManager复制
OutputBuffer = Irp->AssociatedIrp.SystemBuffer; //由Iomanager 复制
InputSize = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
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更安全。

 #define CTL_CODE( DeviceType, Function, Method, Access ) (                 \
((DeviceType) << ) | ((Access) << ) | ((Function) << ) | (Method) )
#define CTL_MDL \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_IN_DIRECT,FILE_ANY_ACCESS)

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

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

完成处理之后

     Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = OutputSize;
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) << ) | ((Access) << ) | ((Function) << ) | (Method) )
#define CTL_MDL \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_NEITHER,FILE_ANY_ACCESS)
 PIO_STACK_LOCATION  IrpSp;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
pvInputBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
pvOutputBuffer = Irp->UserBuffer; //UserBuffer
ulOutputLen = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
ProbeForWrite(pvOutputBuffer,ulOutputLen,sizeof(CHAR)); //对用户地址空间进行读写操作,判断是否可写 ProbeForRead() 只能针对用户地址空间
ulIoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;

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

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

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

#define EVENT_NAME   L"\\BaseNamedObjects\\NotifyEvent"
 PKEVENT EventObject = NULL;
HANDLE hEvent = NULL;
UNICODE_STRING uniEventName;
RtlInitUnicodeString(&uniEventName,EVENT_NAME);
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,,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);

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

  1. Ring0层创建事件,Ring3层接收

    在学习驱动过程中,一个很重要的内容就是Ring3层与Ring0层的通信,方法有很多种,互斥体,信号量,文件等等,用的比较普遍的,还是事件.所以在学习的过程中,做了一个简单的Demo,主要是体会一下方法 ...

  2. ring0和ring3的区别

    现在探讨内核程序和应用程序之间的本质区别.除了能用WDK编写内核程序和阅读一部分Windows的内核代码之外,我们还需要了解它们的本质是什么,它们和我们熟悉的应用程序有什么区别. Intel的x86处 ...

  3. Windows x86/ x64 Ring3层注入Dll总结

    欢迎转载,转载请注明出处:http://www.cnblogs.com/uAreKongqi/p/6012353.html 0x00.前言 提到Dll的注入,立马能够想到的方法就有很多,比如利用远程线 ...

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

    前言 假设说我们的计算机中安装有杀毒软件,那么当我们有意或无意地下载了一个恶意程序后.杀软一般都会弹出一个对话框提示我们,下载的程序非常可能是恶意程序,建议删除之类的.或者杀软就不提示.直接删除了:或 ...

  5. python基础之类和对象、对象之间的交互、类名称空间与对象/实例名称空间

    一 面向对象初识 Python要么是面向过程要么是面向对象. 概念及优缺点: 面向过程的程序设计的核心是过程,过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东 ...

  6. python基础--面向对象基础(类与对象、对象之间的交互和组合、面向对象的命名空间、面向对象的三大特性等)

    python基础--面向对象 (1)面向过程VS面向对象 面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西. ...

  7. 我的Android第五章:通过Intent实现活动与活动之间的交互

    Intent在活动的操作 作用: Itent是Android程序中各个组件直接交换的一个重要方式可以指定当前组件要执行任务同时也可以给各个组件直接进行数据交互              同时Inten ...

  8. Fragments之间的交互(实现参数传递)

    Fragments之间的交互(实现参数传递) 日常开发中,通常Fragments之间可能需要交互,比如基于用户事件改变Fragment的内容.所有Fragment之间的交互需要通过他们关联的Activ ...

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

    前言: 上节我们学习到了指令和控制器之间的交互,通过给指令添加动作,调用了控制器中的方法.本节我们学习指令和指令之间是如何交互的,我们通过一个小游戏来和大家一起学习,听大漠老师说这是国外的人写的dem ...

随机推荐

  1. 戏说PHP的嵌套函数

    PHP很早就支持嵌套函数了.并是不PHP5.3有闭包时才有的.然而,它却不是象JS,AS那样的闭包嵌套.即它的嵌套函数根本无闭包模式的逃脱. PHP嵌套函数有一些特别之处.最特别的是,当外部函数被调用 ...

  2. P3401: [Usaco2009 Mar]Look Up 仰望

    这道题第一眼还以为是树状数组,于是乎打着打着也是能过的 ; var n,i,j,maxx:longint; h,l:array[..] of longint; p:array[..] of longi ...

  3. 【收藏】Linux添加/删除用户和用户组

    1.建用户: adduser phpq                             //新建phpq用户 passwd phpq                               ...

  4. 评价正在使用输入法软件产品----QQ拼音输入法

    评价一下大家手头正在使用输入法或者搜索类的软件产品. 我现在使用的是系统自带的QQ拼音输入法,以前使用的是搜狗拼音输入法,后来发现可能由于我的系统重装过好几次,搜狗输入法也重装了好几次,而每次都删不干 ...

  5. 理解bashrc和profile[转载]

    这儿有一篇文章不错 https://wido.me/sunteya/understand-bashrc-and-profile/ http://blog.csdn.net/luotuo44/artic ...

  6. python-根据字符串动态生成对象eval

    # -*- coding: utf-8 -*- stock1={ 'stockName':"沈阳机床", ", 'averagePrice_yesterday':34.0 ...

  7. JavaScript的DOM操作(2)

    补充:   回车符\r和换行符\n的区别:\r 相当于enter,是段落与段落之间的区别, \n 相当于shift+enter,是行与行之间距离,比较小 几种window操作方法: 1.获取当前窗口大 ...

  8. it小小鸟心得

    本来打算就这么浑浑噩噩的过完我的大学四年生涯,但当我读完这本书,改变了我的想法.许多人说大学是最美好的,确实,在这里,我每天都不用认真听讲,不用准时去上课,可是久而久之,自己有感而发觉得这样碌碌无为下 ...

  9. ZOJ Monthly, July 2015

    B http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5552 输入n,表示有n个数1到n.A先拿,B后拿,依次拿,每次可以拿任意一 ...

  10. C++中的RAII机制

    http://www.jellythink.com/archives/101 前言 在写C++设计模式——单例模式的时候,在写到实例销毁时,设计的GC类是很巧妙的,而这一巧妙的设计就是根据当对象的生命 ...