驱动开发:基于事件同步的反向通信

简介: 在之前的文章中`LyShark`一直都在教大家如何让驱动程序与应用层进行`正向通信`,而在某些时候我们不仅仅只需要正向通信,也需要反向通信,例如杀毒软件如果驱动程序拦截到恶意操作则必须将这个请求动态的转发到应用层以此来通知用户,而这种通信方式的实现有多种,通常可以使用创建Socket套接字的方式实现,亦或者使用本章所介绍的通过`事件同步`的方法实现反向通信。

在之前的文章中LyShark一直都在教大家如何让驱动程序与应用层进行正向通信,而在某些时候我们不仅仅只需要正向通信,也需要反向通信,例如杀毒软件如果驱动程序拦截到恶意操作则必须将这个请求动态的转发到应用层以此来通知用户,而这种通信方式的实现有多种,通常可以使用创建Socket套接字的方式实现,亦或者使用本章所介绍的通过事件同步的方法实现反向通信。

基于事件同步方式实现的通信需要用的最重要函数IoCreateNotificationEvent()这是微软定为开发者提供的。

IoCreateNotificationEvent 例程创建或打开一个命名通知事件,用于通知一个或多个执行线程已发生事件。

PKEVENT IoCreateNotificationEvent(
  [in]  PUNICODE_STRING EventName,
  [out] PHANDLE         EventHandle
);

其中的第二个参数EventHandle指向返回事件对象的内核句柄的位置的指针。此处为了能更好的接收和管理指针与进程之间的关系我们最好定义一个DEVICE_EXTEN结构体。

// 自定义设备扩展
typedef struct
{
   
   
    HANDLE  hProcess;         // HANDLE
    PKEVENT pkProcessEvent;   // 事件对象
    HANDLE  hProcessId;       // PID
    HANDLE  hpParProcessId;   // PPID
    BOOLEAN bIsCreateMark;    // Flag
}DEVICE_EXTEN, *PDEVICE_EXTEN;

驱动入口处PsSetCreateProcessNotifyRoutine则用于创建一个进程回调,该回调函数被指定为pMyCreateProcessThreadRoutine当回调函数被创建后,一旦有新进程创建则会执行函数内部的具体流程。

如代码所示,在pMyCreateProcessThreadRoutine函数内首先通过(PDEVICE_EXTEN)GlobalDevObj->DeviceExtension得到了自定义设备扩展指针,接着将当前进程的PID,PPID,isCreate等属性赋予到扩展指针中,当一切准备就绪后,通过调用KeSetEvent将当前进程事件设置为有信号状态,设置后重置为默认值。

// 自定义回调函数
VOID pMyCreateProcessThreadRoutine(IN HANDLE pParentId, HANDLE hProcessId, BOOLEAN bisCreate)
{
   
   
    PDEVICE_EXTEN pDeviceExten = (PDEVICE_EXTEN)GlobalDevObj->DeviceExtension;

    // 将进行信息依次复制到结构体中
    pDeviceExten->hProcessId = hProcessId;
    pDeviceExten->hpParProcessId = pParentId;
    pDeviceExten->bIsCreateMark = bisCreate;

    // 设置为有信号
    KeSetEvent(pDeviceExten->pkProcessEvent, 0, FALSE);

    // 重置状态信号
    KeResetEvent(pDeviceExten->pkProcessEvent);
}

此时由于客户端中通过OpenEventW(SYNCHRONIZE, FALSE, EVENT_NAME)打开了内核对象,并通过WaitForSingleObject(hProcessEvent, INFINITE)一直在等待事件,一旦内核驱动KeSetEvent(pDeviceExten->pkProcessEvent, 0, FALSE)设置为有信号状态,则应用层会通过DeviceIoControl向内核层发送IOCTL控制信号并等待接收数据。

此时主派遣函数DisPatchIoControl被触发执行,通过(PPROCESS_PTR)pUserOutPutBuffer获取到应用层缓冲区设备指针,并依次通过pBuffer->的方式设置参数,最后返回状态码,此时应用层PROCESS_PTR中也就得到了进程创建的相关信息。

pBuffer = (PPROCESS_PTR)pUserOutPutBuffer;
uIoControl = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
uReadLen = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
uWriteLen = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;

switch (uIoControl)
{
   
   
case IOCTL:
    pDeviceExten = (PDEVICE_EXTEN)GlobalDevObj->DeviceExtension;
    pBuffer->hProcessId = pDeviceExten->hProcessId;
    pBuffer->hpParProcessId = pDeviceExten->hpParProcessId;
    pBuffer->bIsCreateMark = pDeviceExten->bIsCreateMark;
    break;
default:
    ntStatus = STATUS_INVALID_PARAMETER;
    uWriteLen = 0;
    break;
}

如上就是内核层与应用层的部分代码功能分析,接下来我将完整代码分享出来,大家可以自行测试效果。

驱动程序WinDDK.sys完整代码;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include <ntifs.h>
#include <ntstrsafe.h>

#define IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS)

UNICODE_STRING GlobalSym = {
   
    0 };
PDEVICE_OBJECT GlobalDevObj;

// 自定义设备扩展
typedef struct
{
   
   
    HANDLE  hProcess;         // HANDLE
    PKEVENT pkProcessEvent;   // 事件对象
    HANDLE  hProcessId;       // PID
    HANDLE  hpParProcessId;   // PPID
    BOOLEAN bIsCreateMark;    // Flag
}DEVICE_EXTEN, *PDEVICE_EXTEN;

// 自定义结构体(临时)
typedef struct
{
   
   
    HANDLE hProcessId;
    HANDLE hpParProcessId;
    BOOLEAN bIsCreateMark;
}PROCESS_PTR, *PPROCESS_PTR;

// 自定义回调函数
VOID pMyCreateProcessThreadRoutine(IN HANDLE pParentId, HANDLE hProcessId, BOOLEAN bisCreate)
{
   
   
    PDEVICE_EXTEN pDeviceExten = (PDEVICE_EXTEN)GlobalDevObj->DeviceExtension;

    // 将进行信息依次复制到结构体中
    pDeviceExten->hProcessId = hProcessId;
    pDeviceExten->hpParProcessId = pParentId;
    pDeviceExten->bIsCreateMark = bisCreate;

    // 设置为有信号
    KeSetEvent(pDeviceExten->pkProcessEvent, 0, FALSE);

    // 重置状态信号
    KeResetEvent(pDeviceExten->pkProcessEvent);
}

// 默认派遣函数
NTSTATUS DisPatchComd(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
   
   
    pIrp->IoStatus.Information = 0;
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);

    return pIrp->IoStatus.Status;
}

// 主派遣函数
NTSTATUS DisPatchIoControl(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
   
   
    NTSTATUS ntStatus;
    PIO_STACK_LOCATION pIrpStack;
    PVOID pUserOutPutBuffer;
    PPROCESS_PTR pBuffer;
    ULONG uIoControl = 0;
    ULONG uReadLen;
    ULONG uWriteLen;
    PDEVICE_EXTEN pDeviceExten;

    pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
    pUserOutPutBuffer = pIrp->AssociatedIrp.SystemBuffer;

    pBuffer = (PPROCESS_PTR)pUserOutPutBuffer;
    uIoControl = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
    uReadLen = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
    uWriteLen = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;

    switch (uIoControl)
    {
   
   
    case IOCTL:
        pDeviceExten = (PDEVICE_EXTEN)GlobalDevObj->DeviceExtension;
        pBuffer->hProcessId = pDeviceExten->hProcessId;
        pBuffer->hpParProcessId = pDeviceExten->hpParProcessId;
        pBuffer->bIsCreateMark = pDeviceExten->bIsCreateMark;
        break;
    default:
        ntStatus = STATUS_INVALID_PARAMETER;
        uWriteLen = 0;
        break;
    }

    pIrp->IoStatus.Information = uWriteLen;
    pIrp->IoStatus.Status = ntStatus;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return ntStatus;
}

// 卸载驱动
VOID DriverUnLoad(PDRIVER_OBJECT pDriverObject)
{
   
   
    NTSTATUS ntStatus;
    UNICODE_STRING SymboLicLinkStr = {
   
    0 };

    // 删除符号链接
    ntStatus = RtlUnicodeStringInit(&SymboLicLinkStr, L"\\DosDevices\\Symboliclnk");
    ntStatus = IoDeleteSymbolicLink(&SymboLicLinkStr);

    // 注销进程消息回调
    PsSetCreateProcessNotifyRoutine(pMyCreateProcessThreadRoutine, TRUE);
    IoDeleteDevice(pDriverObject->DeviceObject);
}

// 入口函数
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegistryPath)
{
   
   
    NTSTATUS ntStatus = NULL;
    PDEVICE_OBJECT pDeviceObject = NULL;
    UNICODE_STRING uDeviceName = {
   
    0 };
    UNICODE_STRING uEventName = {
   
    0 };

    // 初始化字符串
    ntStatus = RtlUnicodeStringInit(&uDeviceName, L"\\Device\\ProcLook");
    ntStatus = RtlUnicodeStringInit(&GlobalSym, L"\\DosDevices\\Symboliclnk");
    ntStatus = RtlUnicodeStringInit(&uEventName, L"\\BaseNamedObjects\\ProcLook");
    DbgPrint("hello lyshark.com");
    if (!NT_SUCCESS(ntStatus))
    {
   
   
        return ntStatus;
    }

    // 创建设备
    ntStatus = IoCreateDevice(pDriver,sizeof(DEVICE_EXTEN),&uDeviceName,FILE_DEVICE_UNKNOWN,FILE_DEVICE_SECURE_OPEN,FALSE,&pDeviceObject);
    DbgPrint("[LyShark] 创建设备对象");

    if (!NT_SUCCESS(ntStatus))
    {
   
   
        IoDeleteDevice(pDeviceObject);
        return ntStatus;
    }
    pDriver->Flags |= DO_BUFFERED_IO;
    GlobalDevObj = pDeviceObject;

    // 获取设备扩展指针,并IoCreateNotificationEvent创建一个R3到R0的事件
    PDEVICE_EXTEN pDeviceExten = (PDEVICE_EXTEN)pDeviceObject->DeviceExtension;
    pDeviceExten->pkProcessEvent = IoCreateNotificationEvent(&uEventName, &pDeviceExten->hProcess);
    KeClearEvent(pDeviceExten->pkProcessEvent);
    DbgPrint("[LyShark] 创建事件回调");

    // 创建符号链接
    ntStatus = IoCreateSymbolicLink(&GlobalSym, &uDeviceName);
    if (!NT_SUCCESS(ntStatus))
    {
   
   
        IoDeleteDevice(pDeviceObject);
        return ntStatus;
    }

    // 注册进程创建回调
    ntStatus = PsSetCreateProcessNotifyRoutine(pMyCreateProcessThreadRoutine, FALSE);
    DbgPrint("[LyShark] 创建进程回调");
    if (!NT_SUCCESS(ntStatus))
    {
   
   
        IoDeleteDevice(pDeviceObject);
        return ntStatus;
    }

    // 初始化派遣函数
    for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
    {
   
   
        pDriver->MajorFunction[i] = DisPatchComd;
    }
    pDriver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DisPatchIoControl;

    // 关闭驱动
    pDriver->DriverUnload = DriverUnLoad;
    return ntStatus;
}

应用层客户端程序lyshark.exe完整代码;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include <iostream>
#include <Windows.h>

#define EVENT_NAME L"Global\\ProcLook"
#define IOCTL CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS)

typedef struct
{
   
   
    HANDLE hProcessId;
    HANDLE hpParProcessId;
    BOOLEAN bIsCreateMark;
}PROCESS_PTR, *PPROCESS_PTR;

int main(int argc, char* argv[])
{
   
   
    PROCESS_PTR Master = {
   
    0 };
    PROCESS_PTR Slave = {
   
    0 };
    DWORD dwRet = 0;
    BOOL bRet = FALSE;

    // 打开驱动设备对象
    HANDLE hDriver = CreateFile(L"\\\\.\\Symboliclnk",GENERIC_READ | GENERIC_WRITE,NULL,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if (hDriver == INVALID_HANDLE_VALUE)
    {
   
   
        return 0;
    }

    // 打开内核事件对象
    HANDLE hProcessEvent = OpenEventW(SYNCHRONIZE, FALSE, EVENT_NAME);

    if (NULL == hProcessEvent)
    {
   
   
        return 0;
    }

    while (TRUE)
    {
   
   
        // 等待事件
        WaitForSingleObject(hProcessEvent, INFINITE);

        // 发送控制信号
        bRet = DeviceIoControl(hDriver,IOCTL,NULL,0,&Master,sizeof(Master),&dwRet,NULL);
        if (!bRet)
        {
   
   
            return 0;
        }

        if (bRet)
        {
   
   
            if (Master.hpParProcessId != Slave.hpParProcessId || Master.hProcessId != Slave.hProcessId || Master.bIsCreateMark != Slave.bIsCreateMark)
            {
   
   
                if (Master.bIsCreateMark)
                {
   
   
                    printf("[LyShark] 父进程: %d | 进程PID: %d | 状态: 创建进程 \n", Master.hpParProcessId, Master.hProcessId);
                }
                else
                {
   
   
                    printf("[LyShark] 父进程: %d | 进程PID: %d | 状态: 退出进程 \n", Master.hpParProcessId, Master.hProcessId);
                }
                Slave = Master;
            }
        }
        else
        {
   
   
            break;
        }
    }

    CloseHandle(hDriver);
    return 0;
}

手动编译这两个程序,将驱动签名后以管理员身份运行lyshark.exe客户端,此时屏幕中即可看到滚动输出效果,如此一来就实现了循环传递参数的目的。

image.png

相关文章
|
6月前
基于若依的ruoyi-nbcio流程管理系统支持支持定时边界事件和定时捕获事件
基于若依的ruoyi-nbcio流程管理系统支持支持定时边界事件和定时捕获事件
85 2
|
6月前
|
Linux 调度 数据库
|
存储 监控 Cloud Native
剖析Linux网络包接收过程:掌握数据如何被捕获和分发的全过程(上)
剖析Linux网络包接收过程:掌握数据如何被捕获和分发的全过程
|
6月前
LabVIEW使用多个事件结构来处理同一个事件
LabVIEW使用多个事件结构来处理同一个事件
187 0
|
存储 网络协议 Linux
剖析Linux网络包接收过程:掌握数据如何被捕获和分发的全过程(下)
剖析Linux网络包接收过程:掌握数据如何被捕获和分发的全过程
|
消息中间件 定位技术 调度
《移动互联网技术》第八章 消息与服务:掌握不同类型广播监听方式,以及创建通知的方法
《移动互联网技术》第八章 消息与服务:掌握不同类型广播监听方式,以及创建通知的方法
52 0
|
5G 网络性能优化 文件存储
带你读《5G 系统技术原理与实现》——1.2.3 5G 系统接口功能与协议
带你读《5G 系统技术原理与实现》——1.2.3 5G 系统接口功能与协议
带你读《5G 系统技术原理与实现》——1.2.3 5G 系统接口功能与协议
|
设计模式 缓存
U3D客户端框架之商业项目中的 FSM 有限状态机 实现代码
FSM有限状态机在游戏中的作用主要是做场景的流程管理,进入场景状态后 加载资源初始化,更新状态时执行更新逻辑,离开场景状态时销毁场景资源,数据清理、角色动作状态切换,进入时播放动作,离开时播放下一个当作等。
|
数据安全/隐私保护
VOS怎样实现平台之间的动态注册和一些小问题?
VOS怎样实现平台之间的动态注册和一些小问题?
|
移动开发 小程序 iOS开发
小程序和h5之间的通讯及注意事项
起因:在小程序中嵌入已有的h5页面,用于快速开发。但是h5和小程序中的事件通讯是需要解决的问题。参照官网后实践之后得出结论。
1068 0