Windows内核2

IRP(I/O Request Package)

IRP是什么

从概念上讲IRP类似于windows应用程序的消息。我们知道在windows中应用程序是由消息驱动的。

IRP的全名是I/O Request Package,即输入输出请求包,它是Windows内核中的一种非常重要的数据结构。上层应用程序与底层驱动程序通信时,应用程序会发出I/O请求,操作系统将相应的I/O请求转换成相应的IRP,不同的IRP会根据类型被分派到不同的派遣例程中进行处理。

与IRP相关的几个常用的事件

驱动层 功能解释 对应于应用层
IRP_MJ_CREATE 请求一个句柄 CreateFile
IRP_MJ_CLOSE 关闭句柄 CloseHandle
IRP_MJ_READ 从设备得到数据 ReadFile
IRP_MJ_WRITE 传送数据到设备 WriteFile
IRP_MJ_DEVICE_CONTROL 控制操作利用IOCTL宏 DeviceIoControl

这几个IRP事件放在MajorFunction里,MajorFuncton是驱动对象(PDRIVER_OBJECT DriverObject)的一个数组形式的成员

1720595022058

查看IRP_MJ_MAXIMUM_FUNCTION可以找到整个IRP事件序列

1720595035575

一般写驱动文件=> 打开 关闭 读取 写入 控制 分别对应

IRP_MJ_CREATE 打开
IRP_MJ_CLOSE 关闭
IRP_MJ_READ 读取 //可选
IRP_MJ_WRITE 写入//可选
IRP_MJ_DEVICE_CONTROL 控制(可以取代读写)

在 Windows 驱动程序开发中,IRP(I/O Request Packet,输入/输出请求数据包)是用于描述和传递 I/O 请求的重要数据结构。在处理 IRP 时,可以直接从 IRP 结构本身获取一些信息,而另一些信息则需要从 IRP 堆栈(IRP stack)中获取。以下是常见的从 IRP 和 IRP 堆栈中获取的信息:

直接从 IRP 中获取的信息:

  1. IRP 类型(IRP Type):指示 IRP 的类型,例如 IRP_MJ_READ、IRP_MJ_WRITE 等。
  2. IRP 标志(IRP Flags):包含了关于 IRP 状态和属性的标志信息,例如是否为同步或异步操作。
  3. 请求标识(RequestorMode):指示 IRP 是由用户模式发起的还是内核模式发起的请求。
  4. I/O 栈大小(StackCount):IRP 堆栈中的堆栈大小,通常用于多层驱动栈的处理。
  5. I/O 栈指针(Tail.Overlay.CurrentStackLocation):指向当前处理的 IRP 堆栈位置的指针。

从 IRP 堆栈中获取的信息:

  1. MajorFunction:指示当前 IRP 堆栈帧的主要功能码,例如 IRP_MJ_READ、IRP_MJ_WRITE 等。
  2. Parameters:包含了 IRP 参数的结构体,具体取决于 IRP 的主要功能码。例如,IRP_MJ_READ 对应的是 READ 操作的参数,IRP_MJ_WRITE 对应的是 WRITE 操作的参数。
  3. FileObject:表示与 IRP 关联的文件对象,如果适用的话。
  4. 设备对象(DeviceObject):指示 IRP 正在传输的设备对象。
  5. 传输的数据(Buffer、MdlAddress 等):如果 IRP 包含数据传输(如读写操作),可以从相应的字段获取数据的缓冲区地址或者 MDL(Memory Descriptor List)地址。
  6. 状态信息(IoStatus):用于指示 I/O 操作的状态和结果,如成功或失败的状态码。

常见的内核API前缀及其功能

  1. Zw / Nt
    • 用途:用户模式和内核模式下的系统服务调用。于Ring3 API同功能的函数
  2. Io
    • 用途:I/O管理器函数,处理设备I/O请求。
  3. Ke
    • 用途:内核调度器和同步函数。
  4. Mm
    • 用途:内存管理函数。
  5. Ps
    • 用途:进程和线程管理函数。
  6. Ob
    • 用途:对象管理器函数。
  7. Ex
    • 用途:执行器(executive)函数,提供通用的系统服务,如内存分配、同步等。
  8. Rtl
    • 用途:运行时库函数,提供字符串操作、内存管理等功能。和Ring3的C库不一样,但是Ring3的API还是能用,Ring0的这套库更安全
  9. Cm
    • 用途:配置管理器函数,处理注册表操作。
  10. Po
    • 用途:电源管理函数。
  11. Hal
    • 用途:硬件抽象层函数。
  12. FsRtl
    • 用途:文件系统运行时库函数。

全新字符串格式

微软设计UNICODE_STRINGANSI_STRING结构体,而不是直接使用原始的char*wchar_t*指针,是为了实现更高的安全性、灵活性和一致性。

原因:

安全性

长度信息UNICODE_STRINGANSI_STRING结构体包含了字符串的长度信息。这使得操作这些字符串时能够避免缓冲区溢出等安全问题。直接使用char*wchar_t*可能无法可靠地知道字符串的长度,容易引发缓冲区溢出等问题。

1
2
3
4
5
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;

灵活性

动态分配和管理:使用结构体可以更方便地管理字符串的动态分配和释放。可以预先分配一个更大的缓冲区并在需要时调整长度,而无需频繁分配和释放内存。

1
2
UNICODE_STRING ustr;
RtlInitUnicodeString(&ustr, L"Example");

一致性

一致的API设计

通过定义结构体,微软可以设计一组一致的API来操作字符串。例如,

1
RtlCopyUnicodeString

函数可以统一处理

1
UNICODE_STRING

结构,而不需要关心底层的字符指针操作。

1
2
3
4
NTSTATUS RtlCopyUnicodeString(
PUNICODE_STRING DestinationString,
PCUNICODE_STRING SourceString
);

跨平台支持

平台无关:结构体可以更容易地在不同平台和架构之间保持一致。直接使用char*wchar_t*在不同平台可能有不同的实现和行为,使用结构体可以抽象出这些差异。

高效处理

优化和高效处理:通过结构体,操作系统可以更高效地处理字符串操作。例如,

1
RtlUpcaseUnicodeString

函数可以直接在结构体上操作,而不需要逐字符转换。

1
2
3
4
5
NTSTATUS RtlUpcaseUnicodeString(
PUNICODE_STRING DestinationString,
PCUNICODE_STRING SourceString,
BOOLEAN AllocateDestinationString
);

兼容性

兼容性UNICODE_STRINGANSI_STRING提供了一个明确的机制来处理不同字符编码(宽字符和窄字符)。这对于支持国际化和不同语言环境非常重要。

注册IRP

展示了一个典型的驱动程序调度例程,它通过检查IRP的Major Function来处理不同的I/O请求:

当然以下代码只是注册了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
#include <ntifs.h>
NTSTATUS IRP_CALL(PDEVICE_OBJECT device, PIRP pirp)
{
UNREFERENCED_PARAMETER(device);
UNREFERENCED_PARAMETER(pirp);

KdPrint(("zdsoft:进入派遣函数"));
PIO_STACK_LOCATION irpStackL;

irpStackL = IoGetCurrentIrpStackLocation(pirp);//获取应用层传来的参数

switch (irpStackL->MajorFunction)
{
case IRP_MJ_DEVICE_CONTROL://DeviceIoControl
{
KdPrint(("zdsoft:用户层调用了DeviceIoControl"));
break;
}
case IRP_MJ_CREATE://CreateFile
{
KdPrint(("zdsoft:用户层调用了CreateFile"));
break;
}
case IRP_MJ_CLOSE://CloseHandle
{
KdPrint(("zdsoft:用户层调用了CloseHandle"));
break;
}
}
pirp->IoStatus.Status = STATUS_SUCCESS;
pirp->IoStatus.Information = 4;//返回给DeviceIoControl中的倒数第二个参数lpBytesReturned
IoCompleteRequest(pirp, IO_NO_INCREMENT);//调用方已完成所有I/O请求处理操作,并且不增加优先级
KdPrint(("zdsoft:离开派遣函数"));
return STATUS_SUCCESS;//返回0,表示成功
}

//卸载驱动时被调用
void DriverUnload(PDRIVER_OBJECT DriverObject)
{
KdPrint(("zdsoft:进入卸载例程DriverObject=%p 行号=%d", DriverObject, __LINE__));
}

//加载驱动时被调用
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
DriverObject->DriverUnload = DriverUnload;//注册卸载例程-回调函数
DriverObject->MajorFunction[IRP_MJ_CREATE] = IRP_CALL;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = IRP_CALL;

KdPrint(("zdsoft:进入DriverEntry入口点 DriverObject=%p 行号=%d\n", DriverObject, __LINE__));//Debug
//KdPrint(("zdsoft:RegistryPath=%s\n, RegistryPath->Buffer"));//多字节字符集
KdPrint(("zdsoft:RegistryPath=%ws\n", RegistryPath->Buffer));//Unicode 宽字符
return 0;//返回成功
}

代码分析:

1
2
3
4
5
pirp->IoStatus.Status = STATUS_SUCCESS;
pirp->IoStatus.Information = 4;
IoCompleteRequest(pirp, IO_NO_INCREMENT);
KdPrint(("zdsoft:离开派遣函数"));
return STATUS_SUCCESS;

这几行代码是在处理完I/O请求后,设置IRP的状态并完成该请求。

pirp->IoStatus.Status = STATUS_SUCCESS;
设置IRP的状态为成功。IoStatus.Status是一个NTSTATUS类型的字段,用于指示I/O操作的结果。将其设置为STATUS_SUCCESS表示操作已成功完成。

pirp->IoStatus.Information = 4;

设置IRP的附加信息。IoStatus.Information字段用于传递与I/O操作相关的额外信息。在这个例子中,设置为4,这通常是与特定操作相关的值,例如返回给DeviceIoControllpBytesReturned参数。lpBytesReturned表示实际传输的字节数,设置这个字段可以告诉调用方操作的结果细节。

IoCompleteRequest(pirp, IO_NO_INCREMENT);

完成IRP请求。IoCompleteRequest函数通知I/O管理器该IRP已经处理完毕,可以继续处理下一个I/O操作。

第二个参数IO_NO_INCREMENT用于控制完成IRP时的优先级提升。IO_NO_INCREMENT表示不提升线程的优先级。其他可能的值有IO_DISK_INCREMENTIO_CD_ROM_INCREMENT,这些值会在完成I/O操作时暂时提升线程优先级,以便更快地处理后续操作。

KdPrint(("zdsoft:离开派遣函数"));

在大多数情况下,使用KdPrintDbgPrint没有太大区别。Kd是一个宏定义,在Debug模式下,会自动替换为DbgPrint,在不是Debug的情况下,无作用

return STATUS_SUCCESS;

返回STATUS_SUCCESS,表示调度例程成功执行。这个返回值是给操作系统使用的,表示该I/O请求已成功处理。

NTSTATUS是Windows操作系统中用来表示函数返回状态的类型。它是一个32位的整数,用于标识函数调用的结果,包含了操作成功、错误代码、警告等不同的状态信息。

1720596722417

我们发现无论是加载或是卸载都没有触发IRP事件,这是因为在用户层(也就是R3环 )没有调用CreateFile打开设备也没有绑定符号。这个放在后面讲,现在只讲如何注册IRP。

实现交互

创建设备

IoCreateDevice 是一个内核模式 API,用于在驱动程序中创建设备对象。设备对象表示驱动程序控制的逻辑或物理设备。创建设备对象后,驱动程序可以处理 I/O 请求,并将这些请求传递给设备对象。

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

参数解释

  • DriverObject:指向驱动程序对象的指针,由操作系统在调用 DriverEntry 时提供。
  • DeviceExtensionSize:指定设备扩展的大小。设备扩展是驱动程序分配的一块内存,用于存储设备相关的上下文信息。
  • DeviceName:指向设备名称的指针。如果设备名不需要,则此参数可以为 NULL。填了NULL,系统会随机命名 手动命名要满足 \Device\MyDevice ( L”\Device\MyDevice” )
  • DeviceType:设备类型,可以是标准设备类型之一,例如 FILE_DEVICE_DISKFILE_DEVICE_KEYBOARD 等。

也可以填不知道

1
#define FILE_DEVICE_UNKNOWN             0x00000022
  • DeviceCharacteristics:设备特征标志,可以是 0 或由系统定义的一组特征标志的 OR 值。例如 FILE_DEVICE_SECURE_OPEN
  • Exclusive:指示设备是否为独占设备。TRUE 表示独占,FALSE 表示非独占。独占的话,一次只能一个人操作。
  • DeviceObject:指向 PDEVICE_OBJECT 类型的指针,用于返回新创建的设备对象。

创建设备的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NTSTATUS status;
PDEVICE_OBJECT DeviceObject = NULL;
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDevice");
status = IoCreateDevice(
DriverObject, // 驱动程序对象
0, // 设备扩展大小
&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"));

运行一下发现确实安装设备成功了

1720610942796

但是再安装,再启动就失败了。这是为什么?

之前提到过,驱动是不会自己释放资源的,设备名字已经有了,就不能再有重复的了。所以我们需要删除设备对象,所以我们在Unload函数卸载掉这个设备对象

1
2
3
4
5
6
7
8
9
10
//卸载驱动时被调用
void DriverUnload(PDRIVER_OBJECT DriverObject)
{
KdPrint(("zdsoft:进入卸载例程DriverObject=%p 行号=%d", DriverObject, __LINE__));
if (DriverObject->DeviceObject != NULL)
{
IoDeleteDevice(DriverObject->DeviceObject);
}

}

驱动设备与符号链接

在Windows驱动程序开发中,驱动设备与符号链接之间的关系是关键概念。符号链接(symbolic link)用于将用户模式下的设备名称与内核模式下的设备对象联系起来,使应用程序能够通过简单的设备名称访问设备对象。 也就是说,如果这个设备允许三环使用,那么就是用的是符号链接,不允许三环使用,就不要用符号链接

驱动设备不是驱动对象,而是驱动对象创建的,初学者经常把这两个在概念上搞混。那么符号链接又是什么?为什么会有符号链接?这个要从Windows下的设备命名说起,Windows下的设备命名是这样的:“\Device\设备名”,这个有点怪前面Device是固定的-常量,后面设备名是自己起的-变量。如果没有给设备起名字,I/O管理器也会随机分配一段数字为设备名,例如:“\Device\000001”

(以前有叫狗娃,狗蛋的,现在文明些了有叫果果,糖糖)”。但是这样的名字上不了台面,只能在家里用,家里就相当于内核驱动层,等长大一些了要上学了就要有个学名,学校的同学老师就相当于用户应用层,如果哪个同学叫你狗娃你肯定会不高兴了,所以我们在写驱动的时候最好创建一个符号链接,这是一个好习惯。

创建符号链接的示例:

1
2
3
4
5
6
7
8
9
UNICODE_STRING SymbolicLinkName;
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\MySymbolicLink");
status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
if (!NT_SUCCESS(status))
{
IoDeleteDevice(DeviceObject);
KdPrint(("Failed to create symbolic link: %X\n", status));
return status;
}

然后Unload函数在卸载的时候,也要删除符号链接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING SymbolicLinkName;
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\MySymbolicLink");

IoDeleteSymbolicLink(&SymbolicLinkName);//先删掉符号链接

if (DriverObject->DeviceObject != NULL)
{
IoDeleteDevice(DriverObject->DeviceObject);//再删除设备
}

KdPrint(("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
#include "header.h"
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING SymbolicLinkName;
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\MySymbolicLink");

IoDeleteSymbolicLink(&SymbolicLinkName);

if (DriverObject->DeviceObject != NULL)
{
IoDeleteDevice(DriverObject->DeviceObject);
}

KdPrint(("Driver Unloaded\n"));
}

extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);

NTSTATUS status;
PDEVICE_OBJECT DeviceObject = NULL;
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDevice");

status = IoCreateDevice(
DriverObject, // 驱动程序对象
0, // 设备扩展大小
&DeviceName, // 设备名称
FILE_DEVICE_UNKNOWN, // 设备类型
0, // 设备特征
FALSE, // 非独占设备
&DeviceObject // 返回的设备对象指针
);

if (!NT_SUCCESS(status))
{
KdPrint(("Failed to create device: %X\n", status));
return status;
}

UNICODE_STRING SymbolicLinkName;
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\MySymbolicLink");

status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);

if (!NT_SUCCESS(status))
{
IoDeleteDevice(DeviceObject);
KdPrint(("Failed to create symbolic link: %X\n", status));
return status;
}

// 注册卸载例程
DriverObject->DriverUnload = DriverUnload;

// 注册调度例程
DriverObject->MajorFunction[IRP_MJ_CREATE] = IRP_CALL;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = IRP_CALL;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IRP_CALL;

KdPrint(("Driver Loaded\n"));

return STATUS_SUCCESS;
}

NTSTATUS IRP_CALL(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);

PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);

switch (irpStack->MajorFunction)
{
case IRP_MJ_CREATE:
KdPrint(("IRP_MJ_CREATE\n"));
break;
case IRP_MJ_CLOSE:
KdPrint(("IRP_MJ_CLOSE\n"));
break;
case IRP_MJ_DEVICE_CONTROL:
KdPrint(("IRP_MJ_DEVICE_CONTROL\n"));
break;
default:
KdPrint(("Unknown IRP Major Function\n"));
break;
}

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);

return STATUS_SUCCESS;
}

安装启动成功以后:

1720620592317

用WinObj查看GLOBAL就会显示出我们自己创建的符号链接,还可以看到和哪个设备绑定

所以我们平时在Ring3操作的C盘,D盘,其实就是箭号指向的符号链接

1720620718730

实现Ring0与Ring3的交互

如何在Visual Studio编译出能在XP上跑的代码?

看这个博客即可

VS2019怎样编译出可以在WinXP上运行的exe?_vs2019 xp-CSDN博客

我们在3环写程序:

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
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
HANDLE hFile = CreateFileA(
"\\\\.\\MySymbolicLink",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
);

char szBuffer[20];
DWORD dwBytes;
DWORD flag;
flag=ReadFile(hFile, szBuffer, sizeof(szBuffer), &dwBytes, NULL);
if (!flag)
{
cout << "Read error!" << endl;
}
flag=WriteFile(hFile, szBuffer, sizeof(szBuffer), &dwBytes, NULL);
if (!flag)
{
cout << "Write error!" << endl;
}
CloseHandle(hFile);

system("pasue");

return 0;

}

发现触发了驱动的回调函数,这就实现了Ring3与Ring0的交互

1720624511416

换成32位Win7

折腾了一下,发现64位驱动只能在64位系统下使用,32位驱动只能在32位系统下使用

XP系统还是太老了,于是决定换Win7系统,然后写一个MFC程序

1720667595476

Load和Unload的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void CMFCRing3Dlg::OnBnClickedLoad()
{
// TODO: 在此添加控件通知处理程序代码
hDeviceFile = CreateFile( //驱动层会产生一个IRP_MJ_CREATE事件
"\\\\.\\MySymbolicLink",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
);
return;
}
void CMFCRing3Dlg::OnBnClickedUnload()
{
// TODO: 在此添加控件通知处理程序代码
CloseHandle(hDeviceFile); //驱动层会产生一个IRP_MJ_CLOSE事件
return;
}

接下来是驱动的代码

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

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING SymbolicLinkName;
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\KR_2024_07_11_Device_SymbolLink");

// 先删掉符号链接
NTSTATUS status = IoDeleteSymbolicLink(&SymbolicLinkName);
if (!NT_SUCCESS(status)) {
KdPrint(("Failed to delete symbolic link! Status: 0x%X\n", status));
}

// 再删除设备
PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject;
while (deviceObject != NULL)
{
PDEVICE_OBJECT nextDeviceObject = deviceObject->NextDevice;
IoDeleteDevice(deviceObject);
deviceObject = nextDeviceObject;
}

KdPrint(("Driver Unloaded\n"));
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
// 先创建设备,然后创建符号链接
UNREFERENCED_PARAMETER(RegisterPath);
KdPrint(("qidong!"));
NTSTATUS status;
PDEVICE_OBJECT DeviceObject = NULL;
UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, L"\\Device\\KR_2024_07_11_Device");

// 创建设备
status = IoCreateDevice(
DriverObject, // 驱动对象
0, // 设备扩展大小
&DeviceName, // 设备名称
FILE_DEVICE_UNKNOWN,// 设备类型
0, // 设备特征
FALSE, // 非独占设备
&DeviceObject // 返回设备对象指针
);

if (!NT_SUCCESS(status))
{
KdPrint(("Failed to create device! Status: 0x%X\n", status));
return status;
}

// 创建符号链接
UNICODE_STRING SymbolicLinkName;
RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\KR_2024_07_11_Device_SymbolLink");
status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName);
if (!NT_SUCCESS(status))
{
IoDeleteDevice(DeviceObject);
KdPrint(("Failed to create symbolic link! Status: 0x%X\n", status));
return status;
}

// 注册卸载例程
DriverObject->DriverUnload = DriverUnload;

// 注册调度例程
DriverObject->MajorFunction[IRP_MJ_CREATE] = IRP_CALL;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = IRP_CALL;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IRP_CALL;
DriverObject->MajorFunction[IRP_MJ_READ] = IRP_CALL;
DriverObject->MajorFunction[IRP_MJ_WRITE] = IRP_CALL;

KdPrint(("Driver Loaded\n"));
return STATUS_SUCCESS;
}

NTSTATUS IRP_CALL(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);

switch (irpStack->MajorFunction)
{
case IRP_MJ_CREATE:
KdPrint(("IRP_MJ_CREATE\n"));
break;
case IRP_MJ_CLOSE:
KdPrint(("IRP_MJ_CLOSE\n"));
break;
case IRP_MJ_DEVICE_CONTROL:
KdPrint(("IRP_MJ_DEVICE_CONTROL\n"));
break;
case IRP_MJ_READ:
KdPrint(("IRP_MJ_READ\n"));
break;
case IRP_MJ_WRITE:
KdPrint(("IRP_MJ_WRITE\n"));
break;
default:
KdPrint(("Unknown IRP Major Function\n"));
break;
}

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

效果如图:

1720683440493