过滤驱动

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

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

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

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

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

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

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

1725612145301

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

完成例程函数

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

1
2
3
4
5
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例程!

实战目标驱动,过滤驱动

目标驱动代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#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");
}

过滤驱动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#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;
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");
}

应用程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 应用层客户端
#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. HelloDDKA就接收到了IRP请求,并根据类型派遣到HelloDDKA的处理函数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会额外加一些奇奇怪怪的符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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是否立即返回,得看目标驱动的例程函数是啥,如果是同步,则会阻塞,如果是异步,那么就会立即返回,过滤驱动继续完成例程函数代码

同步的情况:

1
2
3
4
5
6
7
8
9
10
11
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; // 同步返回最终的处理状态
}

异步的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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。

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

1
2
3
4
5
if (Irp->PendingReturned)
{
IoMarkIrpPending(Irp);
}
return Irp->IoStatus.Status;

过滤驱动完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#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