Windows内核安全

2、第一个驱动程序

1、课程目标

  1. 理解Windows驱动程序架构
  2. 掌握WDK开发环境配置
  3. 编写第一个内核驱动程序
  4. 学会驱动的加载和卸载

2、名词解释

术语 解释
WDK Windows Driver Kit,驱动开发包
KMDF Kernel-Mode Driver Framework
WDM Windows Driver Model
DriverEntry 驱动入口函数
DriverUnload 驱动卸载函数
IRP I/O Request Packet

3、使用工具

工具 用途
Visual Studio IDE开发环境
WDK 驱动开发包
WinDbg 内核调试
OSR Driver Loader 驱动加载工具
DebugView 查看调试输出

4、技术原理

4.1、驱动程序架构

┌─────────────────────────────────────────────────────────────┐
│                    Windows驱动架构                          │
│                                                             │
│  用户模式 (Ring3)                                          │
│  ┌────────────────────────────────────────────────────┐    │
│  │  应用程序 → CreateFile/DeviceIoControl/ReadFile   │    │
│  └────────────────────────────────────────────────────┘    │
│                         ↓                                   │
│  ─────────────────── 系统调用边界 ────────────────────     │
│                         ↓                                   │
│  内核模式 (Ring0)                                          │
│  ┌────────────────────────────────────────────────────┐    │
│  │  I/O Manager → 创建IRP → 发送到驱动                │    │
│  └────────────────────────────────────────────────────┘    │
│                         ↓                                   │
│  ┌────────────────────────────────────────────────────┐    │
│  │  Driver Object                                     │    │
│  │  ├─ DriverEntry()     - 初始化                     │    │
│  │  ├─ DriverUnload()    - 卸载清理                   │    │
│  │  ├─ MajorFunction[]   - IRP处理函数数组            │    │
│  │  └─ DeviceObject      - 设备对象                   │    │
│  └────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

4.2、驱动类型

类型 说明 适用场景
Legacy NT驱动 传统驱动模型 简单驱动、安全工具
WDM驱动 支持PnP和电源管理 硬件驱动
KMDF驱动 框架封装、简化开发 现代驱动开发
微型过滤器 文件系统过滤 文件监控/杀毒

5、代码实现

5.1、示例1:最简单的驱动程序

// FirstDriver.c - 第一个驱动程序
#include <ntddk.h>

// 驱动卸载函数
VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
    UNREFERENCED_PARAMETER(DriverObject);
    
    DbgPrint("[FirstDriver] Driver unloaded!\n");
}

// 驱动入口函数
NTSTATUS DriverEntry(
    PDRIVER_OBJECT  DriverObject,
    PUNICODE_STRING RegistryPath
) {
    UNREFERENCED_PARAMETER(RegistryPath);
    
    DbgPrint("[FirstDriver] Driver loaded!\n");
    DbgPrint("[FirstDriver] DriverObject: 0x%p\n", DriverObject);
    
    // 设置卸载函数
    DriverObject->DriverUnload = DriverUnload;
    
    return STATUS_SUCCESS;
}

5.2、示例2:完整的驱动框架

// CompleteDriver.c - 完整驱动框架
#include <ntddk.h>

#define DEVICE_NAME     L"\\Device\\MyDevice"
#define SYMBOLIC_NAME   L"\\DosDevices\\MyDevice"

// 全局设备对象
PDEVICE_OBJECT g_DeviceObject = NULL;

// IRP处理 - 创建/关闭
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;
}

// IRP处理 - 读取
NTSTATUS DispatchRead(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
) {
    UNREFERENCED_PARAMETER(DeviceObject);
    
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
    ULONG readLength = irpStack->Parameters.Read.Length;
    PVOID buffer = Irp->AssociatedIrp.SystemBuffer;
    
    DbgPrint("[Driver] Read request, length: %u\n", readLength);
    
    // 返回一些数据
    if (buffer && readLength >= 5) {
        RtlCopyMemory(buffer, "Hello", 5);
        Irp->IoStatus.Information = 5;
    } else {
        Irp->IoStatus.Information = 0;
    }
    
    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    
    return STATUS_SUCCESS;
}

// IRP处理 - 写入
NTSTATUS DispatchWrite(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
) {
    UNREFERENCED_PARAMETER(DeviceObject);
    
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
    ULONG writeLength = irpStack->Parameters.Write.Length;
    PVOID buffer = Irp->AssociatedIrp.SystemBuffer;
    
    DbgPrint("[Driver] Write request, length: %u\n", writeLength);
    
    if (buffer && writeLength > 0) {
        DbgPrint("[Driver] Data: %.*s\n", writeLength, (char*)buffer);
    }
    
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = writeLength;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    
    return STATUS_SUCCESS;
}

// IRP处理 - DeviceIoControl
NTSTATUS DispatchDeviceControl(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
) {
    UNREFERENCED_PARAMETER(DeviceObject);
    
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
    ULONG ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
    PVOID inputBuffer = Irp->AssociatedIrp.SystemBuffer;
    ULONG inputLength = irpStack->Parameters.DeviceIoControl.InputBufferLength;
    ULONG outputLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
    ULONG bytesReturned = 0;
    NTSTATUS status = STATUS_SUCCESS;
    
    DbgPrint("[Driver] IOCTL: 0x%X\n", ioControlCode);
    
    switch (ioControlCode) {
        case 0x222000:  // 自定义IOCTL码
            if (inputBuffer && inputLength > 0) {
                DbgPrint("[Driver] Input: %.*s\n", inputLength, (char*)inputBuffer);
            }
            if (outputLength >= 4) {
                *(ULONG*)inputBuffer = 0x12345678;
                bytesReturned = 4;
            }
            break;
            
        default:
            status = STATUS_INVALID_DEVICE_REQUEST;
    }
    
    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = bytesReturned;
    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("[Driver] Unloaded\n");
}

// 驱动入口
NTSTATUS DriverEntry(
    PDRIVER_OBJECT  DriverObject,
    PUNICODE_STRING RegistryPath
) {
    NTSTATUS status;
    UNICODE_STRING deviceName;
    UNICODE_STRING symbolicName;
    
    UNREFERENCED_PARAMETER(RegistryPath);
    
    DbgPrint("[Driver] DriverEntry\n");
    
    // 创建设备名称
    RtlInitUnicodeString(&deviceName, DEVICE_NAME);
    RtlInitUnicodeString(&symbolicName, SYMBOLIC_NAME);
    
    // 创建设备对象
    status = IoCreateDevice(
        DriverObject,           // 驱动对象
        0,                      // 设备扩展大小
        &deviceName,            // 设备名称
        FILE_DEVICE_UNKNOWN,    // 设备类型
        0,                      // 设备特征
        FALSE,                  // 非独占
        &g_DeviceObject         // 返回设备对象
    );
    
    if (!NT_SUCCESS(status)) {
        DbgPrint("[Driver] IoCreateDevice failed: 0x%X\n", status);
        return status;
    }
    
    // 创建符号链接(供用户模式访问)
    status = IoCreateSymbolicLink(&symbolicName, &deviceName);
    if (!NT_SUCCESS(status)) {
        DbgPrint("[Driver] IoCreateSymbolicLink failed: 0x%X\n", status);
        IoDeleteDevice(g_DeviceObject);
        return status;
    }
    
    // 设置IRP处理函数
    DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchCreateClose;
    DriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;
    DriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchWrite;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDeviceControl;
    
    // 设置卸载函数
    DriverObject->DriverUnload = DriverUnload;
    
    // 设置IO方式为缓冲IO
    g_DeviceObject->Flags |= DO_BUFFERED_IO;
    g_DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
    
    DbgPrint("[Driver] Device created: %wZ\n", &deviceName);
    
    return STATUS_SUCCESS;
}

5.3、示例3:用户模式测试程序

// TestDriver.c - 用户模式测试程序
#include <windows.h>
#include <stdio.h>

#define DEVICE_NAME "\\\\.\\MyDevice"
#define IOCTL_TEST  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

int main() {
    HANDLE hDevice;
    DWORD bytesReturned;
    char readBuffer[256];
    char writeData[] = "Hello from user mode!";
    ULONG ioctlInput = 0x1234;
    ULONG ioctlOutput = 0;
    
    // 打开设备
    hDevice = CreateFileA(
        DEVICE_NAME,
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    
    if (hDevice == INVALID_HANDLE_VALUE) {
        printf("Failed to open device: %d\n", GetLastError());
        return 1;
    }
    
    printf("Device opened successfully!\n");
    
    // 写入数据
    if (WriteFile(hDevice, writeData, sizeof(writeData), &bytesReturned, NULL)) {
        printf("Write successful, bytes: %d\n", bytesReturned);
    }
    
    // 读取数据
    memset(readBuffer, 0, sizeof(readBuffer));
    if (ReadFile(hDevice, readBuffer, sizeof(readBuffer), &bytesReturned, NULL)) {
        printf("Read successful, bytes: %d, data: %s\n", bytesReturned, readBuffer);
    }
    
    // 发送IOCTL
    if (DeviceIoControl(
            hDevice,
            IOCTL_TEST,
            &ioctlInput, sizeof(ioctlInput),
            &ioctlOutput, sizeof(ioctlOutput),
            &bytesReturned,
            NULL)) {
        printf("IOCTL successful, output: 0x%X\n", ioctlOutput);
    }
    
    CloseHandle(hDevice);
    
    return 0;
}

5.4、示例4:驱动加载服务

// LoadDriver.c - 驱动加载程序
#include <windows.h>
#include <stdio.h>

BOOL LoadDriver(const char* driverPath, const char* serviceName) {
    SC_HANDLE hSCManager = NULL;
    SC_HANDLE hService = NULL;
    BOOL result = FALSE;
    
    // 打开服务控制管理器
    hSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!hSCManager) {
        printf("OpenSCManager failed: %d\n", GetLastError());
        return FALSE;
    }
    
    // 创建服务
    hService = CreateServiceA(
        hSCManager,
        serviceName,            // 服务名
        serviceName,            // 显示名
        SERVICE_ALL_ACCESS,
        SERVICE_KERNEL_DRIVER,  // 内核驱动类型
        SERVICE_DEMAND_START,   // 手动启动
        SERVICE_ERROR_IGNORE,
        driverPath,             // 驱动文件路径
        NULL, NULL, NULL, NULL, NULL
    );
    
    if (!hService) {
        if (GetLastError() == ERROR_SERVICE_EXISTS) {
            // 服务已存在,尝试打开
            hService = OpenServiceA(hSCManager, serviceName, SERVICE_ALL_ACCESS);
        }
        if (!hService) {
            printf("CreateService failed: %d\n", GetLastError());
            CloseServiceHandle(hSCManager);
            return FALSE;
        }
    }
    
    // 启动服务(加载驱动)
    if (StartServiceA(hService, 0, NULL)) {
        printf("Driver loaded successfully!\n");
        result = TRUE;
    } else {
        DWORD err = GetLastError();
        if (err == ERROR_SERVICE_ALREADY_RUNNING) {
            printf("Driver already running.\n");
            result = TRUE;
        } else {
            printf("StartService failed: %d\n", err);
        }
    }
    
    CloseServiceHandle(hService);
    CloseServiceHandle(hSCManager);
    
    return result;
}

BOOL UnloadDriver(const char* serviceName) {
    SC_HANDLE hSCManager = NULL;
    SC_HANDLE hService = NULL;
    SERVICE_STATUS ss;
    BOOL result = FALSE;
    
    hSCManager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    if (!hSCManager) return FALSE;
    
    hService = OpenServiceA(hSCManager, serviceName, SERVICE_ALL_ACCESS);
    if (hService) {
        // 停止服务(卸载驱动)
        if (ControlService(hService, SERVICE_CONTROL_STOP, &ss)) {
            printf("Driver unloaded.\n");
        }
        
        // 删除服务
        if (DeleteService(hService)) {
            printf("Service deleted.\n");
            result = TRUE;
        }
        
        CloseServiceHandle(hService);
    }
    
    CloseServiceHandle(hSCManager);
    return result;
}

int main(int argc, char* argv[]) {
    if (argc < 3) {
        printf("Usage: %s <load|unload> <driver_path|service_name>\n", argv[0]);
        return 1;
    }
    
    if (strcmp(argv[1], "load") == 0) {
        LoadDriver(argv[2], "MyDriver");
    } else if (strcmp(argv[1], "unload") == 0) {
        UnloadDriver(argv[2]);
    }
    
    return 0;
}

6、Visual Studio项目配置

1. 安装WDK(与VS版本匹配)
2. 新建项目 → 驱动 → Kernel Mode Driver (KMDF) 或 Empty WDM Driver
3. 项目属性配置:
   - 配置类型:驱动程序
   - 目标平台:Desktop
   - 驱动模型设置:WDM 或 KMDF
   - 关闭SDL检查(开发期间)

4. 签名配置(测试签名):
   - 驱动签名模式:测试签名
   - 或使用bcdedit /set testsigning on

5. 编译输出:
   - Debug/x64/MyDriver.sys
   - Debug/x64/MyDriver.pdb(调试符号)

7、课后作业

  1. 配置VS+WDK开发环境
  2. 编写一个可以读写的驱动程序
  3. 实现自定义IOCTL通信
  4. 使用WinDbg调试驱动程序

8、扩展阅读

  • Windows Driver Development (Penny Orwick)
  • WDK文档和示例
  • OSR驱动开发资源