0%

驱动-2_内核编程基础

内核API参考文档

https://learn.microsoft.com/zh-cn/windows-hardware/drivers/ddi/_kernel/

基本数据类型

在内核编程时遵守WDK编码习惯

1
2
3
4
ULONG(unsigned long)		PULONG(unsigned long*)
UCHAR(unsigned char) PUCHAR(unsigned char*)
UINT(unsigned int) PUINT(unsigned int*)
VOID(void) PVOID(void*)

返回值

大部分内核函数的返回值都是 NTSTATUS类型,它本质是一个宏,里面包含的类型有很多,如下三个就是常见的返回值:

宏名称 实际值 含义
STATUS_SUCCESS 0x00000000 成功
STATUS_INVALID_PARAMETER 0xC000000D 参数无效
STATUS_BUFFER_OVERFLOW 0x80000005 缓冲区长度不够

当调用内核函数失败时 即返回结构不是STATUS_SUCCESS 那么可以根据返回值在 ntstatus.h 头文件中去查找 也可在微软的SDK文档搜索相关宏名称

img

内核中的异常处理

在内核中一个小小的错误就可能导致蓝屏,例如我们去读写一个无效的内存地址。为了让自己的内核程序更加 健壮,在编写内核程序时要使用到异常处理。

在Windows下提供了结构化异常处理机制,编译器普遍都支持,如下

1
2
3
4
5
6
__try
{
// 填入可能要出错的代码 }
__except (filter_value)
{
// 填入出错后要执行的代码 }

如上示例中的filter_value,就是当内核程序出现异常时决定程序如何执行的,一般有这三种情况:

宏名称 实际值 含义
EXCEPTION_EXECUTE_HANDLER 1 进入except代码块执行
EXCEPTION_CONTINUE_SEARCH 0 不处理异常,由上一层调用函数处理
EXCEPTION_CONTINUE_EXECUTION -1 继续执行错误处的代码

常用的内核内存函数

对内存的使用主要是 申请、设置、拷贝、释放 常用函数如下集合应用层与内核层对比

应用层 内核
malloc ExAllocatePool
memset RtlFillMemory
memcpy RtlMoveMemory
free ExFreePool
使用内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//分配
PVOID ExAllocatePoolWithTag(
POOL_TYPE PoolType, //申请的内存类型 常用值:NonPagedPool表示可执行的非分页内存,PagedPool分页内存,NonPagedPoolNx不可执行的非分页内存 Nx不可执行
SIZE_T NumberOfBytes,//申请的内存大小,单位字节
ULONG Tag//4字节的标志,用于调试,不关心传递0或者使用ExAllocatePool
);
//分配成功返回内存首地址,失败返回NULL

//释放
VOID ExFreePoolWithTag (
PVOID P,//内存块地址
ULONG Tag//申请时的标记
);
//ExAllocatePool对应释放函数ExFreePool

内核中的字符串

内核字符串种类

CHAR(char)、WCHAR(wchar_t)、ANSI_STRING、UNICODE_STRING

CHAR是char在内核中的写法,WCHAR是wchar_t是在内核中的写法,但是一般不建议使用这两种 而使用后两者 ANSI_STRINGUNICODE_STRING(Ascii/Unicode)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct _STRING {
USHORT Length;
USHORT MaximumLength;
#ifdef MIDL_PASS
[size_is(MaximumLength), length_is(Length) ]
#endif // MIDL_PASS
Field_size_bytes_part_opt_(MaximumLength, Length) PCHAR Buffer;
} STRING;
typedef STRING ANSI_STRING
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
#ifdef MIDL_PASS
[size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
#else // MIDL_PASS
_Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;
内核字符串函数

字符串的操作就是创建、复制、比较、转换,但由于编码问题在内核中也有不同的函数表达:

ANSI_STRING字符串 UNICODE_STRING字符串 含义
RtlInitAnsiString RtlInitUnicodeString 创建字符串
RtlCopyString RtlCopyUnicodeString 字符串复制
RtlCompareString RtlCompareUnicoodeString 字符串比较
RtlAnsiStringToUnicodeString RtlUnicodeStringToAnsiString 编码转换
内核字符串操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//UNICODE_STRING初始化
UNICODE_STRING Demostring = { 0 };
RtlInitUnicodeString(&Demostring, L"This is a ....\n");
DbgPrint("%wZ", &Demostring);
//浅拷贝(只拷贝地址 将地址指向同一地方) 将buffer指向字符串的地址
UNICODE_STRING naizistring = { 0 };
WCHAR str[] = L"hacker...\n";
RtlInitUnicodeString(&naizistring, str);
str[0] = 0;
DbgPrint("%wZ", &naizistring);
//深拷贝(copy)
//#include "Ntstrfase.h"
WCHAR str[128] = { 0 };
UNICODE_STRING naizistring = { 0 };
RtlInitEmptyUnicodeString(&naizistring, str, sizeof(str));
RtlUnicodeStringCopyString(&naizistring, L"naizi hacker");
DbgPrint("%wZ", &naizistring);

内核空间与内存模块

进程中有一个4GB大小的内存空间 低2G是程序自己的 高2G是共享的

img

按照规定格式编写的驱动 每一个都可以当做一个模块,也可以称之为内核模块,都遵循PE结构,并可以加载到内核中

在编写时 一般都需要一个入口函数和一个卸载函数

在入口函数中需要两个参数 PDRIVER_OBJECTPUNICODE_STRING

1
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING   RegistryPath)
img
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct _DRIVER_OBJECT {
CSHORT Type;
CSHORT Size;
PDEVICE_OBJECT DeviceObject;
ULONG Flags;
PVOID DriverStart; // 结构体对应的驱动程序在内核空间的位置
ULONG DriverSize; // 结构体对应的驱动程序的大小
PVOID DriverSection; // 指针,指向_LDR_DATA_TABLE_ENTRY结构体 PDRIVER_EXTENSION DriverExtension;
UNICODE_STRING DriverName; // 结构体对应的驱动程序的名字 PUNICODE_STRING HardwareDatabase;
PFAST_IO_DISPATCH FastIoDispatch;
PDRIVER_INITIALIZE DriverInit;
PDRIVER_STARTIO DriverStartIo;
PDRIVER_UNLOAD DriverUnload; // 定义驱动程序卸载函数的地址 PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT;
typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;

通过之前的demoDriver 可以找到驱动对象地址

img

通过windbg进入到其中查看

1
dt _DRIVER_OBJECT 驱动对象地址 
img

DriverSection; // 指针 指向_LDR_DATA_TABLE_ENTRY结构体 ,在该结构体内有一个成员InLoadOrderLinks是双链表,它记录着前一个和后一个内核模块的_LDR_DATA_TABLE_ENTRY结构体地址:

1
dt _LDR_DATA_TABLE_ENTRY  0xffffc90e4c1f98d0
img
1
dt _LDR_DATA_TABLE_ENTRY 0xffffc90e4cc04a10
img

应用与内核间通信

创建设备对象

正常情况下,一个设备对象是对应一个设备的,如:鼠标、键盘。但是设备对象也可以是一个抽象的概念,不 对应到具体某个硬件,也就是我们可以使用如下代码去创建一个设备对象。

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
NTSTATUS Status;	//初始化返回状态
UNICODE_STRING DeviceName; // 创建设备名称
UNICODE_STRING SymbolName; //符号链接
PDEVICE_OBJECT dev = NULL;//初始化 控制设备
RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDriverDemo"); // 设备名称 PDEVICE_OBJECT pDeviceObj = NULL;

// 创建设备对象
Status = IoCreateDevice(
DriverObject, // 调用方驱动程序对象
0,
&DeviceName, // 设备名称
FILE_DEVICE_UNKNOWN, // 设备类型,当前不与某个具体设备挂钩,所以类型为UNKNOWN
0,//FILE_DEVICE_SECURE_OPEN, // 设备属性,大多数驱动程序仅指定 FILE_DEVICE_SECURE_OPEN 属性, 这可确保将相同的安全设置应用到设备的命名空间中的任何打开的请求
TRUE,//FALSE, // 设备对象是否表示独占设备,如果启用了对设备的独占访问,则一次只能打开设备的一个句柄
&dev // 创建的设备对象,指向接收指向新创建的 DEVICE_OBJECT 结构的指针的变量的指针
);

do {
if (!NT_SUCCESS(Status))//不成功
{
if (Status == STATUS_OBJECT_NAME_COLLISION)//名称冲突
{
DbgPrint("[*]设备名称冲突......\n");
}
DbgPrint("[*]创建失败");
break;
}

//初始化符号链接 (设备应用程序不可见 因此驱动要暴漏一个符号链接给应用层
RtlInitUnicodeString(&SymbolName, SYMBOLLINK);
Status = IoCreateSymbolicLink(&SymbolName, &DeviceName);
if (!NT_SUCCESS(Status)) { //不等于0
IoDeleteDevice(dev); //删除设备
DbgPrint("[*]删除设备成功");
break;
}
else {
DbgPrint("[*]创建符号链接成功");
}
} while (FALSE);

return Status;
数据交换配置
1
2
// 设置交换数据方式
pDeviceObj->Flags |= DO_BUFFERED_IO; // 缓冲区读写

创建好设备对象之后,就需要设置0环和3环交换数据的方式,有以下三种方式:

  • \1. 缓冲区读写(DO_BUFFERED_IO),操作系统将应用程序提供缓冲区的数据直接复制到内核模式下的地址中;

  • \2. 直接读写(DO_DIRECT_IO),操作系统会将用户模式下的缓冲区锁住,然后操作系统将这段缓冲区在 内核模式地址再次映射一遍,这样用户模式的缓冲区和内核模式的缓冲区指向的是同一区域的物理内 存,缺点就是要单独占用物理页面;

  • \3. 其他读写(0环直接读取3环线性地址),在调用IoCreateDevice函数创建设备后不设置交换数据模式即默认为其他方式读写,在使用 其他方式读写设备时,派遣函数直接读写应用程序提供的缓冲区地址。在驱动程序中,直接操作应用程 序的缓冲区地址是很危险的(不建议使用),只有驱动程序与应用程序运行在相同线程上下文的情况下,才能使用这种方式。

设备符号连接

设备名称是给0环使用的,如果3环想要访问到设备就需要通过符号连接,可以理解成它是一个设备的别名,如果不这样设置 3环无法访问到设备。

1
2
3
4
// 创建符号链接
UNICODE_STRING DeviceName;//设备名称
UNICODE_STRING SymbolName;//符号链接
RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDriverDemo");

注意:

设备名称的作用是给内核对象使用的,如果要在Ring3访问 必须有符号链接(可以理解为是设备的别名),没有这个在RIng3下不可见

内核模式下符号链接名是以\??\开头 如 C盘就是 \??\C:

用户模式下符号链接名则是以\\.\开头 如C盘就是 \\.\C:

在实际代码中需要对 \ 转义为 \\

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
L"\\??\\MyDriverDemo"   	\\??\MyDriverDemo
L"\\\\.\\MyDriverDemo" \\.\MyDriverDemo
PDEVICE_OBJECT dev = NULL;//初始化 控制设备

NTSTATUS CreateDevice(PDRIVER_OBJECT DriverObject) {

NTSTATUS Status; //初始化返回状态
UNICODE_STRING DeviceName; // 创建设备名称
UNICODE_STRING SymbolName; //符号链接
RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDriverDemo"); // 设备名称 PDEVICE_OBJECT pDeviceObj = NULL;

// 创建设备对象
Status = IoCreateDevice(
DriverObject, // 调用方驱动程序对象
0,
&DeviceName, // 设备名称
FILE_DEVICE_UNKNOWN, // 设备类型,当前不与某个具体设备挂钩,所以类型为UNKNOWN
0,//FILE_DEVICE_SECURE_OPEN, // 设备属性,大多数驱动程序仅指定 FILE_DEVICE_SECURE_OPEN 属性, 这可确保将相同的安全设置应用到设备的命名空间中的任何打开的请求
TRUE,//FALSE, // 设备对象是否表示独占设备,如果启用了对设备的独占访问,则一次只能打开设备的一个句柄
&dev // 创建的设备对象,指向接收指向新创建的 DEVICE_OBJECT 结构的指针的变量的指针
);

do {
if (!NT_SUCCESS(Status))//不成功
{
if (Status == STATUS_OBJECT_NAME_COLLISION)//名称冲突
{
DbgPrint("[*]设备名称冲突......\n");
}
DbgPrint("[*]创建失败");
break;
}
//初始化符号链接 (设备应用程序不可见 因此驱动要暴漏一个符号链接给应用层
RtlInitUnicodeString(&SymbolName, SYMBOLLINK);
Status = IoCreateSymbolicLink(&SymbolName, &DeviceName);
if (!NT_SUCCESS(Status)) { //不等于0
IoDeleteDevice(dev); //删除设备
DbgPrint("[*]删除设备成功");
break;
}
else {
DbgPrint("[+]创建符号链接成功");
}
} while (FALSE);

return Status;
}
派遣函数与IRP类型

当单击鼠标时会产生MSG消息,该消息发送给窗口对象,在窗口对象内会根据消息序号找到对应的处理函数进行处理。同理,在驱动程序中,当在3环使用了某个函数就会产生IRP消息,该消息发送给设备对象,在设备对象内会根据消息类型选择对应的派遣函数处理。

img

常见的IRP类型与其对应的3环下的函数及作用如下所示:

IRP类型 来源函数 函数作用
IRP_MJ_CREATE CreateFile 打开设备
IRP_MJ_READ ReadFile 从设备中读取数据
IRP_MJ_WRITE WriteFile 从设备中写入数据
IRP_MJ_CLOSE CloseHandle 关闭设备
IRP_MJ_DEVICE_CONTROL DeviceIoControl 设备控制,比读取、写入操作更加灵活
IRP_MJ_POWER X 在操作系统处理电源消息时产生该类型
IRP_MJ_SHUTDOWN X 关闭系统前会产生该类型

想注册某个IRP类型对应的派遣函数时候可以使用如下格式 :

1
DriverObject->MajorFunction[IRP类型] = 派遣函数名

//MajorFunction是一个具有28个成员的数组 对应着28种IRP类型

img
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
#define IRP_MJ_CREATE                   0x00
#define IRP_MJ_CREATE_NAMED_PIPE 0x01
#define IRP_MJ_CLOSE 0x02
#define IRP_MJ_READ 0x03
#define IRP_MJ_WRITE 0x04
#define IRP_MJ_QUERY_INFORMATION 0x05
#define IRP_MJ_SET_INFORMATION 0x06
#define IRP_MJ_QUERY_EA 0x07
#define IRP_MJ_SET_EA 0x08
#define IRP_MJ_FLUSH_BUFFERS 0x09
#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a
#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b
#define IRP_MJ_DIRECTORY_CONTROL 0x0c
#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d
#define IRP_MJ_DEVICE_CONTROL 0x0e
#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f
#define IRP_MJ_SHUTDOWN 0x10
#define IRP_MJ_LOCK_CONTROL 0x11
#define IRP_MJ_CLEANUP 0x12
#define IRP_MJ_CREATE_MAILSLOT 0x13
#define IRP_MJ_QUERY_SECURITY 0x14
#define IRP_MJ_SET_SECURITY 0x15
#define IRP_MJ_POWER 0x16
#define IRP_MJ_SYSTEM_CONTROL 0x17
#define IRP_MJ_DEVICE_CHANGE 0x18
#define IRP_MJ_QUERY_QUOTA 0x19
#define IRP_MJ_SET_QUOTA 0x1a
#define IRP_MJ_PNP 0x1b
#define IRP_MJ_PNP_POWER IRP_MJ_PNP // Obsolete....
#define IRP_MJ_MAXIMUM_FUNCTION 0x1b

派遣函数一般也是有固定格式

1
2
3
4
5
6
7
8
9
NTSTATUS MyDispatchFunction(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
// 业务代码
// 设置返回状态
pIrp->IoStatus.Status = STATUS_SUCCESS;
pIrp->IoStatus.Information = 0; // 返回给3环多少数据,没有则填0
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
1
2
3
4
5
// 这里为了方便所有的请求都用一个处理函数,在函数内部去区分请求,再做不同的逻辑处理
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = MyDispatch;
}
灵活通信
应用层

在应用层使用DeviceIoControl函数向驱动发送请求 DeviceIoControl会使内核中的设备对象收到一个设备控制请求

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

//利用CTL_CODE这个宏来生成一个字节设备控制请求功能号 0-7ff已被微软保留 只能用比这个大的
#define SENDSTR CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_WRITE_DATA)
#define RECVSTR CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_READ_DATA)
#define SEND_AND_RECV_STR CTL_CODE(FILE_DEVICE_UNKNOWN,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define STR_MAX_LEN 512 //定义链表缓冲区最大长度

//内存清零初始化函数
void initoutbuf(UCHAR buf[STR_MAX_LEN])
{
memset(buf, 0, sizeof(char) * STR_MAX_LEN);
return;
}

void main() {
//打开设备 L"\\??\\MyDriverDemo" \\.\MyDriverDemo
HANDLE device = CreateFileW(L"\\\\.\\MyDriverDemo", GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, NULL);

if (device == INVALID_HANDLE_VALUE) {
printf("[*]获取驱动句柄失败!错误:%d\n", GetLastError());
getchar();
return;
}

//char* msg = "[+]Ring3->Ring0";
UCHAR buffer[STR_MAX_LEN] ;//初始化返回数据的缓冲区
DWORD size = 0;

//给驱动发送请求 DeviceIoControl会使内核中的设备对象收到一个设备控制请求

BOOL bRet = DeviceIoControl(device, SEND_AND_RECV_STR, "Ring3->Ring0", strlen("Ring3->Ring0") + 1, (LPVOID)buffer, STR_MAX_LEN, &size, 0);
if (bRet) {
printf("[+]接收到R0发送的消息%s\r\n", buffer);

}

CloseHandle(device);
getchar();
return 0;
}
内核
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
#include "ntddk.h"

//定义控制码
#define SENDSTR CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_WRITE_DATA)
#define RECVSTR CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_READ_DATA)
#define SEND_AND_RECV_STR CTL_CODE(FILE_DEVICE_UNKNOWN,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS)
//定义符号链接名
#define SYMBOLLINK L"\\??\\MyDriverDemo"
#define STR_MAX_LEN 512 //定义链表缓冲区最大长度

PDEVICE_OBJECT dev = NULL;//初始化 控制设备

NTSTATUS CreateDevice(PDRIVER_OBJECT DriverObject) {
NTSTATUS Status;//返回状态
UNICODE_STRING DeviceName;//设备名称
UNICODE_STRING SymbolName;//符号链接
RtlInitUnicodeString(&DeviceName, L"\\Device\\MyDriverDemo");

Status = IoCreateDevice(
DriverObject,
0,
&DeviceName,
FILE_DEVICE_UNKNOWN,
0,
TRUE,
&dev
);

do {
if (!NT_SUCCESS(Status))//不成功
{
if (Status == STATUS_OBJECT_NAME_COLLISION)//名称冲突
{
DbgPrint("[*]设备名称冲突......\n");
}
DbgPrint("[*]创建失败");
break;
}

//初始化符号链接 (设备应用程序不可见 因此驱动要暴漏一个符号链接给应用层
RtlInitUnicodeString(&SymbolName, SYMBOLLINK);
Status = IoCreateSymbolicLink(&SymbolName, &DeviceName);
if (!NT_SUCCESS(Status)) { //不等于0
IoDeleteDevice(dev); //删除设备
DbgPrint("[+]删除设备成功");
break;
}
else {
DbgPrint("[+]创建符号链接成功");
}
} while (FALSE);

return Status;
}

//派遣函数
NTSTATUS MyDispatch(PDEVICE_OBJECT dev, PIRP pIrp) {
NTSTATUS Status = STATUS_SUCCESS;//返回状态
ULONG retLen = 0;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(pIrp); //获取IRP数据
ULONG uIoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode; //获取操作码

PVOID Outputbuffer = pIrp->AssociatedIrp.SystemBuffer;//获取缓冲区地址(输入输出的缓存区都是一个,是共享的)
PVOID Inputbuffer = pIrp->AssociatedIrp.SystemBuffer;
ULONG uDataInlen = IrpStack->Parameters.DeviceIoControl.InputBufferLength; //获取输入数据的长度
ULONG uDataOutlen = IrpStack->Parameters.DeviceIoControl.InputBufferLength; //获取输出数据的长度

if (IrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL){
switch (uIoControlCode)
{
//case SENDSTR:
//DbgPrint((char*)buffer);
//retLen = uDataInlen;
//break;
case SEND_AND_RECV_STR:
if (Inputbuffer != NULL && uDataInlen > 0)
{
DbgPrint("[+]接收到R3发送的消息:%s\r\n", Inputbuffer);
}
if (Outputbuffer != NULL && uDataInlen >= strlen("Ring0->Ring3") + 1) {
memcpy(Outputbuffer, "Ring0->Ring3", strlen("Ring0->Ring3") + 1);
Status = STATUS_SUCCESS;
retLen = strlen("Ring0->Ring3") + 1;
//DbgPrint("%s\r\n", Outputbuffer);
}
break;
default:
//到这里的请求都是不接受的请求 一律返回非法参数错误
Status = STATUS_INVALID_PARAMETER;
break;
}

}
//结束请求 用这个information记录返回
pIrp->IoStatus.Information = retLen;
pIrp->IoStatus.Status = Status;// Ring3 GetLastError()
IoCompleteRequest(pIrp, IO_NO_INCREMENT); //将Irp返回给IO管理器
return Status;//RIng3 DeviceIoControl()返回值
}

//卸载函数
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
if (DriverObject != NULL)
{
UNICODE_STRING SymbolName; //初始化符号链接
RtlInitUnicodeString(&SymbolName, SYMBOLLINK);
IoDeleteSymbolicLink(&SymbolName); //删除符号链接
if (dev != NULL) {
IoDeleteDevice(dev);
}
DbgPrint("[+]删除设备和符号链接成功...\n");
}
return STATUS_SUCCESS;
}


NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DbgPrint("[%ws]Hello Kernel World\n", __FUNCTIONW__);
if (RegistryPath != NULL)
{
DbgPrint("[%ws]所在注册表位置:%wZ\n", __FUNCTIONW__, RegistryPath);
}
if (DriverObject != NULL)
{
DbgPrint("[%ws]驱动对象地址:%p\n", __FUNCTIONW__, DriverObject);
//创建控制设备
CreateDevice(DriverObject);
//设置分发函数
/*DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyDispatch;
DriverObject->MajorFunction[IRP_MJ_CREATE] = CreateDispatchFunc;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = CloseDispatchFunc;*/
// 这里driver->MajorFunction是一个数组,不同的请求可以设置不同的处理函数
// 这里为了方便所有的请求都用一个处理函数,在函数内部去区分请求,再做不同的逻辑处理
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = MyDispatch;
}

DriverObject->DriverUnload = DriverUnload;
DbgPrint("[+]驱动加载成功");
}
return STATUS_SUCCESS;
}
img

欢迎关注我的其它发布渠道

------------- 💖 🌞 本 文 结 束 😚 感 谢 您 的 阅 读 🌞 💖 -------------