添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

内存空间分为用户层和系统层,普通的应用程序只能运行在用户层,为了可以操作系统层的内存 所以引入了驱动程序,有了驱动就可以通过用户层来操作系统层的内存及函数,所以驱动就是应用层和系统层之间的一个桥梁

在应用层通过创建符号链接,自动产生驱动层的IRP事件,即可执行系统层的IRP函数,从而将应用层的数据传到系统层。

首先加载驱动使得系统层存在一个符号链接,然后应用层就可以创建跟系统层同名的符号链接

其实本质上是驱动加载完成时会产生一块共享内存用于R3和R0数据交换,控制码用于控制读写哪块内存

R0创建驱动对象->R0创建驱动设备->R0创建符号链接->R3打开符号链接->R3传入控制码(读、写)->R0执行IRP函数 -> R0根据控制码判断读写哪块共享内存->R3收到R0的读写结果 ->R0删除符号链接 ->R0删除驱动设备

#include<ntifs.h>
//控制码与用户层保持一致
#define ReadCtl  CTL_CODE(FILE_DEVICE_UNKNOWN,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS) //读控制码
#define WriteCtl CTL_CODE(FILE_DEVICE_UNKNOWN,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS) //写控制码
#define RWCtl    CTL_CODE(FILE_DEVICE_UNKNOWN,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS) //读写控制码
void IRP_IO_Read(PIRP pirp)
	char* buff = (char*)pirp->AssociatedIrp.SystemBuffer;
	//获取R3传来的参数(控制码)
	PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(pirp);
	//将R0读取到的数据写入到向共享缓冲区
	char R0returnbuf[] = "zxxx R0 read data \n";
	ULONG len = sizeof(R0returnbuf);
	memcpy_s(buff, len, R0returnbuf, len);
	KdPrint(("zxxx IRP_IO_Read read data to SystemBuffer \n"));
	//每次IRP执行完了 要执行下面三行 作为返回结果
	pirp->IoStatus.Status = STATUS_SUCCESS;
	pirp->IoStatus.Information = len;  //共享缓冲区返回的长度
	IoCompleteRequest(pirp, IO_NO_INCREMENT);
void IRP_IO_Write(PIRP pirp)
void IRP_IO_ReadWrite(PIRP pirp)
//创建驱动对象->创建驱动设备->创建符号链接->使用符号链接  ->删除符号链接 ->删除驱动设备
//当用户层打开符号链接时 会产生IRP事件执行IRP函数 通过IRP函数与内核通信
//创建驱动对象并绑定符号链接
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)
		KdPrint(("zxxx 驱动设备对象创建成功 \n"));
		//创建符合链接
		UNICODE_STRING uzSymbolName;
		RtlInitUnicodeString(&uzSymbolName,L"\\??\\MyDriver"); //初始化符号链接 符号链接格式 L"\\??\\名字
		//为驱动设备绑定符号链接    后续不会使用驱动对象与内核交换,而是使用符号链接与内核交换
		status = IoCreateSymbolicLink(&uzSymbolName,&MyDriver);
		if (status == STATUS_SUCCESS)
			KdPrint(("zxxx 符号链接创建成功 %wZ \n",&uzSymbolName));
			KdPrint(("zxxx 符号链接创建失败 %wZ \n", &uzSymbolName));
		KdPrint(("zxxx 驱动设备对象创建失败 \n"));
		IoDeleteDevice(device);
	return status;
//传入驱动设备的IRP事件
NTSTATUS IRP_CALL(PDEVICE_OBJECT device,PIRP pirp)
	device;
	KdPrint(("zxxx 发生IRP事件 进入IRP函数 \n"));
	PIO_STACK_LOCATION irpStackL;
	irpStackL = IoGetCurrentIrpStackLocation(pirp);
	switch (irpStackL->MajorFunction)
		case IRP_MJ_CREATE:
			KdPrint(("zxxx IRP_MJ_CREATE \n"));
			break;
		case IRP_MJ_CLOSE:
			KdPrint(("zxxx IRP_MJ_CLOSE \n"));
			break;
		case IRP_MJ_DEVICE_CONTROL:
			KdPrint(("zxxx IRP_MJ_DEVICE_CONTROL \n"));
			//取到的R3的控制码
			UINT32 CtlCode = irpStackL->Parameters.DeviceIoControl.IoControlCode;
			KdPrint(("zxxx IRP_MJ_DEVICE_CONTROL R0控制码:%X \n", CtlCode));
			if ( CtlCode  == ReadCtl )
				KdPrint(("zxxx IRP_MJ_DEVICE_CONTROL ReadCtl R0控制码:%X \n", CtlCode));
				IRP_IO_Read(pirp); //这里写入到共享缓冲剂即可,打印R3访问共享缓冲区打印
				return STATUS_SUCCESS;
			else if ( CtlCode == WriteCtl )
				KdPrint(("zxxx IRP_MJ_DEVICE_CONTROL WriteCtl R0控制码:%X \n", CtlCode));
				//取出R3缓冲区的数据
				//根据控制代码来选择使用AssociatedIrp.SystemBuffer的读缓冲区还是写缓冲区
				char* R3buff = (char*)pirp->AssociatedIrp.SystemBuffer;
				KdPrint(("zxxx IRP_MJ_DEVICE_CONTROL R0缓冲区:%s \n", R3buff));
			else if (CtlCode == RWCtl )
				KdPrint(("zxxx IRP_MJ_DEVICE_CONTROL RWCtl R0控制码:%X \n", CtlCode));
			break;
	//注意 只要pirp这个对象发生变化 就要跟着下面这三行 
	pirp->IoStatus.Status = STATUS_SUCCESS;
	pirp->IoStatus.Information = 4;
	IoCompleteRequest(pirp,IO_NO_INCREMENT);
	KdPrint(("zxxx 结束IRP事件 离开IRP函数 \n"));
	return STATUS_SUCCESS;
//卸载驱动
void Unload(PDRIVER_OBJECT pDriver)
	KdPrint(("zxxx unload  %p \n", pDriver));
	//先删除符号链接
	//再删除驱动设备
	if (pDriver->DeviceObject)
		UNICODE_STRING uzSymbolName;
		RtlInitUnicodeString(&uzSymbolName, L"\\??\\MyDriver");
		IoDeleteSymbolicLink(&uzSymbolName);
		IoDeleteDevice(pDriver->DeviceObject);
		KdPrint(("zxxx 删除符号链接 \n"));
		KdPrint(("zxxx 删除驱动设备 \n"));
NTSTATUS
DriverEntry(
	_In_ PDRIVER_OBJECT DriverObject,
	_In_ PUNICODE_STRING RegistryPath
	//初始化驱动对象
	DriverObject->DriverUnload = Unload;					//指定卸载驱动函数
	DriverObject->MajorFunction[IRP_MJ_CREATE] = IRP_CALL;	//指定IRP事件函数
	DriverObject->MajorFunction[IRP_MJ_CLOSE] = IRP_CALL;   //指定IRP事件函数
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IRP_CALL;   //指定IRP事件函数
	//创建驱动设备
	CreateDevice(DriverObject);
	RegistryPath;
	KdPrint(("zxxx entry  \n"));
	return 0;

应用层(MFC)

//通过不同的控制码让驱动执行不同的函数
//控制码在用户层和驱动层都需要定义且保持一致
#include<winioctl.h>
#define ReadCtl  CTL_CODE(FILE_DEVICE_UNKNOWN,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS) //读控制码
#define WriteCtl CTL_CODE(FILE_DEVICE_UNKNOWN,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS) //写控制码
#define RWCtl    CTL_CODE(FILE_DEVICE_UNKNOWN,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS) //读写控制码
static HANDLE DeviceHandle = NULL;
//打开驱动按钮
void CMFCApplication1Dlg::OnBnClickedButton1()
	// TODO: 在此添加控件通知处理程序代码
	//创建驱动
	//执行 CreateFileW 会触发IRP事件 执行IRP函数 IRP_MJ_CREATE被执行
	DeviceHandle = CreateFileW(
		L"\\??\\MyDriver",		//符号链接
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL,
//关闭驱动按钮
void CMFCApplication1Dlg::OnBnClickedButton2()
	// TODO: 在此添加控件通知处理程序代码
	//执行 CloseHandle 会触发IRP事件 执行IRP函数 IRP_MJ_CLOSE被执行
	CloseHandle(DeviceHandle);
//读数据按钮
void CMFCApplication1Dlg::OnBnClickedButton3()
	// TODO: 在此添加控件通知处理程序代码
	DWORD dwRetSize = 0;
	typedef struct TINPUT_BUF
		DWORD m_arg1;
		DWORD m_arg2;
		DWORD m_arg3;
		DWORD m_arg4;
		DWORD m_arg5;
		DWORD m_arg6;
	}TINPUT_BUF;
	//打印控制码 测试 验证R3与R0是否一致
	char buftest[256];
	sprintf_s(buftest, "zxxx R3 控制码:%x", ReadCtl);
	OutputDebugStringA(buftest);
	char WriteData[100] = "zxxx R3 DeviceIoControl read test \n";
	TINPUT_BUF inBuf = { 0 };    //写数据 R3的数据写入到R0
	CHAR OutBuf[512] = { 0 };	//输出缓冲区	   //读数据 R0的数据读出到R3
	//IRP函数DeviceIoControl  
	//这里的控制码+缓冲区 指明了R3读(写)了哪块共享内存,后续驱动用控制码即可执行对应的读写操作
	DeviceIoControl(
		DeviceHandle,		//CreateFile 打开驱动设备返回的句柄
		ReadCtl,			//控制码 CTL_CODE  与IRP事件对应
		WriteData,			//输入缓冲区 &inBuf
		sizeof(inBuf),		//输入缓冲区大小
		&OutBuf,			//输出缓冲区
		512,		//输出缓冲区大小
		&dwRetSize,			//返回字节数
	//打印返回参数
	CString csStr;
	csStr.Format(L"zxxx R3读到的数据 %x \n", OutBuf[0]);
	OutputDebugStringA("zxxx R3读到的数据 \n");
	strcat_s(OutBuf," zxxx R3");
	//R3读取到的结果
	OutputDebugStringA(OutBuf);
//写数据按钮
void CMFCApplication1Dlg::OnBnClickedButton4()
	// TODO: 在此添加控件通知处理程序代码
	DWORD dwRetSize = 0;
	typedef struct TINPUT_BUF
		DWORD m_arg1;
		DWORD m_arg2;
		DWORD m_arg3;
		DWORD m_arg4;
		DWORD m_arg5;
		DWORD m_arg6;
	}TINPUT_BUF;
	//打印控制码 测试 验证R3与R0是否一致
	char buftest[256];
	sprintf_s(buftest,"zxxx R3 控制码:%x", WriteCtl);
	OutputDebugStringA(buftest);
	char WriteData[100] = "zxxx R3 DeviceIoControl write test \n";
	//打印缓冲区 测试 验证R3与R0是否一致
	char buftest2[100];
	memcpy(buftest2, WriteData,100);
	OutputDebugStringA(buftest2);
	TINPUT_BUF inBuf = { 1,2,3,4,5,0x6ABC666 };    //写数据 R3的数据写入到R0
	DWORD OutBuf[6] = { 0 };	//输出缓冲区	   //读数据 R0的数据读出到R3
	//IRP函数DeviceIoControl  
	//这里的控制码+缓冲区 指明了R3读(写)了哪块共享内存,后续驱动使用控制码即可执行对应的读写操作
	DeviceIoControl(
		DeviceHandle,		//CreateFile 打开驱动设备返回的句柄
		WriteCtl,			//控制码 CTL_CODE  与IRP事件对应
		WriteData,			//输入缓冲区 &inBuf
		sizeof(inBuf),		//输入缓冲区大小
		&OutBuf,			//输出缓冲区
		sizeof(OutBuf),		//输出缓冲区大小
		&dwRetSize,			//返回字节数

 MFC一些配置问题:

直接设备读写 其原理是锁定用户空间内存(这解决了分页内存置换导致缺页异常的问题)并将其映射到内核空间地址,直接对该内核空间地址进行写入即可,由于进程的内核空间是共享的。所以属于任何进程的线程都可以写入。 注意的点: 1. 要为设备添加DO_DIRECT_IO标志 2. 为R3下ReadFile和WriteFile派遣函数时获取对对应传入参数 a. 其中WriteFile的用户输入内存地址和ReadFile的设备输出地址通过MmGetSystemAddressForMdlSafe函数锁定IRP.
在上一节课我们证实了在用户层调用CreateFile函数时,相应的在驱动层会响应一个IRP_MJ_CREATE的事件。 这节课我们来看看用户层和驱动层是怎么交换数据的。 首先来介绍一下控制码,由CTL_CODE宏创建,是一个唯一的32位系统I/O控制代码,这个控制代码包括4部分组成: DeviceType(设备类型,高16位(16-31位)), Function(功能 2-13位), Method(I/O传递的方式),有4种(METHOD_BUFFERED,METHOD_IN_DIRECT,METHOD_