news 2026/2/9 23:08:40

ring0与ring3通信

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ring0与ring3通信

前置知识

IRP是什么

IRP的处理机制类似于windows窗口程序中的“消息处理”机制,驱动程序接收到不同类型的IRP后,会进入不同的派遣函数,在派遣函数中IRP得到处理。

派遣函数是什么

用户模式下所有对驱动程序的I/O请求,全部由操作系统转换为一个叫做IRP的数据结构,不同的IRP数据会被“派遣”道不同的派遣函数中,这也是派遣函数名字的由来。

驱动程序并不是“被直接调用”的,而是由操作系统根据 IRP 的类型进行派遣。

每一种 IRP 都有一个“主功能码(Major Function)”,例如:

  • IRP_MJ_CREATE:打开设备(CreateFile)
  • IRP_MJ_CLOSE:关闭设备(CloseHandle)
  • IRP_MJ_DEVICE_CONTROL:设备控制(DeviceIoControl)

驱动通过注册 派遣函数,告诉系统:

“当收到某种类型的 IRP 时,请调用我这个函数来处理。”

用户模式发起操作(如 DeviceIoControl) 操作系统将其转换为 IRP 根据 IRP 类型,进入驱动中对应的派遣函数 驱动在派遣函数中完成处理并返回结果

什么是设备对象(DEVICE_OBJECT)?

设备对象是驱动在内核中创建的、用于接收 I/O 请求的内核对象。

在 Windows 中,驱动程序本身并不能直接接收 IRP,
真正接收 IRP 的,是驱动创建的 设备对象(DEVICE_OBJECT)。

在代码中,对应的就是:

IoCreateDevice(...)

创建成功后,系统会为该驱动生成一个设备对象,
之后所有发往该设备的 IRP,都会被派遣到这个设备对象所属的驱动中。

对于我们这种 0 环 / 3 环通信驱动来说:

这个设备并不对应真实硬件, 而是一个“纯软件设备”, 用来承载用户态与驱动之间的通信。

设备名(\Device\xxx)是做什么的?

设备对象在内核中必须有一个唯一的名字,
这个名字存在于 内核对象命名空间 中,通常以 \Device\ 开头,例如:

\\Device\\MyDevice

这个名字的特点是:

  • 只在内核态可见

  • Ring3 程序不能直接使用这个名字打开设备

  • 它的主要作用是:在内核中唯一标识一个设备对象

为什么还需要符号链接?

既然 Ring3 不能直接访问 \Device\MyDevice,
那用户程序如何找到这个设备呢?

答案是:通过符号链接(Symbolic Link)。

驱动可以创建一个符号链接,例如:

\\??\\MyDevice->\\Device\\\MyDevice

代码

ring3

#include<stdio.h>#include<windows.h>#include<winioctl.h>#defineOPER1CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)#defineOPER2CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)/***************************************************************************///打开驱动服务句柄//打开三环链接名:\\\\.\\Device/***************************************************************************/intmain(void){DWORD dwInBuffer=0x123456789;TCHAR szOutBuffer[10]={0};DWORD dw;//1. 通过符号链接,打开设备HANDLE g_hDevice=CreateFileW(L"\\\\.\\MyDevice",// 设备名,例如 "\\\\.\\MyDevice"GENERIC_READ|GENERIC_WRITE,// 读写权限0,// 共享模式:0 表示不共享0,// 安全属性:NULL = 默认OPEN_EXISTING,// 只能打开已存在的设备FILE_ATTRIBUTE_NORMAL,// 普通文件属性(对设备来说一般无所谓)0// 模板句柄:不用,传0);//2. 测试通信DeviceIoControl(g_hDevice,//设备句柄OPER2,//操作码&dwInBuffer,//输入缓冲区地址sizeof(dwInBuffer),//输入缓冲区长度szOutBuffer,//输出缓冲区地址sizeof(szOutBuffer),//输出缓冲区长度&dw,//返回长度NULL);//指向OVERLAPPED 此处为NULLprintf("%x",*(int*)szOutBuffer);//3. 关闭设备CloseHandle(g_hDevice);system("pause");return0;}

ring0

#include<ntifs.h>#defineOPER1CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)#defineOPER2CTL_CODE(FILE_DEVICE_UNKNOWN,0x900,METHOD_BUFFERED,FILE_ANY_ACCESS)#defineDEVICE_NAMEL"\\Device\\MyDevice"// Ring3用CreateFile打开设备时,用"\\\\.\\MyDevice"#defineSYMBOLICLINK_NAMEL"\\??\\MyDevice"// IRP_MJ_CREATE处理函数//参数1:一个驱动可以有多个 DEVICE_OBJECT,所以同一个分发函数可以用 pDevObj 区分“这次是哪个设备收到的 IRP”。//参数2:这次 I/O 请求对应的 IRP 结构体,里面记录了://1.这是一个 Create 请求(MajorFunction = IRP_MJ_CREATE)//2.打开方式、共享模式等参数//3.一个 IO_STATUS_BLOCK:pIrp->IoStatusNTSTATUSIrpCreateProc(PDEVICE_OBJECT pDevObj,PIRP pIrp){DbgPrint("(mydriver)DispatchCreate ... \n");//每个 IRP 里都有一个 IO_STATUS_BLOCK IoStatus//IoStatus.Status:本次 I/O 请求的 NTSTATUS 返回码//IoStatus.Information:与这次请求相关的“额外信息”(比如读/写了多少字节)//你必须显式把 IoStatus.Status 设置成成功或失败,//否则就是“垃圾值”,I/O 管理器按照那个垃圾值判断,于是 Ring3 的 CreateFile 返回失败。pIrp->IoStatus.Status=STATUS_SUCCESS;//这个字段的含义取决于 是什么 IRP://对 IRP_MJ_READ:常常表示“实际读取了多少字节”//对 IRP_MJ_WRITE:常常表示“实际写入了多少字节”//对 IRP_MJ_DEVICE_CONTROL:表示“返回给用户缓冲区的数据长度”//对 IRP_MJ_CREATE / IRP_MJ_CLOSE:一般没有额外数据需要告诉 Ring3,所以设为 0pIrp->IoStatus.Information=0;//告诉 I/O 管理器:“这个 IRP 我处理完了,可以结束这次 I / O 请求了。”//如果不调用IoCompleteRequest,Ring3 那边的 CreateFile 会一直等(同步调用),永远等不到结果,表现就是程序卡死/挂住。IoCompleteRequest(pIrp,IO_NO_INCREMENT);//return STATUS_SUCCESS; 和 IoStatus.Status 的关系//这里返回值也是 NTSTATUS,I / O 管理器确实也会用这个值,但真正传回 Ring3 的是 pIrp->IoStatus.Status。returnSTATUS_SUCCESS;}// IRP_MJ_CLOSE处理函数NTSTATUSIrpCloseProc(PDEVICE_OBJECT pDevObj,PIRP pIrp){DbgPrint("(mydriver)DispatchClose ... \n");pIrp->IoStatus.Status=STATUS_SUCCESS;pIrp->IoStatus.Information=0;IoCompleteRequest(pIrp,IO_NO_INCREMENT);returnSTATUS_SUCCESS;}// IRP_MJ_DEVICE_CONTROL处理函数 用来处理与Ring3交互NTSTATUSIrpDeviceControlProc(PDEVICE_OBJECT pDevObj,PIRP pIrp){NTSTATUS status=STATUS_INVALID_DEVICE_REQUEST;ULONG uRead;//当设置交互模式为pDeviceObj->Flags |= DO_BUFFERED_IO;那 I/O 管理器会://1.在内核态分配一块缓冲区:SystemBuffer//2.把 Ring3 的输入缓冲区(InBuff)拷贝过来//3.把 SystemBuffer 指针放到 pIrp->AssociatedIrp.SystemBuffer//4.完成 IRP 时,再把 SystemBuffer 前 IoStatus.Information 个字节拷回 Ring3 的输出缓冲区(OutBuff)//设置临时变量的值PIO_STACK_LOCATION pIrpStack=IoGetCurrentIrpStackLocation(pIrp);// 获取控制码ULONG uIoControlCode=pIrpStack->Parameters.DeviceIoControl.IoControlCode;// 获取缓冲区地址(输入和输出的缓冲区都是一个)PVOID pIoBuffer=pIrp->AssociatedIrp.SystemBuffer;// Ring3 发送数据的长度:用户写进来的数据长度(你最多只能读这么多字节)。ULONG uInLength=pIrpStack->Parameters.DeviceIoControl.InputBufferLength;// Ring0 发送数据的长度:用户预留的输出缓冲区最大长度(你最多填这么多字节)。ULONG uOutLength=pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;switch(uIoControlCode){caseOPER1:{DbgPrint("(mydriver)IrpDeviceControlProc -> OPER1 ... \n");pIrp->IoStatus.Information=0;status=STATUS_SUCCESS;break;}caseOPER2:{DbgPrint("(mydriver)IrpDeviceControlProc -> OPER2 接收字节数:%d \n",uInLength);DbgPrint("(mydriver)IrpDeviceControlProc -> OPER2 输出字节数:%d \n",uOutLength);// Read From Buffermemcpy(&uRead,pIoBuffer,4);DbgPrint("(mydriver)IrpDeviceControlProc -> OPER2 ... %x \n",uRead);// Write To BufferuRead++;memcpy(pIoBuffer,&uRead,4);// Set StatuspIrp->IoStatus.Information=4;status=STATUS_SUCCESS;break;}}// 设置返回状态pIrp->IoStatus.Status=status;IoCompleteRequest(pIrp,IO_NO_INCREMENT);returnstatus;}VOIDDriverUnload(PDRIVER_OBJECT pDriver){UNICODE_STRING SymbolicLinkName={0};DbgPrint("(mydriver)驱动程序停止运行了 . \r\n");// 删除符号链接 删除设备RtlInitUnicodeString(&SymbolicLinkName,SYMBOLICLINK_NAME);IoDeleteSymbolicLink(&SymbolicLinkName);IoDeleteDevice(pDriver->DeviceObject);}NTSTATUSDriverEntry(PDRIVER_OBJECT pDriver,PUNICODE_STRING pReg){DbgPrint("(mydriver)驱动程序开始运行了 . \r\n");//初始化ring0设备名称UNICODE_STRING Devicename;RtlInitUnicodeString(&Devicename,DEVICE_NAME);// 创建设备PDEVICE_OBJECT pDeviceObj=NULL;NTSTATUS status=IoCreateDevice(pDriver,//驱动对象0,//设备扩展大小:为每个设备对象 额外分配的扩展空间大小&Devicename,//设备名称FILE_DEVICE_UNKNOWN,//设备类型FILE_DEVICE_SECURE_OPEN,//设备特性FALSE,//是否“独占设备”:TRUE:同一时间只能有 一个 句柄打开这个设备。第二个进程再 CreateFile 就会失败。//FALSE:多个进程 / 线程可以同时打开。&pDeviceObj);//输出:系统创建好的 PDEVICE_OBJECT 指针,通过它你以后能访问到设备扩展、标志位等等。if(status!=STATUS_SUCCESS){DbgPrint("(mydriver)创建设备失败! \r\n");returnstatus;}//设置交互数据的方式pDeviceObj->Flags|=DO_BUFFERED_IO;// 创建符号链接名称(给ring3用)UNICODE_STRING SymbolicLinkName;RtlInitUnicodeString(&SymbolicLinkName,SYMBOLICLINK_NAME);// 创建符号链接status=IoCreateSymbolicLink(&SymbolicLinkName,&Devicename);if(status!=STATUS_SUCCESS){DbgPrint("(mydriver)创建符号链接失败! \r\n");IoDeleteDevice(pDeviceObj);returnstatus;}// 设置分发函数和卸载函数pDriver->MajorFunction[IRP_MJ_CREATE]=IrpCreateProc;pDriver->MajorFunction[IRP_MJ_CLOSE]=IrpCloseProc;pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL]=IrpDeviceControlProc;pDriver->DriverUnload=DriverUnload;pDriver->DriverUnload=DriverUnload;return0;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/8 13:55:28

46、商业科技管理的战略有效性与数据驱动洞察

商业科技管理的战略有效性与数据驱动洞察 1. 发明 - 创新 - 商业化的价值链条 在商业科技的发展进程中,存在着一条清晰的价值链条,即从发明到创新,再到商业化。发明是创新的基础,创新则是将发明转化为可推向市场的产品或服务的过程,而商业化则是实现投资回报的最终环节。…

作者头像 李华
网站建设 2026/2/6 9:57:17

48、技术商业化与商业智能的发展策略

技术商业化与商业智能的发展策略 1. 技术商业化评估与规划 1.1 技术评估要点 在评估一项技术的商业化潜力时,有几个关键问题需要考虑: 1. 这项技术能否融入正确的技术开发、营销以及渠道联盟与合作中? 2. 该技术是否处于目标买家的“政治最佳点”? 3. 组织是否拥有将…

作者头像 李华
网站建设 2026/2/6 22:04:21

2、大规模基础设施管理:从发布管理到架构设计的全面指南

大规模基础设施管理:从发布管理到架构设计的全面指南 1. 发布管理 发布管理是规划、设计和构建将产品或软件投入生产所需框架的过程。在基础设施管理方面,发布管理确保基础设施组件能高效地部署到生产环境。对于软件而言,它保证新代码能交付给最终用户或使用该代码的终端系…

作者头像 李华
网站建设 2026/2/5 23:45:50

21、Free Haven:匿名存储系统的设计与实现

Free Haven:匿名存储系统的设计与实现 在当今数字化时代,数据隐私和安全变得至关重要。匿名存储系统为用户提供了一种保护个人信息和数据的方式,使得数据的存储和访问更加安全和私密。Free Haven作为一种先进的匿名存储系统,具有独特的设计和功能,旨在满足用户对数据隐私…

作者头像 李华
网站建设 2026/2/5 15:47:13

12、数据管理与配置管理全解析

数据管理与配置管理全解析 1. Git 协议详解 Git 使用 GNU Privacy Guard (GPG) 密钥对提交进行签名,以此确认作者或工具的真实性。访问 Git 的协议主要有以下几种: - Local(本地协议) :该协议在本地机器上工作,并非远程操作。若存在一个可被所有需要 Git 的客户端访…

作者头像 李华