4. 符号链接创建

任务说明

本案例需要编写一个Windows内核驱动,为设备对象创建符号链接,并提供一个用户模式测试程序验证该符号链接可被正常打开和访问。符号链接是用户模式程序访问内核设备对象的桥梁,没有符号链接,应用程序无法通过Win32 API(如CreateFile)打开设备。

驱动实现

// SymLinkDriver.c
#include <ntddk.h>

#define DEVICE_NAME     L"\\Device\\SymLinkDemo"
#define SYMBOLIC_LINK   L"\\DosDevices\\SymLinkDemo"

// 设备扩展结构
typedef struct _DEVICE_EXTENSION {
    // 本案例无需额外状态,保留空结构便于扩展
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

// 分发例程:处理IRP_MJ_CREATE和IRP_MJ_CLOSE
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_MJ_DEVICE_CONTROL
NTSTATUS DispatchDeviceControl(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;
}

// 卸载例程
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING symbolicLink;
    RtlInitUnicodeString(&symbolicLink, SYMBOLIC_LINK);
    IoDeleteSymbolicLink(&symbolicLink);
    IoDeleteDevice(DriverObject->DeviceObject);
    DbgPrint("SymLinkDemo: Driver unloaded, symbolic link removed.\n");
}

// 入口函数
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    UNREFERENCED_PARAMETER(RegistryPath);
    NTSTATUS status;
    PDEVICE_OBJECT deviceObject = NULL;
    UNICODE_STRING deviceName, symbolicLink;

    RtlInitUnicodeString(&deviceName, DEVICE_NAME);
    RtlInitUnicodeString(&symbolicLink, SYMBOLIC_LINK);

    // 创建设备对象
    status = IoCreateDevice(
        DriverObject,
        sizeof(DEVICE_EXTENSION),
        &deviceName,
        FILE_DEVICE_UNKNOWN,
        0,
        FALSE,
        &deviceObject);
    if (!NT_SUCCESS(status)) {
        DbgPrint("SymLinkDemo: IoCreateDevice failed with 0x%X\n", status);
        return status;
    }

    // 设置设备为直接I/O(本案例未使用缓冲区,但保持规范)
    deviceObject->Flags |= DO_DIRECT_IO;

    // 创建符号链接
    status = IoCreateSymbolicLink(&symbolicLink, &deviceName);
    if (!NT_SUCCESS(status)) {
        DbgPrint("SymLinkDemo: IoCreateSymbolicLink failed with 0x%X\n", status);
        IoDeleteDevice(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;

    // 清除DO_DEVICE_INITIALIZING标志,使设备可被访问
    deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

    DbgPrint("SymLinkDemo: Device created, symbolic link \\\\??\\\\SymLinkDemo is ready.\n");
    return STATUS_SUCCESS;
}

用户模式测试程序

// TestSymLink.c
#include <windows.h>
#include <stdio.h>

int main()
{
    HANDLE hDevice;
    BOOL result;
    DWORD bytesReturned = 0;

    // 打开符号链接对应的设备
    hDevice = CreateFile(
        L"\\\\.\\SymLinkDemo",          // 符号链接路径(Win32命名空间)
        GENERIC_READ | GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);

    if (hDevice == INVALID_HANDLE_VALUE) {
        printf("FAILED: CreateFile returned error %lu\n", GetLastError());
        return 1;
    }

    printf("SUCCESS: Device opened via symbolic link.\n");

    // 发送一个简单的IOCTL(0x220000为FILE_DEVICE_UNKNOWN的IOCTL基础值)
    result = DeviceIoControl(
        hDevice,
        0x220000,          // IOCTL码(本驱动不做实际处理,仅验证通路)
        NULL, 0,
        NULL, 0,
        &bytesReturned,
        NULL);

    if (result) {
        printf("SUCCESS: DeviceIoControl completed.\n");
    } else {
        printf("FAILED: DeviceIoControl error %lu\n", GetLastError());
    }

    CloseHandle(hDevice);
    printf("Test completed.\n");
    return 0;
}

编译与安装步骤

  1. 驱动编译:使用Visual Studio的驱动程序开发套件(WDK),创建“内核模式驱动程序:空项目”,将SymLinkDriver.c加入项目,编译生成SymLinkDriver.sys。
  2. 测试程序编译:使用Visual Studio创建Win32控制台项目,将TestSymLink.c加入项目,编译生成TestSymLink.exe。
  3. 安装驱动:在管理员命令行中创建服务并启动:
    sc create SymLinkDemo type= kernel binPath= C:\Path\To\SymLinkDriver.sys
    sc start SymLinkDemo
  4. 运行测试:在命令行执行TestSymLink.exe。
  5. 清理:测试完成后停止并删除服务:
    sc stop SymLinkDemo
    sc delete SymLinkDemo

验证方法与判定标准

运行TestSymLink.exe后,观察输出:

  • 若显示“SUCCESS: Device opened via symbolic link.”,说明符号链接创建成功且用户模式可通过\\.\SymLinkDemo访问设备对象。
  • 若显示“FAILED: CreateFile returned error 2”(ERROR_FILE_NOT_FOUND),说明符号链接未正确创建或驱动未加载。
  • 若显示“FAILED: CreateFile returned error 5”(ERROR_ACCESS_DENIED),说明设备对象存在但权限不足(本驱动未设置安全描述符,默认允许系统账户访问,普通管理员应可访问)。

同时可在内核调试器(WinDbg)中观察DbgPrint输出,确认驱动加载时打印的“Device created, symbolic link \\\\??\\\\SymLinkDemo is ready.”消息。

符号链接结构示意

用户模式 (Ring 3) TestSymLink.exe \\\\.\\SymLinkDemo 内核模式 (Ring 0) \\DosDevices\\SymLinkDemo \\Device\\SymLinkDemo IoCreateSymbolicLink 对象管理器解析

上图展示了符号链接的解析路径:用户程序通过Win32命名空间路径\\.\SymLinkDemo发起访问,Windows对象管理器将其转换为内核命名空间的\DosDevices\SymLinkDemo,最终通过IoCreateSymbolicLink建立的映射关系定位到实际的设备对象\Device\SymLinkDemo,从而完成打开操作。