D005-驱动设备与符号链接


学习目标: 
  创建驱动设备
  绑定符号链接
  
  
  
typedef struct _UNICODE_STRING {
    USHORT Length;//字符串长度
    USHORT MaximumLength; //最大长度
    wchar_t*   Buffer; 
} UNICODE_STRING;

  
typedef struct _ANSI_STRING {
    USHORT Length;//字符串长度
    USHORT MaximumLength; //最大长度
    char*   Buffer; 
} ANSI_STRING;
  
  #include <ntifs.h>


NTSTATUS DeviceIrpCtl(PDEVICE_OBJECT device, PIRP pirp)
{
	KdPrint(("yjx:进入派遣函数"));
	PIO_STACK_LOCATION irpStackL;
	ULONG CtlCode;
	ULONG InputBuffLength;


	irpStackL = IoGetCurrentIrpStackLocation(pirp); //获取应用层传来的参数

	switch (irpStackL->MajorFunction)
	{
	case IRP_MJ_DEVICE_CONTROL: //DeviceIoControl
	{
		KdPrint(("yjx:用户层调用了 DeviceIoControl"));
		break;
	}
	case IRP_MJ_CREATE: //CreateFile
	{
		KdPrint(("yjx:用户层调用了 CreateFile"));
		break;
	}
	case IRP_MJ_CLOSE: //CloseHandle
	{
		KdPrint(("yjx:用户层调用了 CloseHandle"));
		break;
	}
	}

	pirp->IoStatus.Status = STATUS_SUCCESS;
	pirp->IoStatus.Information = 4;//返回给DeviceIoControl中的 倒数第二个参数lpBytesReturned
	IoCompleteRequest(pirp, IO_NO_INCREMENT);//调用方已完成所有I/O请求处理操作 并且不增加优先级 
	KdPrint(("yjx:离开派遣函数"));
	return STATUS_SUCCESS;
}

NTSTATUS DeviceIrpCtl_Close(PDEVICE_OBJECT device, PIRP pirp)
{
	KdPrint(("yjx:进入派遣函数 DeviceIrpCtl_Close"));
	PIO_STACK_LOCATION irpStackL;
	ULONG CtlCode;
	ULONG InputBuffLength;


	irpStackL = IoGetCurrentIrpStackLocation(pirp); //获取应用层传来的参数

  	KdPrint(("yjx:用户层调用了 CloseHandle DeviceIrpCtl_Close"));
	 

	pirp->IoStatus.Status = STATUS_SUCCESS;
	pirp->IoStatus.Information = 4;//返回给DeviceIoControl中的 倒数第二个参数lpBytesReturned
	IoCompleteRequest(pirp, IO_NO_INCREMENT);//调用方已完成所有I/O请求处理操作 并且不增加优先级 
	KdPrint(("yjx:离开派遣函数 DeviceIrpCtl_Close"));
	return STATUS_SUCCESS;
}

//创建驱动设备对象
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(("yjx:驱动设备对象创建成功，OK \n"));
		//创建符号链接
		UNICODE_STRING uzSymbolName; //符号链接名字		 
		RtlInitUnicodeString(&uzSymbolName, L"\\??\\MyDriver"); //CreateFile
		status = IoCreateSymbolicLink(&uzSymbolName, &MyDriver);
		if (status == STATUS_SUCCESS)
		{
			KdPrint(("yjx:创建符号链接 %wZ 成功 ", &uzSymbolName));
		}
		else
		{
			KdPrint(("yjx:创建符号链接 %wZ 失败 status=%X", &uzSymbolName, status));
		}
	}
	else
	{

		KdPrint(("yjx:驱动设备对象创建失败，删除设备\n"));
		IoDeleteDevice(device);
	}
	return status;
}

void DriverUnLoad(PDRIVER_OBJECT pDriver)
{
	KdPrint(("yjx:进入了 DriverUnLoad例程"));
	if (pDriver->DeviceObject)
	{
	
		//删除符号链接
	 
		UNICODE_STRING uzSymbolName; //符号链接名字		 
		RtlInitUnicodeString(&uzSymbolName, L"\\??\\MyDriver"); //CreateFile
		KdPrint(("yjx:删除符号链接=%wZ",&uzSymbolName));
		IoDeleteSymbolicLink(&uzSymbol);
		//
		KdPrint(("yjx:删除驱动设备"));
		IoDeleteDevice(pDriver->DeviceObject);//删除设备对象

	}
	KdPrint(("yjx:退出 DriverUnLoad例程"));
}
NTSTATUS DriverEntry(PDRIVER_OBJECT driver,PUNICODE_STRING szReg)
{
	KdPrint(("yjx:我的第一个驱动,注册路径=%wZ", szReg));
	driver->DriverUnload = DriverUnLoad;
	//为驱动对象创建一个设备
	NTSTATUS status = CreateDevice(driver);//
	//注册IRP处理 例程
	driver->MajorFunction[IRP_MJ_CREATE] = DeviceIrpCtl; //CreateFile
	driver->MajorFunction[IRP_MJ_CLOSE] = DeviceIrpCtl_Close;//卸载驱动 CloseHandle
	driver->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceIrpCtl;//DeviceIoControl
	return STATUS_SUCCESS;
}


	DeviceHandle = CreateFileW(
		L"\\??\\MyDriver",
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
		
		
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"

