驱动程序使用 EVENT 主动和应用程序通信

利用同步事件实现驱动主动通知应用 一、同步事件 对数据结构 KEVENT 进行初始化、等待和设置,实现多个线程之间的同步。例如,希望主线程 A 在等待线程 B 完成某事后才能做某事,可在主线程 A 中配置KEVENT事件等待,在线程B中完成操作后,对正在等待的KEVENT事件进行设置即可。 实例如下: // 定义一个事件 KEVENT event; // 事件初始化 KeInitializeEvent(&even... 阅读详情

背景

用户层程序使用 DeviceIoControl 将 IOCTL 控制码、输入缓冲区、输出缓冲区传入到内核;内核响应 IRP_MJ_DEVICE_CONTRL 消息,并从 IRP 中获取传入的 IOCTL 控制码、输入缓冲区、输出缓冲区,以此实现数据的交互。

但是,当内核层想主动传递数据到用户层,用户层又怎样才能知道呢?因为只有用户层知道内核层有数据输出的时候,它才会调用 DeviceIoControl 函数去获取数据。所以,本文要介绍的就是基于事件 EVENT 实现的同步框架,可以解决这个的问题。

函数介绍

	// 创建或打开一个事件对象。如果想为对象指定一个访问掩码,应当使用CreateEventEx函数。
    HANDLE CreateEvent(
        LPSECURITY_ATTRIBUTESlpEventAttributes, // 安全属性
        BOOLbManualReset,                       // 复位方式
        BOOLbInitialState,                      // 初始状态
        LPCTSTRlpName                           // 对象名称
    );
    
    // 提供对象句柄访问许可。如果访问被允许,返回相应的对象体的指针。
    NTSTATUS ObReferenceObjectByHandle(
      _In_ HANDLE Handle,					// 句柄。
      _In_ ACCESS_MASK DesiredAccess,		// 访问对象的类型。
      _In_opt_ POBJECT_TYPE ObjectType,		// 指向对象类型。
      _In_ KPROCESSOR_MODE AccessMode,		// 访问模式。
      _Out_ PVOID *Object,					// 指向映射句柄对象的指针。
      _In_opt_ POBJECT_HANDLE_INFORMATION HandleInformation		// 驱动程序设置为 NULL。
 	);

实现原理

我们通过事件 EVENT 实现用户层与内核层的同步操作,具体的实现原理如下:

  • 首先,我们在用户层程序中调用 CreateEvent 函数创建事件 EVENT 并获取事件 EVENT 的句柄。事件初始状态为无信号,而且自动复原。
  • 然后,用户层程序调用 DeviceIoControl 函数将上述创建的 EVENT 句柄传递到内核层驱动程序中,并调用 WaitForSingleObject 函数等待事件 EVENT 的响应。直到事件 EVENT 响应后,程序才会进行下一步操作。
  • 接着,内核层程序就通过 IRP_MJ_DEVICE_CONTROL 消息响应函数获取从用户层传入的事件 EVENT 的句柄。调用 ObReferenceObjectByHandle 内核函数获取内核事件 EVENT 对象。
  • 然后,内核驱动程序可以调用 PsCreateSystemThread 创建多线程,继续执行操作。要想通知用户层程序进行下一步操作,只需调用 KeSetEvent 内核函数,将事件 EVENT 对象的状态设置为信号状态。那么,用户层程序中的事件 EVENT 就是一个有信号状态,WaitForSingleObject 函数就不会阻塞,而是继续往下执行。这样,就可以成功从内核层通知到用户层进行操作了。
  • 最后,用户层调用 CloseHandle 关闭事件 EVENT 句柄;内核层调用 ObDereferenceObject 释放内核事件 EVENT 对象。

这个框架的核心原理就是,将用户层的事件句柄传入内核层的驱动程序中,并有内核驱动程序设置事件对象的信号状态,以此触发用户层的响应。

编码实现

用户层代码:

    int _tmain(int argc, _TCHAR* argv[])
    {
        HANDLE hEvent = NULL;
        HANDLE hDevice = NULL;
        char szOutput[MAX_PATH] = { 0 };
        DWORD dwOutput = MAX_PATH;
        DWORD dwRet = 0;
        BOOL bRet = FALSE;
        // 创建事件, 设置自动复位,初始状态为无信号
        hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
        if (NULL == hEvent)
        {
            printf("CreateEvent Error[%d]\n", ::GetLastError());
        }
        // 打开设备
        hDevice = ::CreateFile(SYM_NAME, GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
            FILE_ATTRIBUTE_ARCHIVE, NULL);
        if (INVALID_HANDLE_VALUE == hDevice)
        {
            printf("CreateFile Error[%d]\n", ::GetLastError());
        }
        // 数据交互, 向内核层中传入事件句柄
        bRet = ::DeviceIoControl(hDevice, IOCTL_MY_TEST, &hEvent, sizeof(hEvent), szOutput, dwOutput, &dwRet, NULL);
        if (FALSE == bRet)
        {
            printf("DeviceIoControl Error[%d]\n", ::GetLastError());
        }
        // 一直等待事件的响应
        ::WaitForSingleObject(hEvent, INFINITE);
        // 数据交互, 从内核层中获取数据
        bRet = ::DeviceIoControl(hDevice, IOCTL_MY_OUTPUT, NULL, 0, szOutput, dwOutput, &dwRet, NULL);
        if (FALSE == bRet)
        {
            printf("DeviceIoControl Error[%d]\n", ::GetLastError());
        }
        // 显示
        printf("[From Kernel Output]%s\n", szOutput);
        // 关闭设备句柄
        ::CloseHandle(hEvent);
        ::CloseHandle(hDevice);
        system("pause");
        return 0;
    }

内核层代码:

    // IRP_MJ_DEVICE_CONTROL 消息处理函数
    NTSTATUS DriverControlHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp)
    {
        NTSTATUS status = STATUS_SUCCESS;
        // 获取当前 IRP 栈空间数据
        PIO_STACK_LOCATION pIoStackLocation = IoGetCurrentIrpStackLocation(pIrp);
        // 获取输入/输出缓冲区
        PVOID pBuffer = pIrp->AssociatedIrp.SystemBuffer;
        // 获取输入缓冲区数据长度
        ULONG ulInputLength = pIoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
        // 获取输出缓冲区数据长度
        ULONG ulOutputLength = pIoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
        // 实际输出数据长度
        ULONG ulInfo = 0;
        // 获取控制码
        ULONG ulControlCode = pIoStackLocation->Parameters.DeviceIoControl.IoControlCode;
        // 根据操作码分别进行操作
        switch (ulControlCode)
        {
        case IOCTL_MY_TEST:
        {
            // 获取传入的事件句柄
            HANDLE hUserEvent = *(HANDLE *)pBuffer;
            // 处理类型32位、64位下类型不匹配的情况
            if (4 == ulInputLength)
            {
                hUserEvent = (HANDLE)((SIZE_T)hUserEvent & 0x0FFFFFFFF);
            }
            // 根据事件句柄获取内核事件对象
            status = ObReferenceObjectByHandle(hUserEvent, EVENT_MODIFY_STATE, *ExEventObjectType, KernelMode, (PVOID)(&g_pKernelEvent), NULL);
            if (!NT_SUCCESS(status))
            {
                DbgPrint("ObReferenceObjectByHandle Error[0x%X]\n", status);
                g_pKernelEvent = NULL;
                break;
            }
            // 创建多线程, 执行操作, 执行完毕后, 发送事件通知用户层
            HANDLE hThread = NULL;
            PsCreateSystemThread(&hThread, 0, NULL, NtCurrentProcess(), NULL, ThreadProc, g_pKernelEvent);
            break;
        }
        case IOCTL_MY_OUTPUT:
        {
            RtlCopyMemory(pBuffer, g_szOutputBuffer, 50);
            ulInfo = 50;
            break;
        }
        default:
            break;
        }
        pIrp->IoStatus.Status = status;
        pIrp->IoStatus.Information = ulInfo;
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
        return status;
    }

    // 多线程处理函数
    VOID ThreadProc(PVOID StartContext)
    {
        DbgPrint("Enter ThreadProc\n");
        // 获取内核对象
        PKEVENT pKernelEvent = (PKEVENT)StartContext;
        // 设置输出缓冲区
        RtlCopyMemory(g_szOutputBuffer, "I am DemonGan From Kernel Event.", (1 + strlen("I am DemonGan From Kernel Event.")));
        // 发送事件, 将事件对象设置为信号状态
        if (NULL != pKernelEvent)
        {
            KeSetEvent(pKernelEvent, IO_NO_INCREMENT, FALSE);
        }
        // 释放事件对象
        ObDereferenceObject(pKernelEvent);
        pKernelEvent = NULL;
        DbgPrint("Leave ThreadProc\n");
    }

相关文章:
https://cloud.tencent.com/developer/article/1446024

本地提权漏洞分析【网络安全】 CVE-2023-21752 是 2023 年开年微软第一个有 exploit 的漏洞,原本以为有利用代码会很好分析,但是结果花费了很长时间,难点主要了两个:漏洞点定位和漏洞利用代码分析,欢迎指正。根据官方信息,该漏洞是 Windows Backup Service 中的权限提升漏洞,经过身份认证的攻击者可利用此漏洞提升至 SYSTEM 权限。成功利用此漏洞需要攻击者赢得竞争条件。EXP 代码位于 Github,提供了两个版本,版本 1 可以实现任意文件删除,可稳定复现; 阅读详情

相关推荐

linux2.22.6内核驱动,应用层跟驱动通讯之----用signal(信号)读取按键值,为驱动主动上报到应用

应用层:#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <poll.h>#include <signal.h>#include <sys/types.h>#include <

qq_18720507的博客 1304

设备驱动程序通知应用程序的几种方法

作 者:■ 北京航空航天大学 周正干 李和平 安振刚摘要:在目前流行的Windows操作系统中,设备驱动程序是操纵硬件的最底层软件接口。为了共享在设备驱动程序设计过程中的经验,给出设备驱动程序通知应用程序的5种方法,详细说明每种方法的原理和实现过程,并给出实现的部分核心代码。希望能够给设备驱动程序的设计者提供一些帮助。关键词:设备驱动程序 异步I/O Virtual Device Driv

roger007的专栏 3647

后台按键DLL技术实现与应用详解

随着自动化操作和程序间交互需求的不断增长,后台按键DLL技术作为实现非焦点窗口模拟输入的核心手段之一,逐渐成为软件开发、测试自动化乃至某些特定应用场景中的关键技术。该技术通过将键盘输入逻辑封装于动态链接库(DLL)中,利用Windows系统提供的API或钩子机制,向目标进程发送虚拟按键消息,从而实现对无焦点窗口的“无感知”控制。与传统前台输入依赖用户界面焦点不同,后台按键绕过常规消息路由,直接注入输入流或模拟底层事件,具有更高的灵活性和隐蔽性。// 示例:DLL导出函数声明。

weixin_36483050的博客 885

驱动通知应用层

驱动部分 驱动想要给应用层发送信号,需要用到kill_fasync()函数和xxx_fasync()函数 例如: kill_fasync(&pled->async_queue, SIGRTMIN, POLL_OUT) //第一个参数为特定的结构体 //第二个参数为信号的类型 //第三个参数为信号值 //作用:驱动通过此函数; 发信号给应用程序,应用程序...

qq495553087的博客 1284

别再硬编码了!WinCC子画面动态加载的5种C脚本写法对比

本文深入解析WinCC子画面动态加载的5种C脚本实现方案,包括基础加载、路径动态解析、多语言切换、窗口自适应布局和混合架构交互。通过代码示例和场景分析,帮助开发者避免硬编码问题,提升工业自动化系统的交互效率和可维护性。特别推荐路径动态解析方案,有效解决项目路径管理的痛点。

weixin_29008691的博客 476

文章详细介绍了进行DoIP(DiagnosticoverInternetProtocol)测试的完整过程,包括样件的正确接线、CANoe软件的环境配置、端口设置、VLAN配置、IP和MAC地址的设定

关于Tester和DUT的地址,其实最主要的就是DUT的IP、MAC和逻辑地址,如果不知道Tester的IP、MAC和逻辑地址,可以配置为与DUT同一网段的就可以。ps:最重的是知道DUT的IP、逻辑、MAC地址,如果有VLAN也需要知道,Tester的地址可以根据DUT地址进行设定,其余的值可根据DoIP的报文查到相关参数。ps:如果在通信中没有建立VLAN,但是源地址和目的地址都填写正确,此时就要考虑是否是遗漏了VLAN,建立一个VLAN,在重新试试是否可通信。此处与硬件连接是相对应的,要注意!

qq_40965861的博客 1128

驱动程序和应用程序之间通信

正文 Q: 请问有什么方法实现驱动程序主动和应用程序进行实时通讯,而不用应用程序采用定时查询的方法? 比如驱动有一事件发生需要立即通知应用程序,或驱动程序需要向应用程序读取一些内容. A: 有一个很容易的方式,在驱动程序和应用程序之间用一个事件。 在应用程序CreateFile的时候,驱动程序IoCreateSynchronizationEvent一个有名的事件,然后应用程序CreateEvent

Tommy(凌飞)的专栏 1935

axi ps读写pl_PL读写DDR:Datamover能干什么

最近发现工程项目中一直在用AXI-DMA。这玩意儿搬数据倒是没问题,就是用axi-lite配置起来非常反人类。。。简单的办法其实是用datamover ip核。这个ip核能干嘛呢。准备写个文章解析一下。由于好多feature没用过,所以仅仅看文档可能理解有误,欢迎指出错误。首先扫个盲:MM2S指的是Memory map to stream. 读DDR. S2MM指的是steam to memory...

weixin_35908791的博客 1597

驱动程序和应用程序之间通信(For Win2000 or later version)

作者 Fang 关键字 驱动 应用 通信 原作者姓名 Fang 文章原始出处 根据网上一些资料整理。 正文 Q: 请问有什么方法实现驱动程序主动和应用程序进行实时通讯,而不用应用程序采用定时查询的方法? 比如驱动有一事件发生需要立即通知应用程序,或驱动程序需要向应用程序读取一些内容. A: 有一个很容易的方式,在驱动程序和应用程序之间用一个事件。 在应用程序CreateFile的时候,驱动程序Io

2017

避坑指南:PCIe设备兼容性那些坑——聊聊MPS/MRRS设置不当引发的血泪史

本文深入探讨了PCIe设备兼容性问题,特别是MPS/MRRS参数设置不当引发的常见故障。通过详细解析MPS(Max Payload Size)和MRRS(Max Read Request Size)的作用机制,提供Linux内核五大配置策略的对比分析,并分享FPGA加速卡等实战案例的排查经验。文章还介绍了诊断工具链、动态调试技巧以及性能优化方法,帮助工程师有效解决PCIe兼容性难题。

weixin_30872499的博客 419

用一台笔记本跑起 SAP HANA:从 express edition 的定位,到本地硬件门槛与安装实操

SAP HANA是面向个人开发者、初创团队与教学科研场景的精简发行版,保留了in-memory列存引擎、SQLScriptAFLGraphSpatial等核心能力,可免费用于开发与一定规模的生产用途(上限为 32 GB RAM)。它最大的特征,是能在资源受限的laptop或上快速落地,让你在本地完成从建模、开发到测试的完整闭环。SAP在交付形态上,提供了多种安装包:预配置的VM OVA、面向Linux的二进制安装包,以及历史上出现过的容器化方案;你可以按现有设备与熟悉的运维方式择优选择。官方文档把VM。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 534

驱动程序和应用程序之间通信(For Win2000 or later )

正文 Q: 请问有什么方法实现驱动程序主动和应用程序进行实时通讯,而不用应用程序采用定时查询的方法? 比如驱动有一事件发生需要立即通知应用程序,或驱动程序需要向应用程序读取一些内容. A: 有一个很容易的方式,在驱动程序和应用程序之间用一个事件。 在应用程序CreateFile的时候,驱动程序IoCreateSynchronizationEvent一个有名的事件,然后应用程序CreateEvent

我的编程之旅 1486

收放卷伺服控制系统详细算法介绍(电子齿轮+张力PID卷绕轴控制功能块)

收放卷控制系统涉及的内容非常多,这里我们介绍全伺服系统利用电子齿轮指令实现主从轴的比例随动速度控制,收放卷控制算法介绍常用链接如下1、收放卷+排线控制收放卷+排线控制系统框图-优快云博客文章浏览阅读24次。1、收放卷前馈量计算FC收放卷前馈量计算FC(CODESYS ST源代码)-优快云博客文章浏览阅读16次。1、收放卷前馈量计算FC(博途PLC):收放卷前馈量计算FC(梯形图+SCL代码)-优快云博客文章浏览阅读534次。2、收放卷前馈量计算FC。

RXXW_Dor的博客 3121

【转帖】驱动程序和应用程序之间通信(For Win2000 or later version)

 正文 Q: 请问有什么方法实现驱动程序主动和应用程序进行实时通讯,而不用应用程序采用定时查询的方法? 比如驱动有一事件发生需要立即通知应用程序,或驱动程序需要向应用程序读取一些内容. A: 有一个很容易的方式,在驱动程序和应用程序之间用一个事件。 在应用程序CreateFile的时候,驱动程序IoCreateSynchronizationEvent一个有名的事件,然后应用程序CreateEven

floweronwarmbed的专栏 579

SVN使用:使用TortoiseSVN遇到提示请执行“清理”命令的解决方法

作为程序员来说,在团队开发过程中关于代码管理的工具使用并不陌生,比较常用的代码合并管理工具如:Git、SVN等,虽然相关工具的使用并不复杂,但是还是要掌握常用的使用方法,尤其是相关的第三方软件的使用也是极为重要的,而且有时候使用这些三方软件来进行代码管理的时候也会遇到一些问题。本篇博文就来分享一下在SVN使用的时候使用TortoiseSVN遇到提示请执行“清理”命令的解决方法,方便查阅使用。

全沾软贱开发攻城狮 1万+

一文搞懂数据标注

数据标注即通过分类、画框、标注、注释等,对图片、语音、文本等数据进行处理,标记对象的特征,以作为机器学习基础素材的过程。简言之,就是需要给数据做好标注,这样机器就能知道学习哪些内容。

女王の专属领地 9940

驱动程序如何发通知给应用程序

这里介绍一下常见的两种办法: 1、驱动程序调用API SendNotifyMessage,发送特定的消息给应用程序,这就要求应用程序要有消息循环机制并且要事先做好消息的处理。 参数 1为窗口句柄,可以设置HWND_BROADCAST表示广播消息。要注意的是不要在参数中传递指针(虚拟地址),因为执行驱动程序的线程和应用程序并不在同一个进程空间中。解决办法可以利用内存映射文件技术,比如在驱动程序中...

weixin_30588675的博客 337

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

事件程序驱动代码html,技术分享 - NT驱动程序与用户层程序基于事件EVENT实现同步通信...

背景之前我们在《NT驱动程序与用户层程序通信》这篇文章中讲了,用户层程序使用 DeviceIoControl 将 IOCTL 控制码、输入缓冲区、输出缓冲区传入到内核;内核响应 IRP_MJ_DEVICE_CONTRL 消息,并从 IRP 中获取传入的 IOCTL 控制码、输入缓冲区、输出缓冲区,以此实现数据的交互。但是,当内核层想主动传递数据到用户层,用户层又怎样才能知道呢?因为只有用户层知道内...

weixin_39828193的博客 324

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

wince 应用和驱动通信的几种方式探讨

作者:我想说wince离我远点 转自:http://blog.youkuaiyun.com/lqxandroid2012/article/details/8208908 我想用三种方式来达到驱动程序通知应用程序。     第一种方式:驱动程序通过PostMessage给操作系统发一个信息,再由应用程序通过GetMessage接受处理,这个方式很简单,让应用程序随时处理驱动程序的信息,

老瓦的笔记本 641

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Ring3与Ring0通信方法若干

本人在[(原创)应用程序与驱动通信的若干方式]文章中阐述了,上下层通信的技术实现部分,但没有结合应用,下面的文章就具体应用给大家展示了使用方法,希望能够大家一些启发。               Ring3与Ring0同步是很有用的手段,在此做一个简要的整理,希望对开发这方面程序的朋友有帮助,好了,开始吧。          1 同步的策略          初写驱动的朋友都知道,通过Dev

2220

【保姆级IDF】ESP32使用WIFI作为AP模式TCP通信:连接客户端+一对多通信

ESP32走TCP进行一对多通信

山岚的博客 3169

驱动中的异步通知机制

http://blog.youkuaiyun.com/longshan_2009/article/details/8730345 异步通知机制 之前所说的几种方法: 1.查询:耗资源 2.中断:read()不会返回 3.poll:在指定时间read返回 他们的共同特点都是应用程序主动的去read,有没有一种方法驱动去提醒应用程序去读? 有,那就是异步通知机制 程序之间发信号 kil

cjsycyl的专栏 841

APP访问硬件的四种方式

【代码】APP访问硬件的四种方式。

weixin_42817311的博客 395

Nginx安装及使用

这五种 I/O 模型中,越往后,阻塞越少,理论上效率也是最优前四种属于同步 I/O,因为其中真正的I/O 操作(recvfrom)将阻塞进程/线程,只有异步 I/O 模型才与 POSIX 定义的异步 I/O 相匹配1、select:select库是在linux和windows平台都基本支持的 事件驱动模型库,并且在接口的定义也基本相同,只是部分参数的含义略有差异,最大并发限制1024,是最早期的事件驱动模型。

qq_54235967的博客 1064
上一篇: 创建系统回调实现行为监控
下一篇: 基于AheadLib工具进行DLL劫持
(-: LYSM :-)
博客等级 码龄10年 428粉丝 273原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值