Windows内核

Windows 组件概述

调用例如CreateFile这个NTDll的API,这个API会进入0环,在0环里面找到相关的服务,将信息先传给IO Mgr,被打包为IRP,然后传递给驱动,等待驱动完成请求,处理完以后,才将结果返回给Ring3

1720751365586

WriteFile => ntdll.dll => kernel => I/O Manager => AllocIrp => Driver

一些介绍

IoCompleteRequest 是 Windows 内核模式驱动程序开发中的一个关键函数,用于完成 I/O 请求并通知 I/O 管理器和请求的发起者(通常是用户模式应用程序或其他内核模式组件)操作已经完成。这个函数会更新 IRP(I/O 请求包)的状态,并根据请求的优先级和完成状态触发相应的后续操作。

1
2
3
4
VOID IoCompleteRequest(
_Inout_ PIRP Irp,
_In_ CCHAR PriorityBoost
);

使用方法

IoCompleteRequest 通常在驱动程序完成对 I/O 请求的处理后调用。它通常在以下几种情况下使用:

  1. IRP 处理完成: 驱动程序完成对 IRP 的处理,例如读写操作已完成。
  2. 错误处理: 驱动程序在处理 IRP 时遇到错误,需要终止请求并返回错误状态。
  3. 中断处理: 驱动程序在处理中断请求时,完成对中断的处理并通知系统。

ProbeForWrite 是 Windows 内核模式下的一个函数,用于验证用户模式传递的缓冲区是否可以写入。它在处理来自用户模式的数据时非常重要,因为内核模式代码不能直接信任用户模式传递的指针。这是因为用户模式的应用程序可以传递一个无效或不合法的指针,导致内核模式代码在访问该指针时引发异常,甚至导致系统崩溃(蓝屏)。 (注意是用户模式传递的缓冲区!!!!)一般也就判断Irp->UserBuffer

1
2
3
4
5
VOID ProbeForWrite(
__inout PVOID Address,
__in SIZE_T Length,
__in ULONG Alignment
);

Address: 用户模式缓冲区的起始地址。

Length: 缓冲区的长度(字节数)。

Alignment: 缓冲区必须对齐的边界(以字节为单位),通常设置为 1 表示不需要特别的对齐。

MmIsAddressValid 是 Windows 驱动程序开发中常用的一个函数,用于检查指定的内存地址是否有效,即是否在当前进程的虚拟地址空间范围内。

1
2
3
BOOLEAN MmIsAddressValid(
PVOID VirtualAddress
);

参数

  • VirtualAddress:要检查的虚拟内存地址。

返回值

  • TRUE:如果指定的虚拟地址在当前进程的虚拟地址空间范围内且有效。
  • FALSE:如果指定的虚拟地址无效或不在当前进程的虚拟地址空间范围内。

Irp->IoStatus.Status = STATUS_SUCCESS;return STATUS_SUCCESS; 在驱动程序开发中有不同的作用和意义,尽管它们都使用了相同的状态代码 STATUS_SUCCESS。下面解释它们的区别和各自的作用:

Irp->IoStatus.Status = STATUS_SUCCESS;

这行代码设置了当前 I/O 请求包(IRP)的状态。IRP 是操作系统用来描述和跟踪 I/O 操作的结构。当驱动程序处理一个 I/O 请求时,它会设置 Irp->IoStatus.Status 字段来指示操作的结果。

  • 作用:

    • 通过设置 Irp->IoStatus.Status,驱动程序通知操作系统这个 I/O 请求的处理状态(成功或失败)。
    • 这个字段的值会在 IoCompleteRequest 调用之后返回给发起 I/O 请求的代码(通常是用户模式应用程序或内核模式组件)。

return STATUS_SUCCESS;

这行代码是函数返回值的一部分,用来指示函数执行的结果状态。它与设置 IRP 状态不同,因为它是函数本身的返回值。

R3往R0写入数据WriteFile

驱动的代码这样写:

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
// MyWrite
NTSTATUS MyWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
DeviceObject;
// PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);
VOID* lpBuf = Irp->UserBuffer; // Use SystemBuffer
KdPrint(("Write\n"));
KdPrint(("0x%p\n", &g_GlobalBuffer));
__try {
//DEVICE_EXTENSION* device_extension_ptr = (DEVICE_EXTENSION*)DeviceObject->DeviceExtension;
// Validate and probe user buffer
if (MmIsAddressValid(lpBuf)) {
ProbeForRead(lpBuf, sizeof(lpBuf), 1);
}
else {
KdPrint(("Invalid user buffer address\n"));
Irp->IoStatus.Status = STATUS_INVALID_USER_BUFFER;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_USER_BUFFER;
}
RtlCopyMemory(g_GlobalBuffer, lpBuf, wcslen((TCHAR*)lpBuf)*sizeof(TCHAR)+1);
KdPrint(("g_GlobalBuffer是 %s\n", g_GlobalBuffer));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = wcslen((TCHAR*)lpBuf)*sizeof(TCHAR);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
KdPrint(("Exception occurred: Access violation\n"));
// Set IRP status for exception
Irp->IoStatus.Status = GetExceptionCode();
Irp->IoStatus.Information = 0;
}
// Complete the request
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Irp->IoStatus.Status;
}

注意注意:

1
RtlCopyMemory(g_GlobalBuffer, lpBuf, wcslen((TCHAR*)lpBuf)*sizeof(TCHAR)+1);

这里要多一个字节,否则没有 ‘\x00’ 结尾容易出大问题

MFC代码:(和下面读出数据用的是同一个):

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
#define DEVICE_NAME L"\\\\.\\2024_07_12_link"
#define BUFFER_SIZE 256
void CReadWriteRing3MFCDlg::OnBnClickedButtonWrite()
{
UpdateData(TRUE);
HANDLE hDevice;
DWORD bytesReturned;
char writeBuffer[BUFFER_SIZE] = "Hello from user mode!";

hDevice = CreateFile(DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
CString errorMsg;
errorMsg.Format(L"Failed to open device. Error code: %d", GetLastError());
AfxMessageBox(errorMsg);
return;
}

if (!WriteFile(hDevice, Write_Text, Write_Text.GetLength(), &bytesReturned, NULL)) {
CString errorMsg;
errorMsg.Format(L"Failed to write to device. Error code: %d", GetLastError());
AfxMessageBox(errorMsg);
CloseHandle(hDevice);
return;
}

CString str;
str.Format(L"Wrote %d bytes to the device: %s\n", bytesReturned, Write_Text);
AfxMessageBox(str);

CloseHandle(hDevice);
}


void CReadWriteRing3MFCDlg::OnBnClickedButtonRead()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(FALSE);


HANDLE hDevice;
DWORD bytesReturned;


hDevice = CreateFile(DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE) {
CString errorMsg;
errorMsg.Format(L"Failed to open device. Error code: %d", GetLastError());
AfxMessageBox(errorMsg);
return;
}

if (!ReadFile(hDevice, Read_Text.GetBuffer(), 100, &bytesReturned, NULL)) {
CString errorMsg;
errorMsg.Format(L"Failed to write to device. Error code: %d", GetLastError());
AfxMessageBox(errorMsg);
CloseHandle(hDevice);
return;
}

CString str;
str.Format(L"read %d bytes to the device: %s\n", bytesReturned, Read_Text);
AfxMessageBox(str);

CloseHandle(hDevice);
return;


}

效果图:

1720871601336

R3从R0读出数据ReadFile

驱动这样写:

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 MyRead(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
DeviceObject;
KdPrint(("Read!!!\n"));
KdPrint(("全局变量有%d个字节",wcslen(g_GlobalBuffer) * sizeof(WCHAR)));

__try {
//DEVICE_EXTENSION* device_extension_ptr = (DEVICE_EXTENSION*)DeviceObject->DeviceExtension;

// Validate and probe user buffer
if (MmIsAddressValid(Irp->UserBuffer)) {
ProbeForRead(Irp->UserBuffer, Irp->IoStatus.Information, sizeof(WCHAR));
}
else {
KdPrint(("Invalid user buffer address\n"));
Irp->IoStatus.Status = STATUS_INVALID_USER_BUFFER;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_USER_BUFFER;
}

//NTSTATUS status;

// 获取字符串的长度

RtlCopyMemory(Irp->UserBuffer, g_GlobalBuffer, wcslen(g_GlobalBuffer)*sizeof(TCHAR)+1);
KdPrint(("Irp->AssociatedIrp.SystemBuffer %ls\n", (PTCHAR)Irp->UserBuffer));
KdPrint(("读到的字数是 %d", wcslen(g_GlobalBuffer)));

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = wcslen(g_GlobalBuffer) * sizeof(WCHAR);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
KdPrint(("Exception occurred: Access violation\n"));

// Set IRP status for exception
Irp->IoStatus.Status = GetExceptionCode();
Irp->IoStatus.Information = 0;
}

return STATUS_SUCCESS;

}

完整驱动程序代码:

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
#include "header.h"
#define BUFFER_SIZE 256
typedef struct _DEVICE_EXTENSION {
CHAR Buffer[BUFFER_SIZE];
ULONG BufferLength;
} DEVICE_EXTENSION, * PDEVICE_EXTENSION;
TCHAR g_GlobalBuffer[BUFFER_SIZE]={0}; // 全局缓冲区
// DriverEntry
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(RegistryPath);

DriverObject->DriverUnload = DriverUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] = MyCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MyClose;
DriverObject->MajorFunction[IRP_MJ_READ] = MyRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = MyWrite;
KdPrint(("Create!\n"));
// 创建设备对象
UNICODE_STRING deviceName = RTL_CONSTANT_STRING(L"\\Device\\MyDevice");
UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(L"\\??\\2024_07_12_link");
PDEVICE_OBJECT deviceObject = NULL;
NTSTATUS status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &deviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &deviceObject);
if (!NT_SUCCESS(status)) {
return status;
}
// 初始化设备扩展
PDEVICE_EXTENSION devExt = (PDEVICE_EXTENSION)deviceObject->DeviceExtension;
RtlZeroMemory(devExt, sizeof(DEVICE_EXTENSION));
status = IoCreateSymbolicLink(&symbolicLink, &deviceName);
if (!NT_SUCCESS(status)) {
IoDeleteDevice(deviceObject);
return status;
}
return STATUS_SUCCESS;
}
// DriverUnload
VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
DbgPrint("Unload\n");
UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(L"\\??\\2024_07_12_link");
IoDeleteSymbolicLink(&symbolicLink);
IoDeleteDevice(DriverObject->DeviceObject);
}
// MyCreate
NTSTATUS MyCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
DbgPrint("Create!\n");
UNREFERENCED_PARAMETER(DeviceObject);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// MyClose
NTSTATUS MyClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
DbgPrint("Close!\n");
UNREFERENCED_PARAMETER(DeviceObject);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// MyRead
NTSTATUS MyRead(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
DeviceObject;
KdPrint(("Read!!!\n"));
KdPrint(("全局变量有%d个字节",wcslen(g_GlobalBuffer) * sizeof(WCHAR)));

__try {
//DEVICE_EXTENSION* device_extension_ptr = (DEVICE_EXTENSION*)DeviceObject->DeviceExtension;

// Validate and probe user buffer
if (MmIsAddressValid(Irp->UserBuffer)) {
ProbeForRead(Irp->UserBuffer, Irp->IoStatus.Information, sizeof(WCHAR));
}
else {
KdPrint(("Invalid user buffer address\n"));
Irp->IoStatus.Status = STATUS_INVALID_USER_BUFFER;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_USER_BUFFER;
}
//NTSTATUS status;
// 获取字符串的长度
RtlCopyMemory(Irp->UserBuffer, g_GlobalBuffer, wcslen(g_GlobalBuffer)*sizeof(TCHAR)+1);
KdPrint(("Irp->AssociatedIrp.SystemBuffer %ls\n", (PTCHAR)Irp->UserBuffer));
KdPrint(("读到的字数是 %d", wcslen(g_GlobalBuffer)));

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = wcslen(g_GlobalBuffer) * sizeof(WCHAR);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
KdPrint(("Exception occurred: Access violation\n"));

// Set IRP status for exception
Irp->IoStatus.Status = GetExceptionCode();
Irp->IoStatus.Information = 0;
}
return STATUS_SUCCESS;
}
// MyWrite
NTSTATUS MyWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
DeviceObject;
// PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp);

VOID* lpBuf = Irp->UserBuffer; // Use SystemBuffer
KdPrint(("Write\n"));

KdPrint(("0x%p\n", &g_GlobalBuffer));
__try {
//DEVICE_EXTENSION* device_extension_ptr = (DEVICE_EXTENSION*)DeviceObject->DeviceExtension;

// Validate and probe user buffer
if (MmIsAddressValid(lpBuf)) {
ProbeForRead(lpBuf, sizeof(lpBuf), 1);
}
else {
KdPrint(("Invalid user buffer address\n"));
Irp->IoStatus.Status = STATUS_INVALID_USER_BUFFER;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_USER_BUFFER;
}
RtlCopyMemory(g_GlobalBuffer, lpBuf, wcslen((TCHAR*)lpBuf)*sizeof(TCHAR)+1);
KdPrint(("g_GlobalBuffer是 %s\n", g_GlobalBuffer));
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = wcslen((TCHAR*)lpBuf)*sizeof(TCHAR);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
KdPrint(("Exception occurred: Access violation\n"));

// Set IRP status for exception
Irp->IoStatus.Status = GetExceptionCode();
Irp->IoStatus.Information = 0;
}
// Complete the request
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Irp->IoStatus.Status;
}

效果图:

1720871629228

IRP_MJ_DEVICE_CONTROL_DeviceIoControl

例如要读一个设备(例如某个光驱)里面的文件,但是因为IRP_MJ_WRITE和IRP_MJ_READ这俩事件是没有传递参数的,这是一个痛点,没办法传递额外的参数

于是就有了去处理这个事件

1
#define IRP_MJ_DEVICE_CONTROL           0x0e

DeviceIoControl 是 Windows 平台上用于与驱动程序通信的重要函数之一,它允许用户应用程序与设备驱动程序进行数据交换和控制操作。以下是关于 DeviceIoControl 函数的详细介绍:

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

参数

  • hDevice:设备的句柄,通过 CreateFile 函数获取。
  • dwIoControlCode:指定设备控制码,用于标识要执行的操作。
  • lpInBuffer:输入数据的缓冲区指针,用于向设备传递数据。
  • nInBufferSize:输入数据缓冲区的大小,以字节为单位。
  • lpOutBuffer:输出数据的缓冲区指针,用于从设备获取数据。
  • nOutBufferSize:输出数据缓冲区的大小,以字节为单位。
  • lpBytesReturned:指向 DWORD 类型的变量的指针,用于接收实际传输的字节数。
  • lpOverlapped:指向 OVERLAPPED 结构的指针,支持异步 I/O 操作。可以传递 NULL 进行同步操作。

DeviceIoControl比WriteFile和ReadFile相比,WriteFile既能IN也能Out

DeviceIoControl 提供了对设备的多种控制功能,不仅限于数据的读写。可以通过控制码(IOCTL)执行特定的设备操作,如配置设备、查询状态、执行诊断命令等。

允许用户定义自定义控制码,使驱动程序能够支持特定的功能和操作,这些是 WriteFileReadFile 所无法实现的。

在驱动程序中,那么IRP_MJ_DEVICE_CONTROL 事件里面,Buffer有俩,Length也有俩,这就很尴尬了。

实际上,输入的Buffer还是Irp->UserBuffer

其他的参数在IRP堆栈里面拿:

1
2
3
4
5
PIO_STACK_LOCATION pIrpstack = IoGetCurrentIrpStackLocation(Irp);
PVOID lpInBuf = Irp->UserBuffer;
PVOID lpOutBuf = pIrpstack->Parameters.DeviceIoControl.Type3InputBuffer;
ULONG nInLength = pIrpstack->Parameters.DeviceIoControl.InputBufferLength;
ULONG nOutLength = pIrpstack->Parameters.DeviceIoControl.OutputBufferLength;

还有一个参数IoControlCode:指定设备控制码,用于标识要执行的操作。

框架:

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
#define IOCTL_CUSTOM_FUNC1 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CUSTOM_FUNC2 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_IN_DIRECT, FILE_ANY_ACCESS)

NTSTATUS MyDeviceControlRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PIO_STACK_LOCATION irpSp;
PVOID inputBuffer;
PVOID outputBuffer;
ULONG inputBufferLength;
ULONG outputBufferLength;
ULONG ioControlCode;
NTSTATUS ntStatus = STATUS_SUCCESS;

UNREFERENCED_PARAMETER(DeviceObject);

irpSp = IoGetCurrentIrpStackLocation(Irp);
inputBuffer = Irp->AssociatedIrp.SystemBuffer;
outputBuffer = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
inputBufferLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
outputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
ioControlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;

switch (ioControlCode) {
case IOCTL_CUSTOM_FUNC1:
// Handle custom function 1
// Example: Copy input data to output buffer
if (inputBuffer && outputBuffer && inputBufferLength == outputBufferLength) {
RtlCopyMemory(outputBuffer, inputBuffer, inputBufferLength);
Irp->IoStatus.Information = inputBufferLength;
} else {
ntStatus = STATUS_INVALID_PARAMETER;
}
break;

case IOCTL_CUSTOM_FUNC2:
// Handle custom function 2
// Example: Return success without doing anything
Irp->IoStatus.Information = 0;
break;

default:
// Unsupported control code
ntStatus = STATUS_INVALID_DEVICE_REQUEST;
break;
}

Irp->IoStatus.Status = ntStatus;
IoCompleteRequest(Irp, IO_NO_INCREMENT);

return ntStatus;
}

CTL_CODE: CTL_CODE 是一个宏,用于定义 Windows 设备控制码(IOCTL)

参数说明

  • DeviceType:设备类型,通常使用 FILE_DEVICE_* 类型定义,如 FILE_DEVICE_UNKNOWNFILE_DEVICE_KEYBOARD 等。
  • Function:功能码,用于标识具体的控制操作,应该是一个唯一的值,用于识别设备支持的不同功能。
  • Method:传输方法,指定数据传输的方式,可以是 METHOD_BUFFEREDMETHOD_IN_DIRECTMETHOD_OUT_DIRECTMETHOD_NEITHER 中的一个。(这个很重要,涉及到参数去哪里拿)
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
switch (METHOD_FROM_CTL_CODE(ioControlCode)) {
case METHOD_BUFFERED:
inputBuffer = Irp->AssociatedIrp.SystemBuffer;
outputBuffer = Irp->AssociatedIrp.SystemBuffer;
break;

case METHOD_IN_DIRECT:
inputBuffer = Irp->AssociatedIrp.SystemBuffer;
if (Irp->MdlAddress) {
outputBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
}
break;

case METHOD_OUT_DIRECT:
inputBuffer = Irp->AssociatedIrp.SystemBuffer;
if (Irp->MdlAddress) {
outputBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
}
break;

case METHOD_NEITHER:
inputBuffer = irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
outputBuffer = Irp->UserBuffer;
break;

default:
status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
  • Access:访问权限,指定谁可以访问设备控制码。常见的值包括 FILE_ANY_ACCESSFILE_READ_ACCESSFILE_WRITE_ACCESS

驱动程序代码:

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
#define IOCTL_CUSTOM_FUNC1 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CUSTOM_FUNC2 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)


NTSTATUS MyDeviceControlRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
PIO_STACK_LOCATION irpSp;
PVOID inputBuffer;
PVOID outputBuffer;
ULONG inputBufferLength;
ULONG outputBufferLength;
ULONG ioControlCode;
NTSTATUS ntStatus = STATUS_SUCCESS;

UNREFERENCED_PARAMETER(DeviceObject);

irpSp = IoGetCurrentIrpStackLocation(Irp);
inputBuffer = Irp->AssociatedIrp.SystemBuffer;
outputBuffer = Irp->AssociatedIrp.SystemBuffer;
inputBufferLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
outputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
ioControlCode = irpSp->Parameters.DeviceIoControl.IoControlCode;
KdPrint(("ioControlCode为0x%x\n", ioControlCode));
KdPrint(("inputBuffer地址为%p,inputBuffer内容为%s\n", inputBuffer, inputBuffer));
KdPrint(("outputBuffer地址为%p,outputBuffer内容为%s\n", outputBuffer, outputBuffer));
KdPrint(("%d %d\n", inputBufferLength, outputBufferLength));
switch (ioControlCode)
{
case IOCTL_CUSTOM_FUNC1:
{
// Handle custom function 1
// Example: Copy input data to output buffer
KdPrint(("IOCTL_CUSTOM_FUNC1!\n"));
KdPrint(("%ls\n", (TCHAR*)inputBuffer));
RtlCopyMemory(g_GlobalBuffer, inputBuffer, inputBufferLength);
KdPrint(("g_GlobalBuffer为%ls\n", g_GlobalBuffer));
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
}


case IOCTL_CUSTOM_FUNC2:
{

KdPrint(("IOCTL_CUSTOM_FUNC2!\n"));

if (wcslen(g_GlobalBuffer)*sizeof(TCHAR) < 256)
outputBufferLength = wcslen(g_GlobalBuffer) * sizeof(TCHAR);
KdPrint(("g_GlobalBuffer为%ls\n", g_GlobalBuffer));
RtlCopyMemory(outputBuffer, g_GlobalBuffer, outputBufferLength);
KdPrint(("outputBuffer为%ls\n", outputBuffer));
Irp->IoStatus.Information = outputBufferLength;
Irp->IoStatus.Status = STATUS_SUCCESS;
break;

}

default:

KdPrint(("无效的CTL_CODE!\n"));
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
break;
}

Irp->IoStatus.Status = ntStatus;
IoCompleteRequest(Irp, IO_NO_INCREMENT);

return Irp->IoStatus.Status;
}

Ring3 MFC代码:

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
#include <winioctl.h>
#define IOCTL_CUSTOM_FUNC1 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_CUSTOM_FUNC2 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
void CReadWriteRing3MFCDlg::OnBnClickedButton1() //control_Write
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
HANDLE hDevice;
DWORD bytesReturned;


hDevice = CreateFile(DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
CString errorMsg;
errorMsg.Format(L"Failed to open device. Error code: %d", GetLastError());
AfxMessageBox(errorMsg);
return;
}


DWORD dwBytesReturned;



BOOL bResult = DeviceIoControl(hDevice,
CTL_CODE(FILE_DEVICE_UNKNOWN, _tcstoul(M_CTL_CODE.GetBuffer(), NULL, 10), METHOD_BUFFERED, FILE_ANY_ACCESS),
CONTROL_WRITE.GetBuffer(), CONTROL_WRITE.GetLength() * sizeof(TCHAR),
nullptr, NULL,
&dwBytesReturned,
NULL);

CString str;
str.Format(L"%d", CONTROL_WRITE.GetLength() * sizeof(TCHAR));
AfxMessageBox(str);
if (bResult)
{
AfxMessageBox(L"成功!");
}
else
{
AfxMessageBox(L"失败!");
}
return;
}



void CReadWriteRing3MFCDlg::OnBnClickedButton3() //Contro_Read
{
// TODO: 在此添加控件通知处理程序代码

HANDLE hDevice;
DWORD bytesReturned;


hDevice = CreateFile(DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
CString errorMsg;
errorMsg.Format(L"Failed to open device. Error code: %d", GetLastError());
AfxMessageBox(errorMsg);
return;
}


DWORD dwBytesReturned;



BOOL bResult = DeviceIoControl(hDevice,
CTL_CODE(FILE_DEVICE_UNKNOWN, _tcstoul(M_CTL_CODE.GetBuffer(), NULL, 10), METHOD_BUFFERED, FILE_ANY_ACCESS),
nullptr, NULL,
CONTROL_READ.GetBuffer(), 256,
&dwBytesReturned,
NULL);

if (bResult)
{
AfxMessageBox(L"成功!");
CString str;
str.Format(L"%d", dwBytesReturned);
AfxMessageBox(str);
UpdateData(FALSE);
}
else
{
AfxMessageBox(L"失败!");
}
return;
}

效果如图:

1720880395498