代码例子来源: Win驱动9]Windows分层驱动_windows分层驱动程序 数据-CSDN博客

过滤驱动是一种独立的驱动程序,直接插入内核驱动栈中,拦截并处理驱动程序之间的数据传输。它可以拦截底层的I/O请求,并在不改变原始请求的情况下,修改、延迟或直接处理这些请求。

过滤驱动:位于设备驱动程序堆栈的上层,负责拦截、修改和处理 I/O 请求或响应,并可能将其转发给目标驱动。

目标驱动:位于设备驱动程序堆栈的下层,直接与硬件交互,处理实际的 I/O 操作。

和注册回调的区别: 过滤驱动运行在内核模式下的驱动栈中,拦截并处理I/O请求;回调函数则在某些事件发生时被内核或应用调用,可能在用户模式或内核模式下运行。 过滤驱动可以拦截设备级的I/O请求(如文件读写、网络包),而回调函数通常处理系统事件(如进程创建、线程创建、加载模块等)。 过滤驱动适合用于文件系统、网络、设备的深度控制和拦截;回调函数则用于监控和响应特定的系统事件,例如进程管理、内存分配等。

最重要的是:这玩意贼稳定,受微软支持的,想附加就附加

当请求完成的时候,会调用完成例程函数,用以下函数可以添加,可以从里面获取到键盘扫描码....吗?

1725612145301

但是我们还是会使用原来的键盘回调,等于又把完成例程函数修改回去了,所以这个方法并不可行

完成例程函数

通过IoSetCompletionRoutine 设置的完成例程函数(Completion Routine)在 Windows 驱动开发中主要用于在 I/O 请求(IRP)完成后执行一些额外的操作。

NTSTATUS CompletionRoutine(
    PDEVICE_OBJECT DeviceObject, // 设备对象
    PIRP Irp,                    // 与该 I/O 请求相关联的 IRP
    PVOID Context                // 上下文信息,由 IoSetCompletionRoutine 传递
);

调用时机:

当一个IRP请求调用了IoCompleteRequest,促使古I/O管理器就会检查IRP是否已经设置好了完成例程函数,如果设置了执行IoSetCompletionRoutine 关联的完成例程。

DPC例程

DPC例程(Deferred Procedure Call,延迟过程调用) 是 Windows 内核中的一种机制,允许高优先级的任务将某些工作推迟到稍后执行,通常在一个相对较低的中断请求级别(IRQL)。这有助于优化系统性能,避免在高优先级的中断上下文中执行过多的工作。

在这里的应用场景是,当驱动程序在处理异步 I/O 请求时,会利用 DPC 来处理请求的完成例程。

例如 键盘驱动程序会在用户按下某个键时生成一个扫描码。此时,键盘驱动会将扫描码存入 IRP 的缓冲区,并调用 IoCompleteRequest。 所以我们就可以通过DPC例程,等待一段时间后,自己注册一个完成例程函数,然后从完成例程函数里面读出来扫描码

注意注意,调用CreateFile一定在驱动程序用注册Create的IRP例程!

实战目标驱动,过滤驱动

目标驱动代码:

#include "header.h"
typedef struct _DEVICE_EXTENSION {
    UNICODE_STRING ustrDevName;
    UNICODE_STRING ustrSymName;
    PDEVICE_OBJECT pDevice;
    PIRP pCurrentIrp;
    KDPC Dpc;
    KTIMER Timer;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
​
NTSTATUS DispatchRoutine(PDEVICE_OBJECT pDev, PIRP pIrp) {
    pDev;
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return(STATUS_SUCCESS);
}
​
NTSTATUS WriteRoutine(PDEVICE_OBJECT pDev, PIRP pIrp) {
    pDev;
    KdPrint(("进入WriteRoutine\n"));
    //IoSkipCurrentIrpStackLocation(pIrp);
    //IoCallDriver(pDev, pIrp);
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    KdPrint(("离开WriteRoutine\n"));
​
    return(STATUS_SUCCESS);
}
​
NTSTATUS ReadRoutine(PDEVICE_OBJECT pDev, PIRP pIrp) {
    KdPrint(("DriverA: 进入ReadRoutine\n"));
    PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDev->DeviceExtension;
​
    IoMarkIrpPending(pIrp); // 挂起该IRP
    pdx->pCurrentIrp = pIrp; // 保存当前IRP到设备拓展中
    LARGE_INTEGER timeout;
    timeout.QuadPart = -30 * 1000 * 1000; // 3秒倒计时,单位是100纳秒
    KeSetTimer(&pdx->Timer, timeout, &pdx->Dpc); // 开启定时器,3秒后执行DPC例程
    KdPrint(("DriverA: 离开ReadRoutine\n"));
​
    return(STATUS_PENDING);
}
​
// DPC例程主要是为了完成IRP请求
void DPCRoutine(PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) {
    Dpc;
    SystemArgument1;
    SystemArgument2;
    KdPrint(("DPC例程: 进入了DPC例程\n"));
    PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)DeferredContext;
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
​
    PIRP pIrp = pDevExt->pCurrentIrp;
    pIrp->IoStatus.Information = 0;
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    KdPrint(("DPC例程完成了挂起的IRP请求\n"));
    KdPrint(("DPC例程: 离开了DPC例程\n"));
}
​
​
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    UNREFERENCED_PARAMETER(RegistryPath);
    KdPrint(("Create!"));
    KdBreakPoint();
    // 驱动程序卸载例程&注册例程
    DriverObject->DriverUnload = DriverUnloadRoutine;
​
    for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i)
        DriverObject->MajorFunction[i] = DispatchRoutine;
    DriverObject->MajorFunction[IRP_MJ_READ] = ReadRoutine;
​
    //创建设备
    NTSTATUS status;
    PDEVICE_OBJECT DeviceObject = NULL;
    UNICODE_STRING DeviceName;
    RtlInitUnicodeString(&DeviceName, L"\\Device\\HelloDDKA"); 
    status = IoCreateDevice(
        DriverObject,                // 驱动程序对象
        sizeof(DEVICE_EXTENSION),                           // 设备扩展大小
        &DeviceName,                 // 设备名称
        FILE_DEVICE_UNKNOWN,         // 设备类型
        0,                           // 设备特征
        FALSE,                       // 非独占设备
        &DeviceObject                // 返回的设备对象指针
    );
    if (!NT_SUCCESS(status))
    {
        KdPrint(("Failed to create device: %X\n", status));
        return status;
    }
    KdPrint(("Device created successfully\n"));
​
    //创建符号链接
    UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(L"\\??\\HelloDDKA");
    status = IoCreateSymbolicLink(&symbolicLink, &DeviceName);
    if (!NT_SUCCESS(status))
    {
        KdPrint(("Failed to create device: %X\n", status));
        return status;
    }
    KdPrint(("Device created successfully\n"));
​
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
    pDevExt->pDevice = DeviceObject;
    pDevExt->ustrDevName = DeviceName;
​
    KeInitializeTimer(&pDevExt->Timer);
    KeInitializeDpc(&pDevExt->Dpc, DPCRoutine, DeviceObject);
    RtlInitUnicodeString(&pDevExt->ustrSymName, L"\\??\\HelloDDKA");
    return STATUS_SUCCESS;
}
​
​
​
VOID DriverUnloadRoutine(IN PDRIVER_OBJECT DriverObject)
{
    PDEVICE_OBJECT pNextDev = DriverObject->DeviceObject;
​
    while (pNextDev) {
        PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextDev->DeviceExtension;
        KdPrint(("符号链接名: %wZ\n", pDevExt->ustrSymName));
        IoDeleteSymbolicLink(&pDevExt->ustrSymName);
        IoDeleteDevice(pDevExt->pDevice);
        pNextDev = pNextDev->NextDevice;  //会在驱动程序调用 IoCreateDevice 时自动被设置
    }
​
    DbgPrint("Driver unloaded\n");
}

过滤驱动:

#include "header.h"
​
typedef struct _DEVICE_EXTENSION {
    UNICODE_STRING ustrDevName;
    PDEVICE_OBJECT pDevice;
    PDEVICE_OBJECT pTargetDev;
} DEVICE_EXTENSION, * PDEVICE_EXTENSION;
​
​
​
NTSTATUS ReadRoutine(PDEVICE_OBJECT pDev, PIRP pIrp) {  
    KdPrint(("DriverB: 进入ReadRoutine\n"));
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDev->DeviceExtension;
    IoSkipCurrentIrpStackLocation(pIrp); // 把IO堆栈往上挪动一格
    NTSTATUS status = IoCallDriver(pDevExt->pTargetDev, pIrp); // 把IRP传递给下层设备,交给目标驱动进行处理
    KdPrint(("DriverB: 离开ReadRoutine\n"));
    return(status);
}
​
​
NTSTATUS CloseRoutine(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) {  //交给目标驱动处理
    KdPrint(("DriverB:Enter B CloseRoutine\n"));
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    IoSkipCurrentIrpStackLocation(pIrp);
    ntStatus = IoCallDriver(pdx->pTargetDev, pIrp);
    KdPrint(("DriverB:Leave B CloseRoutine\n"));
    return ntStatus;
}
​
NTSTATUS CreateRoutine(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) {//交给目标驱动处理
    KdPrint(("DriverB:Enter B CreateRoutine\n"));
    NTSTATUS ntStatus = STATUS_SUCCESS;
​
    PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    IoSkipCurrentIrpStackLocation(pIrp);
    ntStatus = IoCallDriver(pdx->pTargetDev, pIrp);
    KdPrint(("DriverB:Leave B CreateRoutine\n"));
​
    return ntStatus;
}
​
​
NTSTATUS DispatchRoutine(PDEVICE_OBJECT pDev, PIRP pIrp) {   //DispatchRoutine 是不会将 I/O 请求传递给目标驱动的
    pDev;
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);  //等于啥也不做
    return(STATUS_SUCCESS);
}
​
​
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    UNREFERENCED_PARAMETER(RegistryPath);
    KdPrint(("Create!"));
    KdBreakPoint();
    // 驱动程序卸载例程&注册例程
    DriverObject->DriverUnload = DriverUnloadRoutine;
    for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i)
        DriverObject->MajorFunction[i] = DispatchRoutine; // 除了读IRP其他都在过滤驱动中被拦截完成
    DriverObject->MajorFunction[IRP_MJ_READ] = ReadRoutine;
    DriverObject->MajorFunction[IRP_MJ_CREATE] = CreateRoutine;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = CloseRoutine;
​
​
    //创建设备对象
    UNICODE_STRING DeviceName;
    NTSTATUS status = NULL;
    PDEVICE_OBJECT pDevObj;
    RtlInitUnicodeString(&DeviceName, L"\\Device\\HelloDDKB");
    status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &DeviceName,
        FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj);
    if (!NT_SUCCESS(status)) {
        KdPrint(("IoCreateDevice %08X\n", status));
        return(status);
    }
​
    pDevObj->Flags |= DO_BUFFERED_IO;
    PDEVICE_OBJECT pFilterDevObj = pDevObj;
​
​
    //寻找目标驱动设备对象
    UNICODE_STRING ustrTargetDevName;
    PFILE_OBJECT pFileObj;
    PDEVICE_OBJECT pTargetDevObj, pTarget;
    RtlInitUnicodeString(&ustrTargetDevName, L"\\Device\\HelloDDKA");
    status = IoGetDeviceObjectPointer(&ustrTargetDevName, FILE_ALL_ACCESS, &pFileObj, &pTargetDevObj);
    if (!NT_SUCCESS(status)) {
        KdPrint(("IoGetDeviceObjectPointer %08X\n", status));
        return(status);
    }
​
​
    // 获取成功后需要将过滤驱动附在在目标设备上面
    pTarget = IoAttachDeviceToDeviceStack(pFilterDevObj, pTargetDevObj);  //返回值是附着到的设备对象,有别于pTargetDevObj
    if (pTarget==NULL) { // 附着失败要进行清理工作
        ObDereferenceObject(pFileObj);  //减少内核对象的引用计数。当引用计数变为零时,内核对象会被释放。
        IoDeleteDevice(pFilterDevObj);  
        KdPrint(("附加失败!\n"));
        return(STATUS_UNSUCCESSFUL);
    }
​
​
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pFilterDevObj->DeviceExtension;
    pDevExt->pTargetDev = pTarget;
    pFilterDevObj->Flags |= (pTarget->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO));//这行代码是将目标设备对象的 I/O 模式(即是否使用直接 I/O 或缓冲 I/O)复制到过滤设备对象中。
    pFilterDevObj->Flags &= ~DO_DEVICE_INITIALIZING;  //这行代码是清除过滤设备对象的 DO_DEVICE_INITIALIZING 标志,表示设备对象的初始化已完成。
    pFilterDevObj->Characteristics = pTarget->Characteristics; //复制属性
    pFilterDevObj->DeviceType = pTarget->DeviceType;  //复制属性
​
    ObDereferenceObject(pFileObj); // 别忘记成功了也要对引用对象解引用
    return STATUS_SUCCESS;
}
​
​
VOID DriverUnloadRoutine(IN PDRIVER_OBJECT DriverObject)
{
    PDEVICE_OBJECT pDevObj = DriverObject->DeviceObject;
    while (pDevObj) {
        PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
        // 从目标设备上解除附着
        IoDetachDevice(pDevExt->pTargetDev);
        // 删除目标设备
        IoDeleteDevice(pDevExt->pDevice);
        pDevObj = pDevObj->NextDevice;
    }
    DbgPrint("Driver unloaded\n");
}

应用程序

// 应用层客户端
#include <windows.h>
#include <stdio.h>
int main() {
    const WCHAR* psz = L"\\\\.\\HelloDDKA";
    HANDLE hDev = CreateFile(psz, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, NULL);
    if (hDev==INVALID_HANDLE_VALUE) 
    {
        printf("无效句柄\n");
        system("pause");
        return(-1);
    }
    DWORD dwRead = 0;
    ReadFile(hDev, NULL, 0, &dwRead, NULL);
    CloseHandle(hDev);
    system("pause");
    return(0);
}

运行起来结果如下:

1725802168425

我们捋一下流程,选取Read_MJ_IRP来举例:

  1. 首先应用程序调用了ReadFile函数,ReadFile会调用NTDLL里的NtReadFile

  2. NTDLL中的NtReadFile会调用系统调用中的NtReadFile

  3. 系统调用的NtReadFile会发送IRP_MJ_READ类型的IRP请求给对应的驱动设备(这里是目标驱动设备,也就是HelloDDKA)

  4. 我们附加的是HelloDDKB,附加在HelloDDKA上面,因为IRP都是从设备栈的栈顶开始自顶而下传递的,所以HelloDDKB首先截获IRP

  5. HelloDDKB截获,所以先根据IRP类型进入了HelloDDKB的ReadRoutine里面

  6. 根据过滤驱动写的代码,ReadRoutine什么也不做,将其转发给了目标驱动HelloDDKA,并保留当前的IO环境,并阻塞在IoCallDriver上

  7. HelloDDKB就接收到了IRP请求,并根据类型派遣到HelloDDKB的处理函数ReadRoutine

  8. ReadRoutine将其挂起,并设置了DPC例程后直接返回挂起状态

  9. 但是此时因为HelloDDKB的IRP_MJ_READ还没有被结束, IRP_MJ_READ 请求的处理通常需要等待调用 IoCompleteRequest(pIrp, IO_NO_INCREMENT) 后才算正式结束,完成例程函数也是在 IoCompleteRequest 调用后被触发。

  10. 3秒后DPC例程被触发,被挂起的IRP请求完成

  11. 此时IRP会沿着设备栈向上返回,解除HelloDDKB中IoCallDriver的阻塞状态并继续执行

  12. 经过一步步网上返回后最终会将运行结果返回应用层的ReadFile函数

1725803506154

用过滤驱动拿到键盘扫描码

查阅资料可以知道这玩意是键盘的驱动,我们通过附加这个驱动,获取到键盘的扫描码

1725928840812

ObReferenceObjectByName :可以根据名字拿到驱动对象,是一个导出函数,需要声明一下才能用,注意要用C语言的符号导出,cpp会额外加一些奇奇怪怪的符号

extern "C"
{
    NTSTATUS ObReferenceObjectByName(
            __in PUNICODE_STRING ObjectName,// 驱动对象的名称
            __in ULONG Attributes,
            __in_opt PACCESS_STATE AccessState,
            __in_opt ACCESS_MASK DesiredAccess,
            __in POBJECT_TYPE ObjectType,
            __in KPROCESSOR_MODE AccessMode,
            __inout_opt PVOID ParseContext,
            __out PVOID* Object
        );
}
​
//举例
status = ObReferenceObjectByName(
        &driverName,                 // 驱动对象的名称
        OBJ_CASE_INSENSITIVE,        // 不区分大小写
        NULL,                        // 访问令牌:内核模式下可以为 NULL
        KernelMode,                  // 访问模式:内核模式
        *IoDriverObjectType,         // 对象类型:驱动对象
        KernelMode,                  // 处理器模式
        NULL,                        // 可选的句柄信息,通常为 NULL
        (PVOID*)&KbdDriver)       // 输出:驱动对象指针

键盘扫描码通常是在键盘类驱动(kbdclass.sys)的完成例程中被处理并返回的

因为中断处理要求的是快,我们不能在读IRP分发函数做一些耗时的工作(比如获取、替换键值等操作),那样的话会导致系统变卡,所以我们只在ReadDispatch中做一个关键操作,设置IRP完成回调函数,意思是指读IRP完成后,再由系统来调用操作键值的函数,那样就不会耽误IRP的传递了。

IRP请求只有一个,可以被传递

完成例程函数是在IoCompleteRequest 执行后立即调用的。完成例程是一个回调函数,它会在驱动程序调用 IoCompleteRequest 标记 IRP 为完成时触发执行。

IoCallDriver是否立即返回,得看目标驱动的例程函数是啥,如果是同步,则会阻塞,如果是异步,那么就会立即返回,过滤驱动继续完成例程函数代码

同步的情况:

NTSTATUS TargetDriverReadRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    // 模拟同步处理
    KdPrint(("Target driver processing IRP synchronously\n"));
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
​
    // 同步处理时立即调用 IoCompleteRequest 完成 IRP
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
​
    return STATUS_SUCCESS; // 同步返回最终的处理状态
}

异步的情况:

NTSTATUS TargetDriverReadRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
    KdPrint(("Target driver processing IRP asynchronously\n"));
​
    // 标记 IRP 挂起,表示异步处理
    IoMarkIrpPending(Irp);
​
    // 启动一个异步处理任务,稍后再完成 IRP
    // 例如,可以在其他线程或 DPC 中完成该 IRP
​
    return STATUS_PENDING; // 立即返回,不等待处理完成
}
​
// 异步处理完成后调用的函数
VOID CompleteIrpLater(PIRP Irp) {
    // 模拟异步完成 IRP
    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
}

为什么我们能拿到键盘扫描码呢?过滤驱动和目标驱动的IRP堆栈的内容不是不共享吗? 答:IRP堆栈不共享,但是SystemBuffer是共享的,我们从SystemBuffer里拿键盘扫描码

键盘驱动在注册的时候,已经提前发了一个IRP_MJ_READ请求,并挂起(于键盘驱动挂起,等待用户按下)

在执行完例程函数后,IRP(I/O请求数据包)请求并不一定就结束了,有些操作是异步的,执行完例程函数后,可能还需要等待其他硬件或驱动程序的响应。挂起可以确保在收到响应之前不释放IRP。

所以在过滤驱动中,我们需要加这一句,确保不出错

if (Irp->PendingReturned)
{
    IoMarkIrpPending(Irp);
}
return Irp->IoStatus.Status;

过滤驱动完整代码

#pragma once
extern "C"
{
#include <ntifs.h>
#include <ntddk.h>
#include <ntddkbd.h>  // 包含键盘输入结构体定义
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);
    VOID DriverUnloadRoutine(IN PDRIVER_OBJECT DriverObject);
    NTSTATUS ObReferenceObjectByName(
            __in PUNICODE_STRING ObjectName,
            __in ULONG Attributes,
            __in_opt PACCESS_STATE AccessState,
            __in_opt ACCESS_MASK DesiredAccess,
            __in POBJECT_TYPE ObjectType,
            __in KPROCESSOR_MODE AccessMode,
            __inout_opt PVOID ParseContext,
            __out PVOID* Object
        );
    extern POBJECT_TYPE* IoDriverObjectType;
}
​
​
#include "header.h"
​
typedef struct _DEVICE_EXTENSION {
    UNICODE_STRING ustrDevName;
    PDEVICE_OBJECT pDevice;
    PDEVICE_OBJECT pTargetDev;
} DEVICE_EXTENSION, * PDEVICE_EXTENSION;
​
​
NTSTATUS DispatchRoutine(PDEVICE_OBJECT pDev, PIRP pIrp) {   //DispatchRoutine 是不会将 I/O 请求传递给目标驱动的
    pDev;
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);  //等于啥也不做
    return(STATUS_SUCCESS);
}
​
​
NTSTATUS FilterReadCompletionRoutine(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
)
{
    
    Context;
    DeviceObject;
    if (NT_SUCCESS(Irp->IoStatus.Status))
    {
        PKEYBOARD_INPUT_DATA myData;
        ULONG KeyNumber = NULL;
        //获取键盘数据
        myData = (PKEYBOARD_INPUT_DATA)Irp->AssociatedIrp.SystemBuffer;
        KeyNumber = ((ULONG)(Irp->IoStatus.Information) / sizeof(PKEYBOARD_INPUT_DATA));
​
        for (ULONG i = 0; i < KeyNumber; i++)
        {
            KdPrint(("number:%u\n", KeyNumber));
            KdPrint(("scancode:%x\n", myData->MakeCode));
        
        }
    }
    if (Irp->PendingReturned)
    {
        IoMarkIrpPending(Irp);
    }
    return Irp->IoStatus.Status;
}
​
NTSTATUS ReadRoutine(PDEVICE_OBJECT pDev, PIRP pIrp) {   
    KdPrint(("DriverB: 进入ReadRoutine\n"));
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDev->DeviceExtension;
    PIO_STACK_LOCATION stack;
​
    stack = IoGetCurrentIrpStackLocation(pIrp);
    IoCopyCurrentIrpStackLocationToNext(pIrp);
​
​
    // 设置完成例程
    IoSetCompletionRoutine(pIrp, FilterReadCompletionRoutine, NULL, TRUE, TRUE, TRUE);
    
    NTSTATUS status = IoCallDriver(pDevExt->pTargetDev, pIrp); // 将 IRP 传递给下层设备
​
    // 仅返回 IoCallDriver 的状态
    return status;
}
​
​
​
​
​
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
    UNREFERENCED_PARAMETER(RegistryPath);
    KdPrint(("Create!"));
​
    KdBreakPoint();
    // 驱动程序卸载例程&注册例程
    DriverObject->DriverUnload = DriverUnloadRoutine;
​
    for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i)
        DriverObject->MajorFunction[i] = DispatchRoutine;
​
    //注册
    DriverObject->MajorFunction[IRP_MJ_READ] = ReadRoutine;
​
​
    //创建设备
    NTSTATUS status;
    PDEVICE_OBJECT DeviceObject = NULL;
    UNICODE_STRING DeviceName;
    RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDevice");
    status = IoCreateDevice(
        DriverObject,                // 驱动程序对象
        sizeof(DEVICE_EXTENSION),                           // 设备扩展大小
        &DeviceName,                 // 设备名称
        FILE_DEVICE_UNKNOWN,         // 设备类型
        0,                           // 设备特征
        FALSE,                       // 非独占设备
        &DeviceObject                // 返回的设备对象指针
    );
​
    if (!NT_SUCCESS(status))
    {
        KdPrint(("Failed to create device: %X\n", status));
        return status;
    }
    KdPrint(("Device created successfully\n"));
​
​
​
​
    //附着在键盘驱动上面:
    //寻找目标驱动设备对象
    PDEVICE_OBJECT pTarget = nullptr;
    PDEVICE_OBJECT KbdDevice = nullptr;
    UNICODE_STRING driverName;
    OBJECT_ATTRIBUTES objectAttributes;
    PDRIVER_OBJECT KbdDriver = nullptr;
​
    RtlInitUnicodeString(&driverName, L"\\Driver\\kbdclass");
​
    // 初始化对象属性
    InitializeObjectAttributes(
        &objectAttributes,           // OBJECT_ATTRIBUTES 结构体
        &driverName,                 // 驱动对象名称
        OBJ_CASE_INSENSITIVE |       // 不区分大小写
        OBJ_KERNEL_HANDLE,           // 内核模式句柄
        NULL,                        // RootDirectory 为空
        NULL                         // SecurityDescriptor 为空
    );
  
    // 确保 IoDriverObjectType 已正确初始化
    if (IoDriverObjectType == NULL) {
        KdPrint(("IoDriverObjectType is NULL.\n"));
        return STATUS_UNSUCCESSFUL;
    }
​
​
    status = ObReferenceObjectByName(
        &driverName,                 // 驱动对象的名称
        OBJ_CASE_INSENSITIVE,        // 不区分大小写
        NULL,                        // 访问令牌:内核模式下可以为 NULL
        KernelMode,                  // 访问模式:内核模式
        *IoDriverObjectType,         // 对象类型:驱动对象
        KernelMode,                  // 处理器模式
        NULL,                        // 可选的句柄信息,通常为 NULL
        (PVOID*)&KbdDriver         // 输出:驱动对象指针
    );
​
​
​
    if (!NT_SUCCESS(status)) {
        KdPrint(("驱动对象失败\n"));
        return(status);
    }
​
​
    KbdDevice = KbdDriver->DeviceObject;
    KbdDevice = KbdDevice->NextDevice;
​
    // 获取成功后需要将过滤驱动附在在目标设备上面
    pTarget = IoAttachDeviceToDeviceStack(DeviceObject, KbdDevice);  //返回值是附着到的设备对象,有别于pTargetDevObj
    if (pTarget == NULL) { // 附着失败要进行清理工作
        IoDeleteDevice(DeviceObject);
        KdPrint(("附加失败!\n"));
        return(STATUS_UNSUCCESSFUL);
    }
​
​
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
    pDevExt->pTargetDev = pTarget;
    DeviceObject->Flags |= (pTarget->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO));//这行代码是将目标设备对象的 I/O 模式(即是否使用直接 I/O 或缓冲 I/O)复制到过滤设备对象中。
    DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;  //这行代码是清除过滤设备对象的 DO_DEVICE_INITIALIZING 标志,表示设备对象的初始化已完成。
    DeviceObject->Characteristics = pTarget->Characteristics; //复制属性
    DeviceObject->DeviceType = pTarget->DeviceType;  //复制属性
​
​
​
    return STATUS_SUCCESS;
}
VOID DriverUnloadRoutine(IN PDRIVER_OBJECT DriverObject)
{
    PDEVICE_OBJECT pDeviceObject = DriverObject->DeviceObject;
​
    while (pDeviceObject != NULL) {
        PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;
​
        // 如果过滤驱动已附加到目标设备,解除附加
        if (pDevExt->pTargetDev != NULL) {
            IoDetachDevice(pDevExt->pTargetDev);  // 取消附着
        }
​
        // 删除设备对象
        PDEVICE_OBJECT pNextDevice = pDeviceObject->NextDevice;  // 备份下一个设备对象
        IoDeleteDevice(pDeviceObject);  // 删除当前设备对象
        pDeviceObject = pNextDevice;  // 继续处理下一个设备对象
    }
​
    DbgPrint("Driver unloaded and detached successfully\n");
}

效果如下

1725930089141