驱动开发:通过SystemBuf与内核层通信

简介: 内核层与应用层之间的数据交互是必不可少的部分,只有内核中的参数可以传递给用户数据才有意义,一般驱动多数情况下会使用`SystemBuf`缓冲区进行通信,也可以直接使用网络套接字实现通信,如下将简单介绍通过SystemBuf实现的内核层与应用层通信机制。

内核层与应用层之间的数据交互是必不可少的部分,只有内核中的参数可以传递给用户数据才有意义,一般驱动多数情况下会使用SystemBuf缓冲区进行通信,也可以直接使用网络套接字实现通信,如下将简单介绍通过SystemBuf实现的内核层与应用层通信机制。

内核与应用层传递结构体,实现应用层用户传入一个结构体到内核,内核处理后返回一段字符串。

内核代码如下,代码已经备注。

#include <ntifs.h>
#include <windef.h>

#define My_Code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)

// 通信结构体
typedef struct Hread
{
    ULONG Flage;
    ULONG Addr;
    ULONG WriteBufferAddr;
    ULONG Size;
    ULONG Pid;
}_Hread, *PtrHread;

typedef struct _DEVICE_EXTENSION
{
    UNICODE_STRING SymLinkName;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

// 驱动关闭提示
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
    PDEVICE_OBJECT pDevObj;
    pDevObj = pDriverObject->DeviceObject;
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    UNICODE_STRING pLinkName = pDevExt->SymLinkName;

    IoDeleteSymbolicLink(&pLinkName);
    IoDeleteDevice(pDevObj);
}

// 默认派遣
NTSTATUS DefDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
    NTSTATUS status = STATUS_SUCCESS;
    pIrp->IoStatus.Status = status;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return status;
}

// 主派遣函数
NTSTATUS IoctlDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    ULONG_PTR Informaiton = 0;
    PVOID InputData = NULL;
    ULONG InputDataLength = 0;
    PVOID OutputData = NULL;
    ULONG OutputDataLength = 0;
    PIO_STACK_LOCATION  IoStackLocation = IoGetCurrentIrpStackLocation(pIrp);             // Irp堆栈  
    InputData = pIrp->AssociatedIrp.SystemBuffer;                                         // 输入堆栈
    OutputData = pIrp->AssociatedIrp.SystemBuffer;                                        // 输出堆栈
    InputDataLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;      // 输入数据大小
    OutputDataLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;    // 输出数据大小
    ULONG Code = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;               // 控制码

    switch (Code)
    {
    case My_Code:
        {
            PtrHread PtrBuff = (PtrHread)InputData;
            ULONG RetFlage = PtrBuff->Flage;
            ULONG RetAddr = PtrBuff->Addr;
            ULONG RetBufferAddr = PtrBuff->WriteBufferAddr;
            ULONG Size = PtrBuff->Size;
            ULONG Pid = PtrBuff->Pid;

            DbgPrint("读取文件标志:%d", RetFlage);
            DbgPrint("读取写入地址:%x", RetAddr);
            DbgPrint("读取缓冲区大小:%d", RetBufferAddr);
            DbgPrint("读取当前大小:%d", Size);
            DbgPrint("要操作进程PID: %d", Pid);

            // 通过内存返回数据.
            char *retBuffer = "hello lyshark";
            memcpy(OutputData, retBuffer, strlen(retBuffer));
            Informaiton = strlen(retBuffer) + 1;
            Status = STATUS_SUCCESS;

            // 通过内存返回数据,另一种通信方式.
            /*
            PVOID addr = (PVOID)"ok";
            RtlCopyMemory(OutputData, addr, 4);
            Informaiton = 4;
            Status = STATUS_SUCCESS;
            */
            break;
        }
    }

    pIrp->IoStatus.Status = Status;                   // 设置IRP完成状态,会设置用户模式下的GetLastError
    pIrp->IoStatus.Information = Informaiton;         // 设置操作的字节
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);         // 完成IRP,不增加优先级
    return Status;
}

// 驱动入口
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
    pDriverObject->DriverUnload = DriverUnload;                         // 注册驱动卸载函数
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = DefDispatchRoutine;   // 注册派遣函数
    pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DefDispatchRoutine;
    pDriverObject->MajorFunction[IRP_MJ_WRITE] = DefDispatchRoutine;
    pDriverObject->MajorFunction[IRP_MJ_READ] = DefDispatchRoutine;
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoctlDispatchRoutine;

    NTSTATUS status;
    PDEVICE_OBJECT pDevObj;
    PDEVICE_EXTENSION pDevExt;

    // 创建设备名称的字符串
    UNICODE_STRING devName;
    RtlInitUnicodeString(&devName, L"\\Device\\MyDevice");
    
    // 创建设备
    status = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENSION), &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj);
    pDevObj->Flags |= DO_BUFFERED_IO;                      // 将设备设置为缓冲I/O设备
    pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension; // 得到设备扩展

    // 创建符号链接
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&symLinkName, L"\\??\\MyDevice");
    pDevExt->SymLinkName = symLinkName;
    status = IoCreateSymbolicLink(&symLinkName, &devName);
    return STATUS_SUCCESS;
}

客户端代码中只需要通过DeviceIoControl()发送控制信号即可,需要注意驱动需要安装并运行起来,否则无法获取到数据。

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

// 自定义的控制信号
#define My_Code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)

// 通信结构体
typedef struct Hread
{
    ULONG Flage;
    ULONG Addr;
    ULONG WriteBufferAddr;
    ULONG Size;
    ULONG Pid;
}_Hread, *PtrHread;

int main(int argc, char* argv[])
{
    // 创建
    HANDLE handle = CreateFileA("\\\\.\\MyDevice", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    unsigned char RetBufferData[20] = { 0 };
    DWORD ReturnLength = 4;
    _Hread buf;

    buf.Flage = 2;
    buf.Addr = 0x401234;
    buf.WriteBufferAddr = 1024;
    buf.Size = 100;
    buf.Pid = 2566;

    DeviceIoControl(handle, My_Code, &buf, 20, (LPVOID)RetBufferData, 4, &ReturnLength, 0);

    for (size_t i = 0; i < 20; i++)
    {
        printf("返回数据: %d \n", RetBufferData[i]);
    }

    CloseHandle(handle);

    getchar();
    return 0;
}

运行这段代码我们看下返回效果:

image.png

相关文章
|
5月前
2023驱动保护学习 -- 应用层与驱动层交互操作
2023驱动保护学习 -- 应用层与驱动层交互操作
27 0
|
5月前
|
存储 测试技术 调度
LabVIEW使用硬件抽象层适应不同的接口
LabVIEW使用硬件抽象层适应不同的接口
37 0
|
5月前
|
Linux API
Linux驱动的软件架构(三):主机驱动与外设驱动分离的设计思想
Linux驱动的软件架构(三):主机驱动与外设驱动分离的设计思想
91 0
|
11月前
|
存储 安全 API
3.5 Windows驱动开发:应用层与内核层内存映射
在上一篇博文`《内核通过PEB得到进程参数》`中我们通过使用`KeStackAttachProcess`附加进程的方式得到了该进程的PEB结构信息,本篇文章同样需要使用进程附加功能,但这次我们将实现一个更加有趣的功能,在某些情况下应用层与内核层需要共享一片内存区域通过这片区域可打通内核与应用层的隔离,此类功能的实现依附于MDL内存映射机制实现。
3.5 Windows驱动开发:应用层与内核层内存映射
驱动开发:通过ReadFile与内核层通信
驱动与应用程序的通信是非常有必要的,内核中执行代码后需要将其动态显示给应用层,但驱动程序与应用层毕竟不在一个地址空间内,为了实现内核与应用层数据交互则必须有通信的方法,微软为我们提供了三种通信方式,如下先来介绍通过`ReadFile`系列函数实现的通信模式。
驱动开发:通过ReadFile与内核层通信
|
Linux
Linux驱动入门(5)LED驱动---驱动分层和分离,平台总线模型
Linux驱动入门(5)LED驱动---驱动分层和分离,平台总线模型
116 0
|
Ubuntu Linux 开发者
韦东山Linux驱动入门实验班(2)hello驱动---驱动层与应用层通讯,以及自动产生设备节点
韦东山Linux驱动入门实验班(2)hello驱动---驱动层与应用层通讯,以及自动产生设备节点
167 0
|
网络安全
驱动开发:内核封装TDI网络通信接口
在上一篇文章`《驱动开发:内核封装WSK网络通信接口》`中,`LyShark`已经带大家看过了如何通过WSK接口实现套接字通信,但WSK实现的通信是内核与内核模块之间的,而如果需要内核与应用层之间通信则使用TDK会更好一些因为它更接近应用层,本章将使用TDK实现,TDI全称传输驱动接口,其主要负责连接`Socket`和协议驱动,用于实现访问传输层的功能,该接口比`NDIS`更接近于应用层,在早期Win系统中常用于实现过滤防火墙,同样经过封装后也可实现通信功能,本章将运用TDI接口实现驱动与应用层之间传输字符串,结构体,多线程收发等技术。
驱动开发:内核封装TDI网络通信接口
|
API C++ Windows
驱动开发:内核封装WSK网络通信接口
本章`LyShark`将带大家学习如何在内核中使用标准的`Socket`套接字通信接口,我们都知道`Windows`应用层下可直接调用`WinSocket`来实现网络通信,但在内核模式下应用层API接口无法使用,内核模式下有一套专有的`WSK`通信接口,我们对WSK进行封装,让其与应用层调用规范保持一致,并实现内核与内核直接通过`Socket`通信的案例。
驱动开发:内核封装WSK网络通信接口
|
安全 API
驱动开发:内核层InlineHook挂钩函数
在上一章`《驱动开发:内核LDE64引擎计算汇编长度》`中,`LyShark`教大家如何通过`LDE64`引擎实现计算反汇编指令长度,本章将在此基础之上实现内联函数挂钩,内核中的`InlineHook`函数挂钩其实与应用层一致,都是使用`劫持执行流`并跳转到我们自己的函数上来做处理,唯一的不同的是内核`Hook`只针对`内核API`函数,但由于其身处在`最底层`所以一旦被挂钩其整个应用层都将会受到影响,这就直接决定了在内核层挂钩的效果是应用层无法比拟的,对于安全从业者来说学会使用内核挂钩也是很重要。
驱动开发:内核层InlineHook挂钩函数