Windows内核安全

7、R3与R0通信

1、课程目标

  1. 理解用户模式与内核模式通信机制
  2. 掌握IOCTL通信方式
  3. 学会使用Direct I/O和Buffered I/O
  4. 实现完整的驱动通信框架

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、课后作业

  1. 实现一个完整的驱动通信框架
  2. 添加数据加密和校验功能
  3. 实现异步通知机制
  4. 编写压力测试程序验证通信稳定性

7、扩展阅读

  • Windows Driver Model (WDM) I/O
  • IOCP完成端口详解
  • 驱动通信安全性分析