1、课程目标
- 理解用户模式与内核模式通信机制
- 掌握IOCTL通信方式
- 学会使用Direct I/O和Buffered I/O
- 实现完整的驱动通信框架
2、名词解释
| 术语 |
解释 |
| IOCTL |
I/O Control Code,设备控制代码 |
| Buffered I/O |
缓冲I/O,系统缓冲区拷贝 |
| Direct I/O |
直接I/O,MDL方式 |
| Neither I/O |
既非缓冲也非直接的I/O方式 |
| CTL_CODE |
定义IOCTL控制码的宏 |
3、使用工具
| 工具 |
用途 |
| Visual Studio |
开发驱动和应用 |
| WinDbg |
调试通信 |
| Device Tree |
查看设备对象 |
4、技术原理
4.1、IOCTL控制码结构
┌─────────────────────────────────────────────────────────────┐
│ IOCTL控制码结构 │
│ │
│ 31-16 15-14 13-2 1-0 │
│ ┌──────────┬─────────┬───────────┬──────────┐ │
│ │ DeviceType│ Required │ Function │ Method │ │
│ │ (16位) │ Access │ Code │ (2位) │ │
│ │ │ (2位) │ (12位) │ │ │
│ └──────────┴─────────┴───────────┴──────────┘ │
│ │
│ CTL_CODE(DeviceType, Function, Method, Access) │
│ │
│ Method传输方式: │
│ - METHOD_BUFFERED (0) - 系统缓冲区 │
│ - METHOD_IN_DIRECT (1) - 输入直接(MDL) │
│ - METHOD_OUT_DIRECT (2) - 输出直接(MDL) │
│ - METHOD_NEITHER (3) - 原始指针 │
└─────────────────────────────────────────────────────────────┘
4.2、三种I/O传输方式对比
| 方式 |
安全性 |
性能 |
适用场景 |
| Buffered I/O |
高 |
低(需拷贝) |
小数据量 |
| Direct I/O |
中 |
高 |
大数据量 |
| Neither I/O |
低 |
最高 |
特殊场景 |
5、代码实现
5.1、示例1:公共头文件定义
// CommDef.h - 通信定义公共头文件
#ifndef COMM_DEF_H
#define COMM_DEF_H
// 设备名称
#define DEVICE_NAME L"\\Device\\MyCommDevice"
#define SYMBOLIC_NAME L"\\DosDevices\\MyCommDevice"
#define USER_DEVICE "\\\\.\\MyCommDevice"
// 设备类型
#define DEVICE_TYPE_MYCOMM 0x8000
// IOCTL控制码定义
#define IOCTL_GET_VERSION CTL_CODE(DEVICE_TYPE_MYCOMM, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_GET_PROCESS CTL_CODE(DEVICE_TYPE_MYCOMM, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_PROTECT_PID CTL_CODE(DEVICE_TYPE_MYCOMM, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_DIRECT_READ CTL_CODE(DEVICE_TYPE_MYCOMM, 0x803, METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
#define IOCTL_DIRECT_WRITE CTL_CODE(DEVICE_TYPE_MYCOMM, 0x804, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
// 版本信息结构
typedef struct _VERSION_INFO {
ULONG MajorVersion;
ULONG MinorVersion;
ULONG BuildNumber;
} VERSION_INFO, *PVERSION_INFO;
// 进程信息结构
typedef struct _PROCESS_ENTRY {
ULONG ProcessId;
ULONG ParentId;
ULONG ThreadCount;
CHAR ImageName[64];
} PROCESS_ENTRY, *PPROCESS_ENTRY;
// 保护PID请求
typedef struct _PROTECT_PID_REQUEST {
ULONG ProcessId;
BOOLEAN Enable;
} PROTECT_PID_REQUEST, *PPROTECT_PID_REQUEST;
#endif
5.2、示例2:驱动端通信实现
// CommDriver.c - 驱动端通信实现
#include <ntddk.h>
#include "CommDef.h"
PDEVICE_OBJECT g_DeviceObject = NULL;
ULONG g_ProtectedPid = 0;
// 版本信息
#define DRIVER_MAJOR_VERSION 1
#define DRIVER_MINOR_VERSION 0
#define DRIVER_BUILD_NUMBER 100
// 创建和关闭处理
NTSTATUS DispatchCreateClose(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
UNREFERENCED_PARAMETER(DeviceObject);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// Buffered I/O方式的IOCTL处理
NTSTATUS HandleBufferedIoctl(PIRP Irp, PIO_STACK_LOCATION IrpStack) {
NTSTATUS status = STATUS_SUCCESS;
ULONG bytesReturned = 0;
PVOID inputBuffer = Irp->AssociatedIrp.SystemBuffer;
PVOID outputBuffer = Irp->AssociatedIrp.SystemBuffer;
ULONG inputLength = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
ULONG outputLength = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
ULONG ioControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
switch (ioControlCode) {
case IOCTL_GET_VERSION: {
if (outputLength < sizeof(VERSION_INFO)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}
PVERSION_INFO pVersion = (PVERSION_INFO)outputBuffer;
pVersion->MajorVersion = DRIVER_MAJOR_VERSION;
pVersion->MinorVersion = DRIVER_MINOR_VERSION;
pVersion->BuildNumber = DRIVER_BUILD_NUMBER;
bytesReturned = sizeof(VERSION_INFO);
break;
}
case IOCTL_GET_PROCESS: {
// 获取进程列表
if (outputLength < sizeof(PROCESS_ENTRY)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}
PPROCESS_ENTRY pEntries = (PPROCESS_ENTRY)outputBuffer;
ULONG maxEntries = outputLength / sizeof(PROCESS_ENTRY);
ULONG count = 0;
// 遍历进程(简化示例)
for (ULONG pid = 4; pid < 1000 && count < maxEntries; pid += 4) {
PEPROCESS process;
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)(ULONG_PTR)pid, &process))) {
pEntries[count].ProcessId = pid;
pEntries[count].ParentId = 0;
pEntries[count].ThreadCount = 0;
PUCHAR imageName = PsGetProcessImageFileName(process);
if (imageName) {
strncpy(pEntries[count].ImageName, (char*)imageName, 63);
}
ObDereferenceObject(process);
count++;
}
}
bytesReturned = count * sizeof(PROCESS_ENTRY);
break;
}
case IOCTL_PROTECT_PID: {
if (inputLength < sizeof(PROTECT_PID_REQUEST)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}
PPROTECT_PID_REQUEST pRequest = (PPROTECT_PID_REQUEST)inputBuffer;
if (pRequest->Enable) {
g_ProtectedPid = pRequest->ProcessId;
DbgPrint("[Comm] Protected PID: %d\n", g_ProtectedPid);
} else {
g_ProtectedPid = 0;
}
break;
}
default:
status = STATUS_INVALID_DEVICE_REQUEST;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = bytesReturned;
return status;
}
// Direct I/O方式处理
NTSTATUS HandleDirectIoctl(PIRP Irp, PIO_STACK_LOCATION IrpStack) {
NTSTATUS status = STATUS_SUCCESS;
ULONG bytesReturned = 0;
ULONG ioControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
switch (ioControlCode) {
case IOCTL_DIRECT_READ: {
// Direct Output - 数据从驱动到用户
PMDL mdl = Irp->MdlAddress;
if (!mdl) {
status = STATUS_INVALID_PARAMETER;
break;
}
PVOID buffer = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority);
if (!buffer) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
ULONG bufferLength = MmGetMdlByteCount(mdl);
// 写入数据到用户缓冲区
char* message = "Direct I/O Data from Driver";
ULONG msgLen = (ULONG)strlen(message) + 1;
if (bufferLength >= msgLen) {
RtlCopyMemory(buffer, message, msgLen);
bytesReturned = msgLen;
}
break;
}
case IOCTL_DIRECT_WRITE: {
// Direct Input - 数据从用户到驱动
PMDL mdl = Irp->MdlAddress;
if (!mdl) {
status = STATUS_INVALID_PARAMETER;
break;
}
PVOID buffer = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority);
ULONG bufferLength = MmGetMdlByteCount(mdl);
if (buffer && bufferLength > 0) {
DbgPrint("[Comm] Direct Write: %.*s\n", bufferLength, (char*)buffer);
}
break;
}
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = bytesReturned;
return status;
}
// IOCTL分发函数
NTSTATUS DispatchDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
UNREFERENCED_PARAMETER(DeviceObject);
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
ULONG method = ioControlCode & 3;
NTSTATUS status;
switch (method) {
case METHOD_BUFFERED:
status = HandleBufferedIoctl(Irp, irpStack);
break;
case METHOD_IN_DIRECT:
case METHOD_OUT_DIRECT:
status = HandleDirectIoctl(Irp, irpStack);
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
}
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
// 驱动卸载
VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
UNICODE_STRING symbolicName;
RtlInitUnicodeString(&symbolicName, SYMBOLIC_NAME);
IoDeleteSymbolicLink(&symbolicName);
if (DriverObject->DeviceObject) {
IoDeleteDevice(DriverObject->DeviceObject);
}
DbgPrint("[Comm] Driver unloaded\n");
}
// 驱动入口
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
NTSTATUS status;
UNICODE_STRING deviceName, symbolicName;
UNREFERENCED_PARAMETER(RegistryPath);
RtlInitUnicodeString(&deviceName, DEVICE_NAME);
RtlInitUnicodeString(&symbolicName, SYMBOLIC_NAME);
status = IoCreateDevice(DriverObject, 0, &deviceName,
DEVICE_TYPE_MYCOMM, 0, FALSE, &g_DeviceObject);
if (!NT_SUCCESS(status)) return status;
status = IoCreateSymbolicLink(&symbolicName, &deviceName);
if (!NT_SUCCESS(status)) {
IoDeleteDevice(g_DeviceObject);
return status;
}
DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDeviceControl;
DriverObject->DriverUnload = DriverUnload;
g_DeviceObject->Flags |= DO_BUFFERED_IO;
g_DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
DbgPrint("[Comm] Driver loaded\n");
return STATUS_SUCCESS;
}
5.3、示例3:用户模式通信库
// CommLib.c - 用户模式通信库
#include <windows.h>
#include <stdio.h>
#include "CommDef.h"
typedef struct _DRIVER_COMM {
HANDLE hDevice;
BOOL bConnected;
} DRIVER_COMM, *PDRIVER_COMM;
// 连接驱动
BOOL Comm_Connect(PDRIVER_COMM pComm) {
pComm->hDevice = CreateFileA(
USER_DEVICE,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (pComm->hDevice == INVALID_HANDLE_VALUE) {
printf("Failed to connect: %d\n", GetLastError());
pComm->bConnected = FALSE;
return FALSE;
}
pComm->bConnected = TRUE;
return TRUE;
}
// 断开连接
VOID Comm_Disconnect(PDRIVER_COMM pComm) {
if (pComm->hDevice != INVALID_HANDLE_VALUE) {
CloseHandle(pComm->hDevice);
pComm->hDevice = INVALID_HANDLE_VALUE;
}
pComm->bConnected = FALSE;
}
// 获取版本
BOOL Comm_GetVersion(PDRIVER_COMM pComm, PVERSION_INFO pVersion) {
DWORD bytesReturned;
if (!pComm->bConnected) return FALSE;
return DeviceIoControl(
pComm->hDevice,
IOCTL_GET_VERSION,
NULL, 0,
pVersion, sizeof(VERSION_INFO),
&bytesReturned,
NULL
);
}
// 获取进程列表
ULONG Comm_GetProcessList(PDRIVER_COMM pComm, PPROCESS_ENTRY pEntries, ULONG maxCount) {
DWORD bytesReturned;
if (!pComm->bConnected) return 0;
if (!DeviceIoControl(
pComm->hDevice,
IOCTL_GET_PROCESS,
NULL, 0,
pEntries, maxCount * sizeof(PROCESS_ENTRY),
&bytesReturned,
NULL)) {
return 0;
}
return bytesReturned / sizeof(PROCESS_ENTRY);
}
// 设置保护PID
BOOL Comm_ProtectPid(PDRIVER_COMM pComm, ULONG pid, BOOL enable) {
DWORD bytesReturned;
PROTECT_PID_REQUEST request;
if (!pComm->bConnected) return FALSE;
request.ProcessId = pid;
request.Enable = enable;
return DeviceIoControl(
pComm->hDevice,
IOCTL_PROTECT_PID,
&request, sizeof(request),
NULL, 0,
&bytesReturned,
NULL
);
}
// 测试程序
int main() {
DRIVER_COMM comm = {0};
printf("=== Driver Communication Test ===\n\n");
if (!Comm_Connect(&comm)) {
printf("Failed to connect to driver\n");
return 1;
}
printf("Connected to driver\n");
// 获取版本
VERSION_INFO version;
if (Comm_GetVersion(&comm, &version)) {
printf("Driver version: %d.%d.%d\n",
version.MajorVersion,
version.MinorVersion,
version.BuildNumber);
}
// 获取进程列表
PROCESS_ENTRY processes[100];
ULONG count = Comm_GetProcessList(&comm, processes, 100);
printf("\nProcess list (%d processes):\n", count);
for (ULONG i = 0; i < count && i < 10; i++) {
printf(" PID: %5d Name: %s\n",
processes[i].ProcessId,
processes[i].ImageName);
}
// 设置保护
if (Comm_ProtectPid(&comm, GetCurrentProcessId(), TRUE)) {
printf("\nProtected current process: %d\n", GetCurrentProcessId());
}
Comm_Disconnect(&comm);
return 0;
}
5.4、示例4:异步IOCTL通信
// AsyncComm.c - 异步通信
#include <windows.h>
#include <stdio.h>
#define IOCTL_WAIT_EVENT CTL_CODE(0x8000, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS)
typedef struct _ASYNC_CONTEXT {
OVERLAPPED Overlapped;
HANDLE hDevice;
BYTE Buffer[1024];
BOOL Pending;
} ASYNC_CONTEXT, *PASYNC_CONTEXT;
// 异步完成回调
VOID CALLBACK IoCompletionCallback(
DWORD dwErrorCode,
DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped
) {
PASYNC_CONTEXT ctx = CONTAINING_RECORD(lpOverlapped, ASYNC_CONTEXT, Overlapped);
if (dwErrorCode == ERROR_SUCCESS) {
printf("[Async] Received %d bytes: %s\n",
dwNumberOfBytesTransfered, ctx->Buffer);
} else {
printf("[Async] Error: %d\n", dwErrorCode);
}
ctx->Pending = FALSE;
}
// 发起异步请求
BOOL StartAsyncRequest(PASYNC_CONTEXT ctx) {
DWORD bytesReturned;
memset(&ctx->Overlapped, 0, sizeof(OVERLAPPED));
ctx->Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
ctx->Pending = TRUE;
BOOL result = DeviceIoControl(
ctx->hDevice,
IOCTL_WAIT_EVENT,
NULL, 0,
ctx->Buffer, sizeof(ctx->Buffer),
&bytesReturned,
&ctx->Overlapped
);
if (!result && GetLastError() == ERROR_IO_PENDING) {
printf("[Async] Request pending...\n");
return TRUE;
}
return result;
}
// 等待异步完成
BOOL WaitForAsyncResult(PASYNC_CONTEXT ctx, DWORD timeout) {
DWORD bytesTransferred;
DWORD result = WaitForSingleObject(ctx->Overlapped.hEvent, timeout);
if (result == WAIT_OBJECT_0) {
GetOverlappedResult(ctx->hDevice, &ctx->Overlapped,
&bytesTransferred, FALSE);
printf("[Async] Completed: %d bytes\n", bytesTransferred);
return TRUE;
}
if (result == WAIT_TIMEOUT) {
printf("[Async] Timeout\n");
CancelIo(ctx->hDevice);
}
return FALSE;
}
// 使用完成端口
HANDLE CreateIoCompletionPort_Wrapper(HANDLE hDevice) {
return CreateIoCompletionPort(hDevice, NULL, 0, 0);
}
DWORD WINAPI IoCompletionThread(LPVOID lpParam) {
HANDLE hIocp = (HANDLE)lpParam;
DWORD bytesTransferred;
ULONG_PTR completionKey;
LPOVERLAPPED pOverlapped;
while (TRUE) {
BOOL result = GetQueuedCompletionStatus(
hIocp,
&bytesTransferred,
&completionKey,
&pOverlapped,
INFINITE
);
if (!result && pOverlapped == NULL) {
break; // 端口已关闭
}
if (pOverlapped) {
PASYNC_CONTEXT ctx = CONTAINING_RECORD(pOverlapped, ASYNC_CONTEXT, Overlapped);
printf("[IOCP] Received %d bytes\n", bytesTransferred);
}
}
return 0;
}
6、课后作业
- 实现一个完整的驱动通信框架
- 添加数据加密和校验功能
- 实现异步通知机制
- 编写压力测试程序验证通信稳定性
7、扩展阅读
- Windows Driver Model (WDM) I/O
- IOCP完成端口详解
- 驱动通信安全性分析