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;
}
编译与安装步骤
- 驱动编译:使用Visual Studio的驱动程序开发套件(WDK),创建“内核模式驱动程序:空项目”,将SymLinkDriver.c加入项目,编译生成SymLinkDriver.sys。
- 测试程序编译:使用Visual Studio创建Win32控制台项目,将TestSymLink.c加入项目,编译生成TestSymLink.exe。
- 安装驱动:在管理员命令行中创建服务并启动:
sc create SymLinkDemo type= kernel binPath= C:\Path\To\SymLinkDriver.sys sc start SymLinkDemo - 运行测试:在命令行执行TestSymLink.exe。
- 清理:测试完成后停止并删除服务:
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.”消息。
符号链接结构示意
上图展示了符号链接的解析路径:用户程序通过Win32命名空间路径\\.\SymLinkDemo发起访问,Windows对象管理器将其转换为内核命名空间的\DosDevices\SymLinkDemo,最终通过IoCreateSymbolicLink建立的映射关系定位到实际的设备对象\Device\SymLinkDemo,从而完成打开操作。