在前面已经介绍了各种如何从3环进入0环的方式了,诸如调用们、中断门、任务门或者驱动加载方式等。那么在正常项目中0环和3环如何进行通讯呢?
设备对象
我们在开发窗口程序时,消息被封装成一个结构体:MSG,在内核开发时,消息被封装成另一个结构体:IRP。在窗口程序中,能够接收消息的只能是窗口对象,在内核中,能够接收IRP消息的只能是设备对象。
创建设备对象
//创建设备名称
UNICODE_STRING Devicename;
RtlInitUnicodeString(&Devicename,L"\Device\MyDevice");
//创建设备
IoCreateDevice(
pDriver,//当前设备所属的驱动对象
0,
&Devicename,//设备对象的名称
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&pDeviceObj//设备对象指针
);
创建的设备对象,存储在pDeviceObj
所指向的地方。
设置交互数据的方式
pDeviceObj->Flags |= DO_BUFFERED_IO;
在驱动程序中,数据读取方式有三种,分别为:
-
缓冲区方式读写(DO_BUFFERED_IO) :操作系统将应用程序提供缓冲区的数据复制到内核模式下的地址中。
-
直接方式读写(DO_DIRECT_IO) :操作系统会将用户模式下的缓冲区锁住。然后操作系统将这段缓冲区在内核模式地址再次映射一遍。这样,用户模式的缓冲区和内核模式的缓冲区指向的是同一区域的物理内存。缺点就是要单独占用物理页面。
-
其他方式读写(在调用IoCreateDevice创建设备后对pDevObj->Flags即不设置DO_BUFFERED_IO也不设置DO_DIRECT_IO此时就是其他方式) :在使用其他方式读写设备时,派遣函数直接读写应用程序提供的缓冲区地址。在驱动程序中,直接操作应用程序的缓冲区地址是很危险的。只有驱动程序与应用程序运行在相同线程上下文的情况下,才能使用这种方式。
创建符号链接
//创建符号链接名称
RtlInitUnicodeString(&SymbolicLinkName,L"\??\MyTestDriver");
//创建符号链接
IoCreateSymbolicLink(&SymbolicLinkName,&Devicename);
特别说明:
-
设备名称的作用是给内核对象用的,如果要在Ring3访问,必须要有符号链接其实就是一个别名,没有这个别名,在Ring3不可见。
-
内核模式下,符号链接是以“??”开头的,如C 盘就是“??C:”
-
而在用户模式下,则是以“\.”开头的,如C 盘就是“\.C:
IRP与派遣函数
在3环开发窗口程序时,窗口对象会根据MSG的不同,找不同的回调函数处理相关的逻辑。
那么在内核中,其实相关的派遣函数就是回调函数。
IRP的类型
-
当应用层通过CreateFile,ReadFile,WriteFile,CloseHandle等函数打开、从设备读取数据、向设备写入数据、关闭设备的时候,会使操作系统产生出IRP_MJ_CREATE,IRP_MJ_READ,IRP_MJ_WRITE,IRP_MJ_CLOSE等不同的IRP。
-
其他类型的IRP
IRP类型 |
来源 |
IRP_MJ_DEVICE_CONTROL |
DeviceControl函数会产生此IRP |
IRP_MJ_POWER |
在操作系统处理电源消息时,产生次IRP |
IRP_MJ_SHUTDOWN |
关闭系统前会产生此IRP |
派遣函数在哪里注册
kd> dt _DRIVER_OBJECT
nt!_DRIVER_OBJECT
+0x000 Type : Int2B
+0x002 Size : Int2B
+0x004 DeviceObject : Ptr32 _DEVICE_OBJECT
+0x008 Flags : Uint4B
+0x00c DriverStart : Ptr32 Void
+0x010 DriverSize : Uint4B
....
+0x030 DriverStartIo : Ptr32 void
+0x034 DriverUnload : Ptr32 void //卸载函数
+0x038 MajorFunction : [28] Ptr32 long //派遣函数
注册派遣函数
NTSTATUS DriverEntry( 。。。。)
{
//设置卸载函数
pDriverObject->DriverUnload = 卸载函数;
//设置派遣函数
pDriverObject->MajorFunction[IRP_MJ_CREATE] = 派遣函数1;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = 派遣函数2;
pDriverObject->MajorFunction[IRP_MJ_WRITE] = 派遣函数3;
pDriverObject->MajorFunction[IRP_MJ_READ] = 派遣函数4;
pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = 派遣函数5;
pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = 派遣函数6;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = 派遣函数7;
pDriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = 派遣函数8;
pDriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = 派遣函数9;
}
IRP_MJ_MAXIMUM_FUNCTION 派遣函数的最大值
派遣函数格式
//派遣函数的格式:
NTSTATUS MyDispatchFunction(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
//处理自己的业务...
//设置返回状态
pIrp->IoStatus.Status = STATUS_SUCCESS;// getlasterror()得到的就是这个值
pIrp->IoStatus.Information = 0;// 返回给3环多少数据 没有填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
通过IRP_MJ_DEVICE_CONTROL交互数据
应用层通过调用DeviceControl函数会产生此IRP.
代码写在下一篇
pai#
原文始发于微信公众号(loochSec):0环与3环通信方式
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论