WDF框架学习

WDF是什么?

Windows Driver Framework (WDF) 是微软提供的一套用于开发Windows设备驱动程序的框架。WDF分为两个子框架:

KMDF (Kernel-Mode Driver Framework):用于开发内核模式的驱动程序。
UMDF (User-Mode Driver Framework):用于开发用户模式的驱动程序。
WDF框架简化了驱动程序开发的复杂性,提供了丰富的API和工具,使得开发者能够更专注于业务逻辑的实现,而不是底层驱动的细节。

总之,将WDF理解为对WDM做了封装,类似于SDK和MFC之间的关系

第一节:第一个WFP驱动程序:

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
#include <ntddk.h>
#include <wdf.h>

#define DebugPrint(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,__VA_ARGS__)

void MyDriverUnload(
_In_ WDFDRIVER Driver
)
{
Driver;
// 释放驱动程序占用的自定义资源
// 例如,释放全局变量、关闭句柄等

// 注意:WDF 框架会自动释放 WDF 对象(如设备对象、队列对象等)
// 因此不需要手动释放这些对象

// 打印调试信息(可选)
DebugPrint("MyDriverUnload: Driver is being unloaded.\n");
}


NTSTATUS DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
WDF_DRIVER_CONFIG config;
WDFDRIVER driver;
KdBreakPoint();
// 初始化 WDF 驱动程序配置
WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
config.DriverInitFlags |= WdfDriverInitNonPnpDriver;
config.EvtDriverUnload = MyDriverUnload;

// 创建 WDF 驱动程序对象
status = WdfDriverCreate(
DriverObject, // WDM 驱动对象
RegistryPath, // 注册表路径
WDF_NO_OBJECT_ATTRIBUTES, // 驱动程序属性(可选)
&config, // 驱动程序配置
&driver // 返回的 WDFDRIVER 句柄
);

if (!NT_SUCCESS(status)) {
// 处理错误
DebugPrint("WFP驱动初始化失败!\n");
return status;
}

// 其他初始化操作
// ...
DebugPrint("WFP驱动初始化成功!\n");
return STATUS_SUCCESS;
}

相关函数介绍

WDF_DRIVER_CONFIG_INIT

WDF_DRIVER_CONFIG_INIT 是 Windows Driver Framework (WDF) 中的一个宏,用于初始化 WDF_DRIVER_CONFIG 结构体。

1
2
3
4
5
6
7
typedef struct _WDF_DRIVER_CONFIG {
ULONG Size;
PFN_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd;
PFN_WDF_DRIVER_UNLOAD EvtDriverUnload;
ULONG DriverInitFlags;
ULONG DriverPoolTag;
} WDF_DRIVER_CONFIG, *PWDF_DRIVER_CONFIG;

EvtDriverDeviceAdd作用:指定设备添加回调函数。当设备被添加到驱动程序时,WDF 框架会调用这个函数。

EvtDriverUnload:指定驱动程序卸载回调函数。当驱动程序被卸载时,WDF 框架会调用这个函数。如果不需要卸载回调函数,可以设置为 NULL。并WDF 框架会自动释放 WDF 对象(如设备对象、队列对象等,因此不需要手动释放这些对象,只需要释放掉自己用到的其他对象

DriverInitFlags
WdfDriverInitNonPnpDriver:表示这是一个非 PnP 驱动程序。
WdfDriverInitNoDispatchOverride:表示驱动程序不会覆盖默认的调度例程。
WdfDriverInitNoFormatChaining:表示驱动程序不会使用格式链。

DriverPoolTag:指定驱动程序使用的内存池标签。内存池标签是一个 4 字符的标识符,用于调试和跟踪内存分配。如果不需要自定义内存池标签,可以设置为 0。

WDF_DRIVER_CONFIG 是 Windows Driver Framework (WDF) 中的一个重要结构体,用于配置 WDF 驱动程序的行为和属性。它在 DriverEntry 函数中用于初始化驱动程序,并传递给 WdfDriverCreate 函数。

1
2
3
4
5
6
VOID
FORCEINLINE
WDF_DRIVER_CONFIG_INIT(
_Out_ PWDF_DRIVER_CONFIG Config,
_In_opt_ PFN_WDF_DRIVER_DEVICE_ADD EvtDriverDeviceAdd
)

其中:**EvtDriverDeviceAdd** 如果不需要设备添加回调函数,可以传递 WDF_NO_EVENT_CALLBACK

调用时机:当该驱动有相关设备被创建,则会调用这个EvtDriverDeviceAdd

WdfDriverCreate

WdfDriverCreate 并不是创建一个新的驱动对象,而是创建一个 WDF 驱动程序对象,并将其与现有的 DriverObject 关联起来,以便使用 WDF 框架的功能。

1
2
3
4
5
6
7
NTSTATUS WdfDriverCreate(
[in] PDRIVER_OBJECT DriverObject,
[in] PCUNICODE_STRING RegistryPath,
[in, optional] PWDF_OBJECT_ATTRIBUTES DriverAttributes,
[in] PWDF_DRIVER_CONFIG DriverConfig,
[out, optional] WDFDRIVER *Driver
);

WDF_DRIVER_CONFIG 是 Windows Driver Framework (WDF) 中的一个重要结构体,用于配置 WDF 驱动程序的行为和属性。它在 DriverEntry 函数中用于初始化驱动程序,并传递给 WdfDriverCreate 函数。

介绍下_WDF_OBJECT_ATTRIBUTES 结构

1
2
3
4
5
6
7
8
9
10
typedef struct _WDF_OBJECT_ATTRIBUTES {
ULONG Size;
PFN_WDF_OBJECT_CONTEXT_CLEANUP EvtCleanupCallback;
PFN_WDF_OBJECT_CONTEXT_DESTROY EvtDestroyCallback;
WDF_EXECUTION_LEVEL ExecutionLevel;
WDF_SYNCHRONIZATION_SCOPE SynchronizationScope;
WDFOBJECT ParentObject;
size_t ContextSizeOverride;
PCWDF_OBJECT_CONTEXT_TYPE_INFO ContextTypeInfo;
} WDF_OBJECT_ATTRIBUTES, *PWDF_OBJECT_ATTRIBUTES;

其中, 如果你定义了 EvtCleanupCallbackEvtDestroyCallback,那么你需要在这些回调函数中手动释放与 WDF 对象相关的资源。 否则WDF框架会自动清除掉这些对象的资源

  • **EvtCleanupCallback**:
    • WDF 设备对象 的引用计数降为 0 时,WDF 框架会调用 EvtCleanupCallback
    • 此时设备对象仍然有效,开发人员可以访问对象的上下文内存和其他属性。
  • **EvtDestroyCallback**:
    • EvtCleanupCallback 执行完毕后,WDF 框架会调用 EvtDestroyCallback
    • 此时设备对象已经无效,开发人员不能访问对象的上下文内存或其他属性。

WdfObjectDereference用来减少WDF设备的引用计数

第二节:设备对象,文件操作,符号链接

WdfControlDeviceInitAllocate 是 Windows Driver Framework (WDF) 中的一个函数,用于为控制设备分配一个 WDFDEVICE_INIT 结构体。

1
2
3
4
PWDFDEVICE_INIT WdfControlDeviceInitAllocate(
[in] WDFDRIVER Driver,
[in] const UNICODE_STRING *SDDLString
);

SDDLString:举个例子SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX 定义了以下权限:

  • 系统(SY):完全访问。
  • 管理员组(BA):完全访问。
  • 所有用户(WD):完全访问。
  • 受限代码(RC):完全访问。

WdfDeviceInitAssignName 是 Windows 驱动程序框架(WDF)中的一个函数,用于为设备对象分配一个名称。

1
2
3
4
NTSTATUS WdfDeviceInitAssignName(
[in] PWDFDEVICE_INIT DeviceInit,
[in, optional] PCUNICODE_STRING DeviceName
);

设备对象中的驱动程序框架文件对象(WDFFILEOBJECT) 可以理解为内核驱动程序与应用层通信的桥梁。它在 WDF(Windows 驱动程序框架)中扮演了非常重要的角色,主要用于管理与设备对象相关的用户模式句柄和上下文数据。

WDF_FILEOBJECT_CONFIG_INIT 函数初始化驱动程序WDF_FILEOBJECT_CONFIG结构。

1
2
3
4
5
6
void WDF_FILEOBJECT_CONFIG_INIT(
[out] PWDF_FILEOBJECT_CONFIG FileEventCallbacks,
[in, optional] PFN_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate,
[in, optional] PFN_WDF_FILE_CLOSE EvtFileClose,
[in, optional] PFN_WDF_FILE_CLEANUP EvtFileCleanup
);

当用户模式应用程序调用 CreateFile 或内核模式组件调用 ZwCreateFile 时, WDF 会触发 EvtDeviceFileCreate

用户模式应用程序调用 CloseHandle 或内核模式组件调用 ZwClose 时,WDF 会触发EvtWdfFileCleanup

当文件对象完全关闭时,驱动程序的 EvtWdfFileClose 被调用。

WdfDeviceInitSetFileObjectConfig

该方法注册事件回调函数并设置驱动程序框架文件对象的配置信息。

1
2
3
4
5
void WdfDeviceInitSetFileObjectConfig(
[in] PWDFDEVICE_INIT DeviceInit,
[in] PWDF_FILEOBJECT_CONFIG FileObjectConfig,
[in, optional] PWDF_OBJECT_ATTRIBUTES FileObjectAttributes
);

完整代码:

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
#include <ntddk.h>
#include <wdf.h>
#include <sddl.h> // SDDL 相关定义
#define DebugPrint(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,__VA_ARGS__)


EVT_WDF_DEVICE_FILE_CREATE EvtWdfDeviceFileCreate;
EVT_WDF_FILE_CLOSE EvtWdfFileClose;
EVT_WDF_FILE_CLEANUP EvtWdfFileCleanup;

void EvtWdfFileCleanup(
WDFFILEOBJECT FileObject
)
{
FileObject;
}


void EvtWdfFileClose(
WDFFILEOBJECT FileObject
)
{
FileObject;
}

void EvtWdfDeviceFileCreate(
WDFDEVICE Device,
WDFREQUEST Request,
WDFFILEOBJECT FileObject
)
{
Device;
Request;
FileObject;
}



void MyDriverUnload(
_In_ WDFDRIVER Driver
)
{
Driver;
// 释放驱动程序占用的自定义资源
// 例如,释放全局变量、关闭句柄等

// 注意:WDF 框架会自动释放 WDF 对象(如设备对象、队列对象等)
// 因此不需要手动释放这些对象

// 打印调试信息(可选)
DebugPrint("MyDriverUnload: Driver is being unloaded.\n");
}


NTSTATUS DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NTSTATUS status; //状态
WDF_DRIVER_CONFIG config; //WDF驱动配置
WDFDRIVER driver; //WDF驱动对象
WDFDEVICE Device; //WDF设备对象
PWDFDEVICE_INIT DeviceInit; //初始化设备对象的创建过程
UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\HelloWDF"); //设备对象的名字
UNICODE_STRING SymbolicLinkName = RTL_CONSTANT_STRING(L"\\??\\HelloWDF"); //
WDF_FILEOBJECT_CONFIG FileConfig;

KdBreakPoint();
// 初始化 WDF 驱动程序配置
WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
config.DriverInitFlags |= WdfDriverInitNonPnpDriver;
config.EvtDriverUnload = MyDriverUnload;

// 创建 WDF 驱动程序对象
status = WdfDriverCreate(
DriverObject, // WDM 驱动对象
RegistryPath, // 注册表路径
WDF_NO_OBJECT_ATTRIBUTES, // 驱动程序属性(可选)
&config, // 驱动程序配置
&driver // 返回的 WDFDRIVER 句柄
);


DeviceInit = WdfControlDeviceInitAllocate(driver,&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX);
if (DeviceInit == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto End;
}


status=WdfDeviceInitAssignName(DeviceInit, &DeviceName);
if (!NT_SUCCESS(status))
{
DebugPrint("设备分配失败!\n");
goto End;
}


//WDF_FILEOBJECT_CONFIG_INIT 函数初始化驱动程序WDF_FILEOBJECT_CONFIG结构
WDF_FILEOBJECT_CONFIG_INIT(&FileConfig, EvtWdfDeviceFileCreate, EvtWdfFileClose, EvtWdfFileCleanup);

//WdfDeviceInitSetFileObjectConfig 方法注册事件回调函数并设置驱动程序框架文件对象的配置信息。
WdfDeviceInitSetFileObjectConfig(DeviceInit, &FileConfig, WDF_NO_OBJECT_ATTRIBUTES);

status = WdfDeviceCreate(&DeviceInit,WDF_NO_OBJECT_ATTRIBUTES,&Device);
if (!NT_SUCCESS(status)) {
// 处理错误
DebugPrint("设备创建失败\n");
goto End;
}

status = WdfDeviceCreateSymbolicLink(Device, &SymbolicLinkName);
if (!NT_SUCCESS(status)) {
// 处理错误
DebugPrint("符号链接创建失败\n");
goto End;
}

WdfControlFinishInitializing(Device);

// 其他初始化操作
// ...
DebugPrint("WFP驱动初始化成功!\n");
return STATUS_SUCCESS;

End:
// ...
DebugPrint("WFP驱动初始化失败!\n");
return STATUS_UNSUCCESSFUL;

}

总结

  • **DriverEntry**:驱动程序的入口函数,负责初始化驱动程序。
  • **WdfDriverCreate**:创建 WDF 驱动程序对象。
  • **WdfControlDeviceInitAllocate**:分配设备初始化结构。
  • **WdfDeviceInitAssignName**:设置设备名称。
  • **WDF_FILEOBJECT_CONFIG_INIT**:初始化文件对象配置。
  • **WdfDeviceInitSetFileObjectConfig**:设置文件对象配置。
  • **WdfDeviceCreate**:创建设备对象。
  • **WdfDeviceCreateSymbolicLink**:创建符号链接。
  • **WdfControlFinishInitializing**:完成设备初始化。

第三节:编写应用程序访问WDF驱动

在第二节,我们写了对于文件的三个回调,分别是以下三个

当然要说明: 文件对象的回调函数(如 EvtWdfDeviceFileCreateEvtWdfFileCloseEvtWdfFileCleanup)主要用于管理文件对象的生命周期,而不是直接处理 I/O 请求(如 ReadFileWriteFile)。WDF 使用队列(Queue)机制来处理 I/O 请求,而不是像 WDM 那样直接通过回调函数处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
void EvtWdfFileCleanup(
WDFFILEOBJECT FileObject
)

void EvtWdfFileClose(
WDFFILEOBJECT FileObject
)

void EvtWdfDeviceFileCreate(
WDFDEVICE Device,
WDFREQUEST Request,
WDFFILEOBJECT FileObject
)

其中,Create回调里面有一个Request,这个我们必须处理它

处理的方式还是比较多的,例如

1.直接完成请求:(当然你也可以验证资质)

1
2
// 直接完成请求
WdfRequestComplete(Request, STATUS_SUCCESS);

2.初始化文件对象上下文

1
2
3
4
5
6
7
8
9
10
11
12
// 获取文件对象的上下文数据
PFILE_CONTEXT fileContext = FileObjectGetContext(FileObject);

// 初始化上下文数据
fileContext->Buffer = ExAllocatePoolWithTag(NonPagedPool, 1024, 'BufT');
if (fileContext->Buffer == NULL) {
WdfRequestComplete(Request,STATUS_INSUFFICIENT_RESOURCES);
return;
}

// 完成请求
WdfRequestComplete(Request, STATUS_SUCCESS);

3.异步完成请求: 如果设备对象的打开操作需要较长时间(例如等待某个资源),可以将请求标记为挂起,并在操作完成后异步完成请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void EvtWdfDeviceFileCreate(
WDFDEVICE Device,
WDFREQUEST Request,
WDFFILEOBJECT FileObject
) {
UNREFERENCED_PARAMETER(Device);
UNREFERENCED_PARAMETER(FileObject);

// 将请求标记为挂起
WdfRequestMarkPending(Request);

// 异步完成请求(例如在某个工作线程中)
MyAsyncOpenDevice(Request);
}

void MyAsyncOpenDevice(WDFREQUEST Request) {
// 模拟异步操作
KeDelayExecutionThread(KernelMode, FALSE, 1000);

// 完成请求
WdfRequestComplete(Request, STATUS_SUCCESS);
}

winobj可以看到全局的符号链接:

1736737321105

直接在应用层去访问这个设备

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
#include <windows.h>
#include <stdio.h>

#define DEVICE_NAME L"\\\\.\\HelloWDF" // 设备的符号链接名称

int main() {
system("pause");
HANDLE hDevice = INVALID_HANDLE_VALUE;
BOOL result = FALSE;
DWORD bytesReturned = 0;
char outputBuffer[100] = { 0 };

// 打开设备
hDevice = CreateFile(
DEVICE_NAME, // 设备名称
GENERIC_READ | GENERIC_WRITE, // 访问权限
0, // 共享模式(0 表示独占)
NULL, // 安全属性
OPEN_EXISTING, // 创建选项
FILE_ATTRIBUTE_NORMAL, // 文件属性
NULL // 模板文件句柄
);

if (hDevice == INVALID_HANDLE_VALUE) {
printf("Failed to open device. Error: %d\n", GetLastError());
return 1;
}
return 0;
}

可以看到成功断下来了

1736737692878

第四节:实现WDF的IO队列

本节将实现对文件设备的控制操作

首先我们先建立一个IO队列的配置

1
WDF_IO_QUEUE_CONFIG       ioConfig

然后用WDF_IO_QUEUE_CONFIG_INIT来初始化这个ioConfig

1
2
3
4
void WDF_IO_QUEUE_CONFIG_INIT(
[out] PWDF_IO_QUEUE_CONFIG Config,
[in] WDF_IO_QUEUE_DISPATCH_TYPE DispatchType
);

其中 DispatchType枚举值是这样的

描述
WdfIoQueueDispatchInvalid 无效的分发类型(仅用于内部验证)。
WdfIoQueueDispatchSequential 顺序分发:I/O 请求按顺序处理,一次只处理一个请求。
WdfIoQueueDispatchParallel 并行分发:I/O 请求可以同时处理,适用于高性能场景。
WdfIoQueueDispatchManual 手动分发:驱动程序需要手动从队列中取出请求并处理。
WdfIoQueueDispatchMax 枚举的最大值(仅用于内部验证)。

_WDF_IO_QUEUE_CONFIG结构中,我们可以发现很多WDM熟悉的东西,例如READ,WRITE,IO_DEVICE_CONTROL,这就是对应的回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _WDF_IO_QUEUE_CONFIG {
ULONG Size;
WDF_IO_QUEUE_DISPATCH_TYPE DispatchType;
WDF_TRI_STATE PowerManaged;
BOOLEAN AllowZeroLengthRequests;
BOOLEAN DefaultQueue;
PFN_WDF_IO_QUEUE_IO_DEFAULT EvtIoDefault;
PFN_WDF_IO_QUEUE_IO_READ EvtIoRead;
PFN_WDF_IO_QUEUE_IO_WRITE EvtIoWrite;
PFN_WDF_IO_QUEUE_IO_DEVICE_CONTROL EvtIoDeviceControl;
PFN_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL EvtIoInternalDeviceControl;
PFN_WDF_IO_QUEUE_IO_STOP EvtIoStop;
PFN_WDF_IO_QUEUE_IO_RESUME EvtIoResume;
PFN_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE EvtIoCanceledOnQueue;
union {
struct {
ULONG NumberOfPresentedRequests;
} Parallel;
} Settings;
WDFDRIVER Driver;
} WDF_IO_QUEUE_CONFIG, *PWDF_IO_QUEUE_CONFIG;

这里做一个表格帮助理解

WDF 回调函数 对应三环 API 描述
EvtIoDefault 无直接对应 默认处理逻辑,覆盖未分类的请求。
EvtIoRead ReadFile, ReadFileEx 处理读请求。
EvtIoWrite WriteFile, WriteFileEx 处理写请求。
EvtIoDeviceControl DeviceIoControl 处理用户模式的 IOCTL 请求。
EvtIoInternalDeviceControl 无直接对应 处理内核模式的 IOCTL 请求。
EvtIoStop 无直接对应 恢复队列请求
EvtIoResume 无直接对应 恢复队列请求
EvtIoCanceledOnQueue CancelIo, CancelIoEx 用户取消队列中未完成的请求时调用。

总结一下顺序

  1. 设备初始化
    • 调用 WdfDeviceInitAllocateWdfControlDeviceInitAllocate 分配并初始化 WDFDEVICE_INIT 结构。
    • 设置设备属性(如设备名称、安全描述符等)。
  2. 文件对象配置初始化
    • 使用 WDF_FILEOBJECT_CONFIG_INIT 初始化 WDF_FILEOBJECT_CONFIG 结构。
    • 设置文件对象的回调函数(如 EvtWdfDeviceFileCreateEvtWdfFileClose 等)。
  3. 设置文件对象配置
    • 调用 WdfDeviceInitSetFileObjectConfig 将文件对象配置与设备初始化结构关联。
  4. 创建设备对象
    • 调用 WdfDeviceCreate 创建设备对象。
  5. 创建 I/O 队列
    • 在设备对象创建成功后,调用 WdfIoQueueCreate 创建 I/O 队列。
  6. 完成设备创建
    WdfControlFinishInitializing(Device);

本节完整驱动程序:

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
#include <ntddk.h>
#include <wdf.h>
#include <sddl.h> // SDDL 相关定义
#define DebugPrint(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,__VA_ARGS__)


EVT_WDF_DEVICE_FILE_CREATE EvtWdfDeviceFileCreate;
EVT_WDF_FILE_CLOSE EvtWdfFileClose;
EVT_WDF_FILE_CLEANUP EvtWdfFileCleanup;


VOID m_EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL(
_In_
WDFQUEUE Queue,
_In_
WDFREQUEST Request,
_In_
size_t OutputBufferLength,
_In_
size_t InputBufferLength,
_In_
ULONG IoControlCode
)
{
Queue;
Request;
OutputBufferLength;
InputBufferLength;
IoControlCode;
DebugPrint("IO_DEVICE_CONTROL\n");
WdfRequestComplete(Request, STATUS_SUCCESS);
}

void EvtWdfFileCleanup(
WDFFILEOBJECT FileObject
)
{
FileObject;
DebugPrint("clean_up\n");
}


void EvtWdfFileClose(
WDFFILEOBJECT FileObject
)
{
FileObject;
DebugPrint("close\n");
}

void EvtWdfDeviceFileCreate(
WDFDEVICE Device,
WDFREQUEST Request,
WDFFILEOBJECT FileObject
)
{
Device;
Request;
FileObject;

DebugPrint("file_create\n");
WdfRequestComplete(Request, STATUS_SUCCESS);

}



void MyDriverUnload(
_In_ WDFDRIVER Driver
)
{
Driver;
// 释放驱动程序占用的自定义资源
// 例如,释放全局变量、关闭句柄等

// 注意:WDF 框架会自动释放 WDF 对象(如设备对象、队列对象等)
// 因此不需要手动释放这些对象

// 打印调试信息(可选)
DebugPrint("MyDriverUnload: Driver is being unloaded.\n");
}


NTSTATUS DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NTSTATUS status; //状态
WDF_DRIVER_CONFIG config; //WDF驱动配置
WDFDRIVER driver; //WDF驱动对象
WDFDEVICE Device; //WDF设备对象
PWDFDEVICE_INIT DeviceInit; //初始化设备对象的创建过程
UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\HelloWDF"); //设备对象的名字
UNICODE_STRING SymbolicLinkName = RTL_CONSTANT_STRING(L"\\??\\HelloWDF"); //
WDF_FILEOBJECT_CONFIG FileConfig; //文件对象的配置信息
WDF_IO_QUEUE_CONFIG ioConfig; //IO队列的配置信息
WDFQUEUE Queue; //这是IO队列句柄

KdBreakPoint();
// 初始化 WDF 驱动程序配置
WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
config.DriverInitFlags |= WdfDriverInitNonPnpDriver;
config.EvtDriverUnload = MyDriverUnload;

// 创建 WDF 驱动程序对象
status = WdfDriverCreate(
DriverObject, // WDM 驱动对象
RegistryPath, // 注册表路径
WDF_NO_OBJECT_ATTRIBUTES, // 驱动程序属性(可选)
&config, // 驱动程序配置
&driver // 返回的 WDFDRIVER 句柄
);


DeviceInit = WdfControlDeviceInitAllocate(driver,&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX);
if (DeviceInit == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto End;
}


status=WdfDeviceInitAssignName(DeviceInit, &DeviceName);
if (!NT_SUCCESS(status))
{
DebugPrint("设备分配失败!\n");
goto End;
}


//WDF_FILEOBJECT_CONFIG_INIT 函数初始化驱动程序WDF_FILEOBJECT_CONFIG结构
WDF_FILEOBJECT_CONFIG_INIT(&FileConfig, EvtWdfDeviceFileCreate, EvtWdfFileClose, EvtWdfFileCleanup);

//WdfDeviceInitSetFileObjectConfig 方法注册事件回调函数并设置驱动程序框架文件对象的配置信息。
WdfDeviceInitSetFileObjectConfig(DeviceInit, &FileConfig, WDF_NO_OBJECT_ATTRIBUTES);

status = WdfDeviceCreate(&DeviceInit,WDF_NO_OBJECT_ATTRIBUTES,&Device);
if (!NT_SUCCESS(status)) {
// 处理错误
DebugPrint("设备创建失败\n");
goto End;
}



WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioConfig, WdfIoQueueDispatchSequential);

ioConfig.EvtIoDeviceControl = m_EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL;

status = WdfIoQueueCreate(Device, &ioConfig, WDF_NO_OBJECT_ATTRIBUTES, &Queue);
if (!NT_SUCCESS(status)) {
// 处理错误
DebugPrint("设备队列创建失败\n");
goto End;
}


status = WdfDeviceCreateSymbolicLink(Device, &SymbolicLinkName);
if (!NT_SUCCESS(status)) {
// 处理错误
DebugPrint("符号链接创建失败\n");
goto End;
}

WdfControlFinishInitializing(Device);
// 其他初始化操作
// ...
DebugPrint("WFP驱动初始化成功!\n");
return STATUS_SUCCESS;

End:
// ...
DebugPrint("WFP驱动初始化失败!\n");
return STATUS_UNSUCCESSFUL;

}

应用层:

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
#include <windows.h>
#include <stdio.h>

#define DEVICE_NAME L"\\\\.\\HelloWDF" // 设备的符号链接名称

int main() {
system("pause");
HANDLE hDevice = INVALID_HANDLE_VALUE;
BOOL result = FALSE;
DWORD bytesReturned = 0;
//char outputBuffer[100] = { 0 };
char inputBuffer[100] = "Test input data"; // 输入缓冲区示例
char outputBuffer[100] = { 0 }; // 输出缓冲区
// 打开设备
hDevice = CreateFile(
DEVICE_NAME, // 设备名称
GENERIC_READ | GENERIC_WRITE, // 访问权限
0, // 共享模式(0 表示独占)
NULL, // 安全属性
OPEN_EXISTING, // 创建选项
FILE_ATTRIBUTE_NORMAL, // 文件属性
NULL // 模板文件句柄
);

if (hDevice == INVALID_HANDLE_VALUE) {
printf("Failed to open device. Error: %d\n", GetLastError());
return 1;
}


// 调用 DeviceIoControl
result = DeviceIoControl(
hDevice, // 设备句柄
NULL, // 控制代码
inputBuffer, // 输入缓冲区
sizeof(inputBuffer), // 输入缓冲区大小
outputBuffer, // 输出缓冲区
sizeof(outputBuffer), // 输出缓冲区大小
&bytesReturned, // 返回的字节数
NULL // 重叠结构(非异步调用时为 NULL)
);
return 0;
}

实现效果:

1736741233393

遇到的坑

1736693479167