驱动开发:通过Async反向与内核通信

在前几篇文章中给大家具体解释了驱动与应用层之间正向通信的一些经典案例,本章将继续学习驱动通信,不过这次我们学习的是通过运用 Async异步模式实现的反向通信,反向通信机制在开发中时常被用到,例如一个杀毒软件如果监控到有异常进程运行或有异常注册表被改写后,该驱动需要主动的通知应用层进程让其知道,这就需要用到驱动反向通信的相关知识点,如下将循序渐进的实现一个反向通信案例。

在开始学习Async反向通信之前先来研究一个Sync正向通信案例,不论是正向反向通信其在通信模式上与 《驱动开发:通过ReadFile与内核层通信》所介绍的通信模式基本一致,都是通过 ReadFile触发驱动中的 IRP_MJ_READ读取派遣,唯一的区别是在传输数据时使用了 MmGetSystemAddressForMdl方式,它将给定 MDL描述的物理页面映射到系统空间,并调用 RtlCopyMemory()将全局字符串复制到这个空间内,这样客户端就可以循环读取内核传出的数据。

我们来看驱动端代码是如何实现的这个功能,代码并没有什么特殊的无法理解的点,只是需要注意我们在驱动入口调用 IoCreateDevice()时传入了第二个参数 FILE_DEVICE_EXTENSION,该参数的作用是,创建设备时,指定设备扩展内存的大小,传一个值进去,就会给设备分配一块非页面内存。

#include
#include

// 保存一段非分页内存,用于给全局变量使用
#define FILE_DEVICE_EXTENSION 4096

// 定义全局字符串
static int global_count = 0;
static char global_char[5][128] = { 0 };

// 驱动绑定默认派遣函数
NTSTATUS _DefaultDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp)
{
    _pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
    _pIrp->IoStatus.Information = 0;
    IoCompleteRequest(_pIrp, IO_NO_INCREMENT);
    return _pIrp->IoStatus.Status;
}

// 驱动创建后触发
NTSTATUS _SyncCreateCloseDispatch(PDEVICE_OBJECT _pDevcieObject, PIRP _pIrp)
{
    _pIrp->IoStatus.Status = STATUS_SUCCESS;
    _pIrp->IoStatus.Information = 0;
    IoCompleteRequest(_pIrp, IO_NO_INCREMENT);
    return _pIrp->IoStatus.Status;
}

// 应用层读数据后触发
NTSTATUS _SyncReadDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp)
{
    NTSTATUS status = STATUS_SUCCESS;
    PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(_pIrp);
    PVOID pBuffer = NULL;
    ULONG uBufferLen = 0;

    do
    {
        // 读写请求使用的是直接I/O方式
        pBuffer = MmGetSystemAddressForMdl(_pIrp->MdlAddress);
        if (pBuffer == NULL)
        {
            status = STATUS_UNSUCCESSFUL;
            break;
        }
        uBufferLen = pIrpStack->Parameters.Read.Length;
        DbgPrint("读字节长度: %d \n", uBufferLen);

        // 最大支持20字节读请求
        uBufferLen = uBufferLen >= 20 ? 20 : uBufferLen;

        // 输出五次字符串
        if (global_count < 5)
        {
            RtlCopyMemory(pBuffer, global_char[global_count], uBufferLen);
            global_count = global_count + 1;
        }

    } while (FALSE);

    // 填写返回状态及返回大小
    _pIrp->IoStatus.Status = status;
    _pIrp->IoStatus.Information = uBufferLen;

    // 完成IRP
    IoCompleteRequest(_pIrp, IO_NO_INCREMENT);
    return status;
}

// 卸载驱动
VOID _UnloadDispatch(PDRIVER_OBJECT _pDriverObject)
{
    // 删除创建的设备
    UNICODE_STRING  Win32DeviceName;
    RtlInitUnicodeString(&Win32DeviceName, L"\\DosDevices\\LySharkSync");
    IoDeleteDevice(_pDriverObject->DeviceObject);
}

// 驱动入口
NTSTATUS DriverEntry(PDRIVER_OBJECT _pDriverObject, PUNICODE_STRING _pRegistryPath)
{
    UNICODE_STRING DeviceName, Win32DeivceName;
    PDEVICE_OBJECT pDeviceObject = NULL;
    NTSTATUS status;
    HANDLE hThread;
    OBJECT_ATTRIBUTES ObjectAttributes;

    // 设置符号名
    RtlInitUnicodeString(&DeviceName, L"\\Device\\LySharkSync");
    RtlInitUnicodeString(&Win32DeivceName, L"\\DosDevices\\LySharkSync");

    // 循环初始化IRP函数
    for (ULONG i = 0; i MajorFunction[i] = _DefaultDispatch;
    }

    // 再次覆盖派遣函数
    _pDriverObject->MajorFunction[IRP_MJ_CREATE] = _SyncCreateCloseDispatch;
    _pDriverObject->MajorFunction[IRP_MJ_CLOSE] = _SyncCreateCloseDispatch;
    _pDriverObject->MajorFunction[IRP_MJ_READ] = _SyncReadDispatch;
    _pDriverObject->DriverUnload = _UnloadDispatch;

    // 分配一个自定义扩展 大小为sizeof(DEVEXT)
    // By: LyShark.com
    status = IoCreateDevice(_pDriverObject, sizeof(FILE_DEVICE_EXTENSION), &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
    if (!NT_SUCCESS(status))
        return status;

    if (!pDeviceObject)
        return STATUS_UNEXPECTED_IO_ERROR;

    // 为全局变量赋值
    strcpy(global_char[0], "hi,lyshark A");
    strcpy(global_char[1], "hi,lyshark B");
    strcpy(global_char[2], "hi,lyshark C");
    strcpy(global_char[3], "hi,lyshark D");
    strcpy(global_char[4], "hi,lyshark E");

    // 指定读写方式为 直接I/O MDL模式
    pDeviceObject->Flags |= DO_DIRECT_IO;

    // 数据传输时地址校验大小
    pDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT;
    status = IoCreateSymbolicLink(&Win32DeivceName, &DeviceName);

    pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
    return STATUS_SUCCESS;
}

对于应用层来说并没有什么特别的,同样调用 ReadFile读取内核中的参数,同样for循环读取五次,代码如下:

#include
#include

int main(int argc, char *argv[])
{
    HANDLE hFile;
    char Buffer[10] = { 0 };
    DWORD dwRet = 0;
    BOOL bRet;

    hFile = CreateFileA("\\\\.\\LySharkSync", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        return 0;

    for (int x = 0; x < 5; x++)
    {
        bRet = ReadFile(hFile, Buffer, 20, &dwRet, NULL);
        if (!bRet)
        {
            CloseHandle(hFile);
            return 0;
        }
        printf("读入数据: %s -> 读取长度: %d \n", Buffer, dwRet);
    }
    return 0;
}

这段代码运行效果如下:

驱动开发:通过Async反向与内核通信

与同步模式不同, &#x5F02;&#x6B65;&#x6A21;&#x5F0F;虽然同样使用 ReadFile实现通信,但在通信中引入了 Event事件通知机制,这也是异步与同步最大的区别所在,用户层可以分别创建多个Event事件,等待内核依次做出相应并最终一并返回。

首先驱动内定义了 _DeviceExtension自定义接口,该接口用于保存此次事件所对应的 Irp以及其所对应的DPC时间等。

异步分发函数 _AsyncReadDispatch同样是被 IRP_MJ_READ派遣函数触发的,触发后其内部会首先 IoGetCurrentIrpStackLocation得到当前IRP的堆栈信息,然后设置 IoMarkIrpPending()并最终将该IRP通过 InsertTailList()插入到IRP链表内等待被处理。

  • IoMarkIrpPending
  • 用于标记指定的IRP,标志着某个驱动的分发例程(分发函数)因需要被其他的驱动程序进一步处理最终返回STATUS_PENDING状态。

函数 _CustomDpc则是定时器内部要执行的具体操作,在 DriverEntry驱动入口处做了如下初始化,初始化了链表,并初始化了一个定时器,最后启动这个定时器每隔1秒都会执行一次 _CustomDpc如果我们的IRP链表内 IsListEmpty() &#x68C0;&#x6D4B;存在数据,则会主动拷贝内存 RtlCopyMemory并推送到应用层。

// 初始化IRP链表
InitializeListHead(&pDevExt->IrpList);
// 初始化定时器
KeInitializeTimer(&(pDevExt->timer));
// 初始化DPC pDevExt是传给_CustomDpc函数的参数
KeInitializeDpc(&pDevExt->dpc, (PKDEFERRED_ROUTINE)_CustomDpc, pDevExt);

// 设置定时时间位1s
pDevExt->liDueTime = RtlConvertLongToLargeInteger(-10000000);
// 启动定时器
KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc);

驱动层完成代码如下所示:

#include

// 自定义接口扩展
typedef struct _DeviceExtension
{
    LIST_ENTRY IrpList;
    KTIMER timer;
    LARGE_INTEGER liDueTime;
    KDPC dpc;
}DEV_EXT, *PDEV_EXT;

// 默认派遣函数
NTSTATUS _DefaultDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp)
{
    _pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;
    _pIrp->IoStatus.Information = 0;
    IoCompleteRequest(_pIrp, IO_NO_INCREMENT);
    return _pIrp->IoStatus.Status;
}

// 创建派遣函数
NTSTATUS _AsyncCreateCloseDispatch(PDEVICE_OBJECT _pDevcieObject, PIRP _pIrp)
{
    _pIrp->IoStatus.Status = STATUS_SUCCESS;
    _pIrp->IoStatus.Information = 0;
    IoCompleteRequest(_pIrp, IO_NO_INCREMENT);
    return  _pIrp->IoStatus.Status;
}

// 读取派遣函数
NTSTATUS _AsyncReadDispatch(PDEVICE_OBJECT _pDeviceObject, PIRP _pIrp)
{
    NTSTATUS status;
    PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(_pIrp);
    PDEV_EXT pDevExt = (PDEV_EXT)_pDeviceObject->DeviceExtension;

    IoMarkIrpPending(_pIrp);

    // 将IRP插入自定义链表中插入的是ListEntry
    InsertTailList(&pDevExt->IrpList, &_pIrp->Tail.Overlay.ListEntry);

    // 返回pending 主要返回给I/O管理器的值必须和IRP的Pending标志位一致
    // By: LyShark.com
    // 即调用iomarkirppending和返回值要一致
    return STATUS_PENDING;
}

// DPC线程
VOID _CustomDpc(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2)
{
    PIRP pIrp;
    PDEV_EXT pDevExt = (PDEV_EXT)DeferredContext;
    PVOID pBuffer = NULL;
    ULONG uBufferLen = 0;
    PIO_STACK_LOCATION pIrpStack = NULL;

    do
    {
        if (!pDevExt)
        {
            break;
        }

        // 检查尾端IRP链表是否为空 为空则跳出
        if (IsListEmpty(&pDevExt->IrpList))
        {
            break;
        }

        // 从IRP链表中取出一个IRP并完成该IRP 取出的是ListEntry的地址
        PLIST_ENTRY pListEntry = (PLIST_ENTRY)RemoveHeadList(&pDevExt->IrpList);
        if (!pListEntry)
            break;

        pIrp = (PIRP)CONTAINING_RECORD(pListEntry, IRP, Tail.Overlay.ListEntry);
        pIrpStack = IoGetCurrentIrpStackLocation(pIrp);

        DbgPrint("当前DPC Irp: 0x%x\n", pIrp);

        // 驱动程序的读写方式位直接I/O
        pBuffer = MmGetSystemAddressForMdl(pIrp->MdlAddress);
        if (pBuffer == NULL)
        {
            pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
            pIrp->IoStatus.Information = 0;
            IoCompleteRequest(pIrp, IO_NO_INCREMENT);

            break;
        }
        uBufferLen = pIrpStack->Parameters.Read.Length;
        DbgPrint("读取DPC长度: %d\n", uBufferLen);

        // 支持5字节以下的读请求
        uBufferLen = uBufferLen > 13 ? 13 : uBufferLen;

        // 复制请求内容
        RtlCopyMemory(pBuffer, "hello lyshark", uBufferLen);

        pIrp->IoStatus.Status = STATUS_SUCCESS;
        pIrp->IoStatus.Information = uBufferLen;

        // 完成该IRP
        IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    } while (FALSE);

    // 重新设置定时器
    KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc);
}

// 卸载驱动
VOID _UnloadDispatch(PDRIVER_OBJECT _pDriverObject)
{
    UNICODE_STRING Win32DeviceName;
    PDEV_EXT pDevExt = (PDEV_EXT)_pDriverObject->DeviceObject->DeviceExtension;

    RtlInitUnicodeString(&Win32DeviceName, L"\\DosDevices\\LySharkAsync");

    // 删除定时器
    // LyShark
    KeCancelTimer(&pDevExt->timer);
    // 删除创建的设备
    IoDeleteDevice(_pDriverObject->DeviceObject);
}

// 驱动入口
NTSTATUS DriverEntry(PDRIVER_OBJECT _pDriverObject, PUNICODE_STRING _pRegistryPath)
{
    UNICODE_STRING DeviceName, Win32DeivceName;
    PDEVICE_OBJECT pDeviceObject = NULL;
    NTSTATUS status;
    PDEV_EXT pDevExt = NULL;
    HANDLE hThread;
    OBJECT_ATTRIBUTES ObjectAttributes;
    CLIENT_ID CID;

    RtlInitUnicodeString(&DeviceName, L"\\Device\\LySharkAsync");
    RtlInitUnicodeString(&Win32DeivceName, L"\\DosDevices\\LySharkAsync");

    for (ULONG i = 0; i MajorFunction[i] = _DefaultDispatch;
    }

    _pDriverObject->MajorFunction[IRP_MJ_CREATE] = _AsyncCreateCloseDispatch;
    _pDriverObject->MajorFunction[IRP_MJ_CLOSE] = _AsyncCreateCloseDispatch;
    _pDriverObject->MajorFunction[IRP_MJ_READ] = _AsyncReadDispatch;
    _pDriverObject->DriverUnload = _UnloadDispatch;

    // 分配自定义扩展
    status = IoCreateDevice(_pDriverObject, sizeof(DEV_EXT), &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject);
    if (!NT_SUCCESS(status))
        return status;
    if (!pDeviceObject)
        return STATUS_UNEXPECTED_IO_ERROR;

    pDeviceObject->Flags |= DO_DIRECT_IO;
    pDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT;
    status = IoCreateSymbolicLink(&Win32DeivceName, &DeviceName);

    pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
    pDevExt = (PDEV_EXT)pDeviceObject->DeviceExtension;

    // 初始化IRP链表
    InitializeListHead(&pDevExt->IrpList);
    // 初始化定时器
    KeInitializeTimer(&(pDevExt->timer));
    // 初始化DPC pDevExt是传给_CustomDpc函数的参数
    KeInitializeDpc(&pDevExt->dpc, (PKDEFERRED_ROUTINE)_CustomDpc, pDevExt);

    // 设置定时时间位1s
    pDevExt->liDueTime = RtlConvertLongToLargeInteger(-10000000);
    // 启动定时器
    KeSetTimer(&pDevExt->timer, pDevExt->liDueTime, &pDevExt->dpc);

    return STATUS_SUCCESS;
}

驱动层说完了,接下来是应用层,对于应用层来说,需要使用 CreateEvent打开通知事件,或者叫做事件对象,然后当有通知时,则直接使用 ReadFile读取对应的缓冲区,当所有读取全部结束 WaitForMultipleObjects等待结束即输出结果。

#include
#include

int main(int argc, char *argv[])
{
    HANDLE hFile;
    char Buffer[3][32] = { 0 };
    DWORD dwRet[3] = { 0 };
    OVERLAPPED ol[3] = { 0 };
    HANDLE hEvent[3] = { 0 };

    // By:LyShark
    hFile = CreateFileA("\\\\.\\LySharkAsync", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
        NULL);
    if (INVALID_HANDLE_VALUE == hFile)
        return  0;

    // event用来通知请求完成
    hEvent[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
    ol[0].hEvent = hEvent[0];

    hEvent[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
    ol[1].hEvent = hEvent[1];

    hEvent[2] = CreateEvent(NULL, TRUE, FALSE, NULL);
    ol[2].hEvent = hEvent[2];

    // 读取事件内容到缓存
    ReadFile(hFile, Buffer[0], 13, &dwRet[0], &ol[0]);
    ReadFile(hFile, Buffer[1], 13, &dwRet[1], &ol[1]);
    ReadFile(hFile, Buffer[2], 13, &dwRet[2], &ol[2]);

    // 等待三个事件执行完毕
    WaitForMultipleObjects(3, hEvent, TRUE, INFINITE);

    // 输出结果
    printf("缓存LyShark A: %s \n", Buffer[0]);
    printf("缓存LyShark B: %s \n", Buffer[1]);
    printf("缓存LyShark C: %s \n", Buffer[2]);

    CloseHandle(hFile);
    return  0;
}

这段代码最终运行效果如下:

驱动开发:通过Async反向与内核通信

Original: https://www.cnblogs.com/LyShark/p/16750551.html
Author: lyshark
Title: 驱动开发:通过Async反向与内核通信

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/801522/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • python能处理csv文件吗_python处理csv文件非常慢

    因此,我尝试打开一个csv文件,读取它的字段,并基于此修复其他一些字段,然后将数据保存回csv。我的问题是csv文件有200万行。最好的方法是什么来加快速度。 csv文件包括 ID…

    Python 2023年8月22日
    052
  • Unity打包WebGL平台如何区别移动端和电脑端

    Unity打包WebGL平台如何区别移动端和电脑端完整解决方案 前情提要 最近有一个项目,其中有一个功能来控制角色移动,电脑端是使用WASD(键盘)控制,手机移动端呢 使用虚拟摇杆…

    Python 2023年9月29日
    039
  • 蒙特卡洛学习笔记

    一、三门问题 三门问题(Monty Hall problem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目Let’s Make a Deal…

    Python 2023年8月26日
    044
  • python数据处理—-数据对象常用的属性、方法

    读取CSV文件为DataFrame对象 使用read_csv()方法读取csv数据成为DataFrame对象: import pandas as pd data = pd.read…

    Python 2023年8月29日
    043
  • python安装包方式汇总

    目录 方法一: 方式二: 方法三: 方法一: 最简单也是最常用的就是: pip install &#x5305;&#x540D; 一般都可以直接安装上。卸载方法类似…

    Python 2023年8月3日
    060
  • Flask + Docker 容器化部署Python应用

    文章目录 * – + 1. 基础介绍 + * 1.1 Flask * 1.2 Gunicorn * 1.3 Docker + 2. 基于 Flask 创建 Python…

    Python 2023年8月12日
    033
  • ThreadLocal源码学习笔记

    系列文章目录和关于我 一丶ThreadLocal结构 每一个Thread对象都有一个名为 threadLocals类型为 ThreadLocal.ThreadLocalMap的属性…

    Python 2023年10月22日
    026
  • 一、python中Numpy_Pandas数据处理基础

    Numpy(Numerical Python) 是 Python语言的一个第三方库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库。Numpy是一个运行速度非…

    Python 2023年8月25日
    061
  • Python小游戏——小鸟管道游戏【附原码】

    前言 又是一篇摸鱼小文章~相信这个游戏应该大多数人都玩过吧 话不多说,现在就来开整,赶紧做完,赶紧摸鱼 不得不说这个小游戏挑战性还蛮大的 开发工具 Python版本:3.6.4 相…

    Python 2023年9月24日
    022
  • 这道Python列表基础题目你确定会

    Original: https://www.cnblogs.com/123456feng/p/16106174.htmlAuthor: 蚂蚁ailingTitle: 这道Pytho…

    Python 2023年5月24日
    055
  • 基于Python的统计建模

    目录 数据集 基于实例的线性回归的简要介绍 代码块 (1)数据的读入 (2)数据的理解 以”数据框形状”的角度去理解数据 以”简要信息&#822…

    Python 2023年9月3日
    065
  • 爬虫系列:Scrapy的三种解析方式你都清楚吗?

    🌵爬虫之Scrapy系列文章🌴欢迎点赞评论学习交流~🌱各位看官多多关注哦😘~ 目录 🍉Scrapy的三种数据解析方式 🌵xpath选择器 🌵CSS选择器 🌵正则匹配 🍉dome 🌴…

    Python 2023年10月3日
    046
  • Numpy基础(1.0)

    在机器学习和深度学习中,图像、声音、文本等输入数据最终都要转换为数组或矩阵。如何有效地进行数组合矩阵的运算?这就需要充分利用Numpy。Numpy是数据科学的通用语言,而且与pyt…

    Python 2023年8月29日
    050
  • 动态规划之0-1背包问题(详解+分析+原码)

    ⭐️ 前面的话⭐️ 本篇文章将介绍算法专题之动态规划中的背包问题,更准确的说是背包问题中最简单的一种类型,即0-1背包问题,就是给你一定容量的背包和若干物品,每种物品只能选一次,告…

    Python 2023年10月27日
    043
  • 配置个人用户信息

    1  $ git config –global user.name "runoob"   $ git config –global user.email t…

    Python 2023年6月12日
    077
  • [深入浅出pandas]ch5高级操作

    对DataFrame其中一列进行逻辑计算,会产生一个对应的由布尔值组成的Series in: df.Q1>36 out: 0 True 1 False 2 True 3 Tr…

    Python 2023年8月17日
    031
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球