创建驱动设备与符号链接
#include<ntifs.h> //ntddk.h
//创建驱动设备对象
NTSTATUS CreateDevice(PDRIVER_OBJECT driver)
{
NTSTATUS status;
UNICODE_STRING MyDriver;
PDEVICE_OBJECT device;//用于存放设备对象
RtlInitUnicodeString(&MyDriver, L"\\DEVICE\\MyDriver");//驱动设备名字
status = IoCreateDevice(driver, sizeof(driver->DriverExtension), &MyDriver, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &device);
if (status == STATUS_SUCCESS)//STATUS_SUCCESS)
{
KdPrint(("驱动设备对象创建成功,OK \n"));
//创建符号链接
UNICODE_STRING uzSymbolName; //符号链接名字
RtlInitUnicodeString(&uzSymbolName, L"\\??\\MyDriver"); //CreateFile
status = IoCreateSymbolicLink(&uzSymbolName, &MyDriver);
if (status == STATUS_SUCCESS)
{
KdPrint(("创建符号链接 %wZ 成功 ", &uzSymbolName));
}
else
{
KdPrint(("创建符号链接 %wZ 失败 status=%X", &uzSymbolName, status));
}
}
else
{
KdPrint(("驱动设备对象创建失败,删除设备\n"));
IoDeleteDevice(device);
}
return status;
}
NTSTATUS IRP_CALL(PDEVICE_OBJECT device, PIRP pirp)
{
device;
KdPrint(("进入派遣函数"));
PIO_STACK_LOCATION irpStackL;
irpStackL = IoGetCurrentIrpStackLocation(pirp); //获取应用层传来的参数
switch (irpStackL->MajorFunction)
{
case IRP_MJ_DEVICE_CONTROL: //DeviceIoControl
{
KdPrint(("用户层调用了 DeviceIoControl"));
break;
}
case IRP_MJ_CREATE: //CreateFile
{
KdPrint(("用户层调用了 CreateFile"));
break;
}
case IRP_MJ_CLOSE: //CloseHandle
{
KdPrint(("用户层调用了 CloseHandle"));
break;
}
}
pirp->IoStatus.Status = STATUS_SUCCESS;
// pirp->IoStatus.Information = 4;//返回给DeviceIoControl中的 倒数第二个参数lpBytesReturned
IoCompleteRequest(pirp, IO_NO_INCREMENT);//调用方已完成所有I/O请求处理操作 并且不增加优先级
KdPrint(("离开派遣函数"));
return STATUS_SUCCESS; //0 返回成功
}
//卸载驱动时会被调用
void DriverUnload(PDRIVER_OBJECT DriverObject)
{
KdPrint(("进入卸载例程DriverObject=%p 行号=%d ", DriverObject, __LINE__));
if (DriverObject->DeviceObject)
{
//删除符号链接
UNICODE_STRING uzSymbolName; //符号链接名字
RtlInitUnicodeString(&uzSymbolName, L"\\??\\MyDriver"); //CreateFile
KdPrint(("删除符号链接=%wZ", &uzSymbolName));
IoDeleteSymbolicLink(&uzSymbolName);
//
KdPrint(("删除驱动设备"));
IoDeleteDevice(DriverObject->DeviceObject);//删除设备对象
}
KdPrint(("退出 DriverUnLoad例程"));
}
//加载驱动时 会被调用
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
KdPrint(("进入 DriverEntry入口点 DriverObject=%p 行号=%d\n", DriverObject, __LINE__)); //Debug
DriverObject->DriverUnload = DriverUnload; //注册卸载例程 回调函数
NTSTATUS status = CreateDevice(DriverObject);为驱动对象创建一个设备
status;
DriverObject->MajorFunction[IRP_MJ_CREATE] = IRP_CALL;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = IRP_CALL;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IRP_CALL;
//KdPrint((" RegistryPath=%s\n", RegistryPath->Buffer)); //多字节字符集
KdPrint((" RegistryPath=%ws\n", RegistryPath->Buffer)); //Unicode 宽字符
return 0; //成功
}
windows下的设备是以"/Device/[设备名]”形式命名的。
例如磁盘分区的c盘,d盘的设备名称就是"/Device/HarddiskVolume1”,"/Device/HarddiskVolume2”, 当然也可以不指定设备名称。如果IoCreateDevice中没有指定设备名称,那么I/O管理器会自动分配一个数字作为设备的名称。例如"/Device/00000001"。/Device/[设备名],不容易记忆,通常符号链接可以理解为设备的别名,更重要的是设备名,只能被内核模式下的其他驱动所识别,而别名可以被用户模式下的应用程序识别,例如c盘,就是名为"c:"的符号链接,其真正的设备对象是"/Device/HarddiskVolume1”,所以在写驱动时候,一般我们创建符号链接,即使驱动中没有用到,这也算是一个好的习惯吧。
驱动中符号链接名是这样写的
L"//??//HelloDDK" --->/??/HelloDDK
L"//DosDevices//HelloDDK"--->/DosDevices/HelloDDK
在应用程序中,符号链接名:
L".//HelloDDK"-->//./HelloDDK
winobj和DeviceTree可以用来查看这些信息。
DosDevices的符号链接名就是??, 所以"//DosDevices//XXXX"其实就是"//??//XXXX"