WDM

Window Driver Module

WDK

VS中没有找到单个组件的这些选项,因此使用官方的方法下载,单独下载sdk和wdk

然后会有一个 VSIX Installer,下载好之后,我们的VS就可以开发了

新建项目,应该可以看到 Driver 选项

或者使用Windows提供的虚拟机 Windows VM

驱动开发

windows驱动分两类,NT式驱动和WDM驱动,后者支持即插即用;

因此选择 WDM(Windows Driver Model), 提供一个驱动入口 DriverEntry 和 驱动卸载 DriverOnload 函数。需要注意C++ 的名称粉碎机制

右键,属性,Driver Setting

1
2
3
4
5
6
7
8
9
10
11
#include <ntddk.h>

EXTERN_C VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
DbgPrint("Unload Module Demo\r\n");
}

EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RigisterEntry) {
DriverObject->DriverUnload = DriverUnload;
DbgPrint("Load Module Demo\r\n");
return STATUS_SUCCESS;
}

官方案例不像 helloworld 的样子。制作你的第一个驱动程序

  • KMDF是由微软提供的一个高级抽象层,它建立在WDM(Windows Driver Model)之上,并提供了一些额外的功能和简化驱动程序开发的接口。KMDF旨在使驱动程序开发人员能够更轻松地编写和维护可靠的内核模式驱动程序。

驱动安装

OSRDriverLoader: Downloads:Driver Loader (osronline.com)

  • w2k: windows 2000
  • WLH: windows vista
  • WNET: windows 7 及以上。使用里面的 AMD64 OSRLOADER
  • WXP: windows XP

DEbug

DebugView - Sysinternals: 它可以显示 DbgPrint 打印出来的log

WinDbg: 需要设置一下,回显内核模式调试 Windows 驱动 - Windows drivers

1
2
3
4
5
# 调试器中显示来自目标系统的所有调试消息
ed nt!Kd_DEFAULT_MASK 0xFFFFFFFF

# 所有调试信息有点多,打印调试字符串
ed nt!Kd_Default_Mask 8

R0 与 R3 通信

驱动程序的主要功能也就是负责处理IO请求,其中大部分IO请求是在派遣函数中处理的。

R0

创建设备对象:使用IoCreateDevice必须管理员才能打开 而使用IoCreateDeviceSecure 则可以普通权限打开

1
2
3
4
5
6
7
8
9
NTSTATUS IoCreateDevice(
[in] PDRIVER_OBJECT DriverObject,
[in] ULONG DeviceExtensionSize,
[in, optional] PUNICODE_STRING DeviceName,
[in] DEVICE_TYPE DeviceType,
[in] ULONG DeviceCharacteristics,
[in] BOOLEAN Exclusive,
[out] PDEVICE_OBJECT *DeviceObject
);

设备名:符号链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
NTSTATUS IoCreateSymbolicLink(
[in] PUNICODE_STRING SymbolicLinkName,
[in] PUNICODE_STRING DeviceName
);

// MS-DOS 命名规范
UNICODE_STRING DeviceName;
UNICODE_STRING DosDeviceName;
NTSTATUS status;

RtlInitUnicodeString(&DeviceName, L"\\Device\\DeviceName");
RtlInitUnicodeString(&DosDeviceName, L"\\DosDevices\\DosDeviceName");
status = IoCreateSymbolicLink(&DosDeviceName, &DeviceName);
if (!NT_SUCCESS(status)) {
/* Symbolic link creation failed. Handle error appropriately. */
}

// NT规范:每个设备接口类都与 GUID 相关联。KMDF 给的代码

注册回调函数 MajorFunction[IDX](IDX 代表注册读写函数,宏函数,不要超过最大值 ;IRP (I/O Request Packet);DRIVER_OBJECT (wdm.h) - Windows drivers

1
2
3
4
5
6
7
8
9
_Use_decl_annotations_
NTSTATUS
DispatchXxx(
struct _DEVICE_OBJECT *DeviceObject,
struct _IRP *Irp
)
{
// Function body
}

删除符号

1
2
3
NTSTATUS IoDeleteSymbolicLink(
[in] PUNICODE_STRING SymbolicLinkName
);

删除设备

1
2
3
void IoDeleteDevice(
[in] PDEVICE_OBJECT DeviceObject
);

IO框架

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

#define DRIVER_NAME L"\\Device\\Demo"
#define LINK_NAME L"\\DosDevices\\Demo"

/*
IOCTL 控制码:
I/O 控制代码是由多个字段组成的 32 位值。
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
DeviceType: 标识设备类型。 此值必须与在驱动程序DEVICE_OBJECT结构的 DeviceType 成员中设置的值匹配
FunctionCode: 标识驱动程序要执行的函数。 小于 0x800 的值是为 Microsoft 保留的。 供应商可以使用 0x800 和更高值
#define CTL_CODE(DeviceType, Function, Method, Access) (
(DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
)
*/

#define IOCTRL_BASE 0x800
#define MYIOCTRL_CODE(i) \
CTL_CODE(FILE_DEVICE_UNKNOWN, IOCTRL_BASE + i, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define CTL_HELLO MYIOCTRL_CODE( 0 )

EXTERN_C VOID DriverUnload(PDRIVER_OBJECT pDriverObject) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Unload Driver\r\n");
}

// 返回值并不代表成功,而是 pIrp->IoStatus.Status 代表IO是否成功
NTSTATUS DispatchCommon(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
pIrp->IoStatus.Status = STATUS_SUCCESS; // IRP记录这次操作是否成功
pIrp->IoStatus.Information = 0; // Information用来记录实际传输的字节数的.

//提交请求.
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "DispatchCommon\r\n"));
return STATUS_SUCCESS; // 上面的 STATUS_SUCCESS是给R3看的.现在的返回时给IO管理器系统的
}

NTSTATUS DispatchRead(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
PVOID pReadBuffer = NULL;
ULONG uReadLength = 0;
PIO_STACK_LOCATION pStack = NULL;
ULONG uMin = 0;
ULONG uHelloStr = 0;

uHelloStr = (wcslen(L"Hello World") + 1) * sizeof(WCHAR);
pReadBuffer = pIrp->AssociatedIrp.SystemBuffer; //缓冲区

//获取IRP堆栈.我们说过3环调用0环.需要封装在IRP结构中.windows是分层驱动.所以IRP头部是共用的.其余的是栈传递.
pStack = IoGetCurrentIrpStackLocation(pIrp);
uReadLength = pStack->Parameters.Read.Length;
uMin = uReadLength > uHelloStr ? uHelloStr : uReadLength;

RtlCopyMemory(pReadBuffer, L"Hello World", uMin); //拷贝到缓冲区中给3环.

pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = uMin;

//提交请求.
IoCompleteRequest(pIrp, IO_NO_INCREMENT);

return STATUS_SUCCESS;
}

NTSTATUS DispatchWrite(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
PVOID pWriteBuffer = NULL;
ULONG uWriteLength = 0;
PIO_STACK_LOCATION pIrpStack = NULL;
PVOID pBuffer = NULL;
//获取IRP堆栈
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);

//获取写的长度.
uWriteLength = pIrpStack->Parameters.Write.Length;
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;

pWriteBuffer = pIrp->AssociatedIrp.SystemBuffer;
//申请内存.
pBuffer = ExAllocatePoolWithTag(PagedPool, uWriteLength, 'DEMO');
if (NULL == pBuffer)
{
pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_INSUFFICIENT_RESOURCES;
}
//提交请求.

RtlZeroMemory(pBuffer, uWriteLength);
//拷贝到0环缓冲区
RtlCopyMemory(pBuffer, pWriteBuffer, uWriteLength);
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Write %s\r\n", (PCHAR)pBuffer));
ExFreePool(pBuffer);
pBuffer = NULL;
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = uWriteLength;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);

return STATUS_SUCCESS;
}

NTSTATUS DispatchControl(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
{
//内核中共享 SystemBuffer 有时间差.先读在写.
PIO_STACK_LOCATION pIrpStack;
PVOID InPutBuffer = NULL;
PVOID OutPutBuffer = NULL;
ULONG uInPutLength = 0;
ULONG uOutPutBufferLength = 0;
ULONG IoCtrl = 0;

InPutBuffer = OutPutBuffer = pIrp->AssociatedIrp.SystemBuffer;
pIrpStack = IoGetCurrentIrpStackLocation(pIrp);

IoCtrl = pIrpStack->Parameters.DeviceIoControl.IoControlCode; //获取控制码.


switch(IoCtrl)
{
case CTL_HELLO:
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "IOCTL HelloWorld\r\n"));
break;
default:
break;
}

pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0;

//提交请求.
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRigisterEntry) {
UNICODE_STRING uDevName = { 0 };
UNICODE_STRING uLinkName = { 0 };
NTSTATUS status;
PDEVICE_OBJECT pDevObj;
RtlInitUnicodeString(&uDevName, DRIVER_NAME);
RtlInitUnicodeString(&uLinkName, LINK_NAME);

DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Init Driver\r\n");

pDriverObject->DriverUnload = DriverUnload;

// 创建设备
status = IoCreateDevice(pDriverObject, 0, &uDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
if (!NT_SUCCESS(status))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[-] Error IoCreateDevice\r\n");
return status;
}

// 不是这个=>蓝屏
pDevObj->Flags |= DO_BUFFERED_IO;

// 创建符号链接
status = IoCreateSymbolicLink(&uLinkName, &uDevName);
if (!NT_SUCCESS(status))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[-] Error IoCreateSymbolicLink\r\n");
IoDeleteDevice(pDevObj);
return status;
}

// IO
for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
pDriverObject->MajorFunction[i] = DispatchCommon;
}
// Read Write IOCTL
pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;
pDriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchWrite;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchControl;

DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[+] Create Driver Success\r\n");

return STATUS_SUCCESS;
}

R3

在用户模式下打开 \\DosDevices\\DosDeviceName 设备,请在打开文件名时指定 \\.\,需要转义符

1
2
3
4
5
6
7
file = CreateFileW(L"\\\\.\\DosDeviceName",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);

通过ReadFileWriteFile 系统调用进行读写,ioctl使用

1
2
3
4
5
6
7
8
9
10
BOOL DeviceIoControl(
[in] HANDLE hDevice,
[in] DWORD dwIoControlCode,
[in, optional] LPVOID lpInBuffer,
[in] DWORD nInBufferSize,
[out, optional] LPVOID lpOutBuffer,
[in] DWORD nOutBufferSize,
[out, optional] LPDWORD lpBytesReturned,
[in, out, optional] LPOVERLAPPED lpOverlapped
);

交互

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
#include <Windows.h>
#include <iostream>


#define IOCTRL_BASE 0x800
#define MYIOCTRL_CODE(i) \
CTL_CODE(FILE_DEVICE_UNKNOWN, IOCTRL_BASE + i, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define CTL_HELLO MYIOCTRL_CODE( 0 )

int main(int argc, char* argv[])
{
HANDLE hFile = CreateFileW(TEXT("\\\\.\\Demo"),
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("CreateFile ErrorCode:%d\n", GetLastError());
return 0;
}
system("pause");
TCHAR szBuff[0X100];
DWORD dwBytes = 0;
if (!ReadFile(hFile, szBuff, sizeof(szBuff) / sizeof(szBuff[0]), &dwBytes, NULL))
{
CloseHandle(hFile);
printf("ReadFile ErrorCode:%d\n", GetLastError());
return 0;
}
printf("bytes:%d data:%ls\n", dwBytes, szBuff);
system("pause");
DeviceIoControl(
hFile,
CTL_HELLO,
NULL,
0,
NULL,
0,
NULL,
NULL
);
CloseHandle(hFile);
system("pause");
return 0;
}


more

关于 DEBUG 问题,常见 DbgPrint, DbgPrintExKdPrintEx这种是宏表示。Ex 函数为了更好的打印出调试信息(消息过滤机制:读取和筛选调试消息 - Windows drivers

1
2
3
4
5
6
7
8
9
10
11
12
#define KdPrintEx(_x_) DbgPrintEx _x_ 
NTSYSAPI ULONG DbgPrintEx(
[in] ULONG ComponentId, // 避免将驱动程序的输出与 Windows 组件的输出混合
[in] ULONG Level, // 指定要发送的消息的严重性
[in] PCSTR Format,
...
);
// DPF: Debug Print Filter
// IHV: Independent Hardware Vendor

KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "DPFLTR_ERROR_LEVEL\n")); // 需要双括号
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "DPFLTR_ERROR_LEVEL\n");

WDF…

驱动写出BUG直接蓝屏😢

Library

Rtl

Windows的Runtime Library(Rtl)函数是一组供Windows操作系统内部使用的函数。它们是由微软提供的低级别函数,用于执行操作系统管理功能,如内存管理、进程和线程管理、文件操作、设备驱动程序和底层交互等。

这些Rtl函数主要用于Windows内核和系统级驱动程序开发,不是普通应用程序开发人员常用的函数库。它们提供了一些高级别函数和抽象层,以方便开发人员与底层操作系统交互和管理。

nt

Windows NT是一个基于内核的操作系统系列,最初由微软公司在20世纪90年代开发和推出。NT代表”New Technology”(新技术),它的设计目标是提供一种稳定、可靠、安全的操作系统平台。

一些常见的Windows NT库:

  1. Kernel32.lib:这是Windows NT内核库,提供了许多系统级别的函数和工具,包括内存管理、进程和线程管理、文件和I/O操作、时间和日期处理等。
  2. User32.lib:这是用户界面库,提供了与用户界面相关的函数,如窗口管理、消息处理、菜单操作、图形设备接口等。