代码例子来源: Win驱动9]Windows分层驱动_windows分层驱动程序 数据-CSDN博客
过滤驱动是一种独立的驱动程序,直接插入内核驱动栈中,拦截并处理驱动程序之间的数据传输。它可以拦截底层的I/O请求,并在不改变原始请求的情况下,修改、延迟或直接处理这些请求。
过滤驱动:位于设备驱动程序堆栈的上层,负责拦截、修改和处理 I/O 请求或响应,并可能将其转发给目标驱动。
目标驱动:位于设备驱动程序堆栈的下层,直接与硬件交互,处理实际的 I/O 操作。
和注册回调的区别: 过滤驱动运行在内核模式下的驱动栈中,拦截并处理I/O请求;回调函数则在某些事件发生时被内核或应用调用,可能在用户模式或内核模式下运行。 过滤驱动可以拦截设备级的I/O请求(如文件读写、网络包),而回调函数通常处理系统事件(如进程创建、线程创建、加载模块等)。 过滤驱动适合用于文件系统、网络、设备的深度控制和拦截;回调函数则用于监控和响应特定的系统事件,例如进程管理、内存分配等。
最重要的是:这玩意贼稳定,受微软支持的,想附加就附加
当请求完成的时候,会调用完成例程函数,用以下函数可以添加,可以从里面获取到键盘扫描码....吗?
但是我们还是会使用原来的键盘回调,等于又把完成例程函数修改回去了,所以这个方法并不可行
完成例程函数
通过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);
}
运行起来结果如下:
我们捋一下流程,选取Read_MJ_IRP来举例:
首先应用程序调用了ReadFile函数,ReadFile会调用NTDLL里的NtReadFile
NTDLL中的NtReadFile会调用系统调用中的NtReadFile
系统调用的NtReadFile会发送IRP_MJ_READ类型的IRP请求给对应的驱动设备(这里是目标驱动设备,也就是HelloDDKA)
我们附加的是HelloDDKB,附加在HelloDDKA上面,因为IRP都是从设备栈的栈顶开始自顶而下传递的,所以HelloDDKB首先截获IRP
HelloDDKB截获,所以先根据IRP类型进入了HelloDDKB的ReadRoutine里面
根据过滤驱动写的代码,ReadRoutine什么也不做,将其转发给了目标驱动HelloDDKA,并保留当前的IO环境,并阻塞在IoCallDriver上
HelloDDKB就接收到了IRP请求,并根据类型派遣到HelloDDKB的处理函数ReadRoutine
ReadRoutine将其挂起,并设置了DPC例程后直接返回挂起状态
但是此时因为HelloDDKB的IRP_MJ_READ还没有被结束,
IRP_MJ_READ
请求的处理通常需要等待调用IoCompleteRequest(pIrp, IO_NO_INCREMENT)
后才算正式结束,完成例程函数也是在IoCompleteRequest
调用后被触发。3秒后DPC例程被触发,被挂起的IRP请求完成
此时IRP会沿着设备栈向上返回,解除HelloDDKB中IoCallDriver的阻塞状态并继续执行
经过一步步网上返回后最终会将运行结果返回应用层的ReadFile函数
用过滤驱动拿到键盘扫描码
查阅资料可以知道这玩意是键盘的驱动,我们通过附加这个驱动,获取到键盘的扫描码
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");
}
效果如下