驱动-与3环程序通讯

3环程序代码

#include <stdio.h>
#include <windows.h>

// 定义自定义消息
// 参数:设备类型,IOCTL码(自定义),读写方式,权限
#define BufferWay CTL_CODE( FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS )
#define DirectWay CTL_CODE( FILE_DEVICE_UNKNOWN, 0x802, METHOD_IN_DIRECT, FILE_ANY_ACCESS )
#define NeitherWay CTL_CODE( FILE_DEVICE_UNKNOWN, 0x803, METHOD_NEITHER, FILE_ANY_ACCESS )

int main()
{
	// 可以使用 CreateFile 符号名去打开一个设备对象,要求管理员权限
	HANDLE DeviceHandle = CreateFile(L"\\\\.\\chengsym", GENERIC_ALL,
		NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	char buff[100] = { "1234" };
	char buff1[100] = {"7890"};
	DWORD bytes = 0;
	// 向设备对象发送消息
	// 句柄-通知码-输入缓冲区-输入缓冲区大小-输出缓冲区
	// -输出缓冲区大小-实际传输的字节数-是否是OVERLAPPED
	DeviceIoControl(DeviceHandle, BufferWay, buff, 100, buff1, 100, &bytes, NULL);
	// 输出结果
	printf("buff:%s\n", buff);
	printf("buff1:%s\n", buff1);
	printf("bytes:%d\n", bytes);

	// 关闭句柄,会相应到 IRP 的 CLOSE 消息
	CloseHandle(DeviceHandle);

	system("pause");
	return 0;
}

0环驱动代码

#include <ntddk.h>

// 设备类型,IOCTL码,读写方式,权限
#define BufferWay CTL_CODE( FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS )
#define DirectWay CTL_CODE( FILE_DEVICE_UNKNOWN, 0x802, METHOD_IN_DIRECT, FILE_ANY_ACCESS )
#define NeitherWay CTL_CODE( FILE_DEVICE_UNKNOWN, 0x803, METHOD_NEITHER, FILE_ANY_ACCESS )

// 创建设备对象,接收的参数是所属的驱动对象
NTSTATUS CreateDevice(PDRIVER_OBJECT DriverObject)
{
	// 创建需要用到的一些变量
	NTSTATUS nstatus = STATUS_SUCCESS;
	PDEVICE_OBJECT DeviceObject = NULL;

	// 初始化设备对象的名称,名称必须是 \\Device\\xxx 的形式
	UNICODE_STRING DeviceName =RTL_CONSTANT_STRING(L"\\Device\\cheng");
	nstatus = IoCreateDevice(
		DriverObject,			// 所属驱动对象,创建后会被添加到它的 DeviceObject 链表中
		0,						// 设备的扩展空间大小,分配的空间会被 DeviceExtension 指向
		&DeviceName,			// 设备对象的名称
		FILE_DEVICE_UNKNOWN,	// 一般说明它是一个和硬件无关的虚拟设备
		0,						// 设备的属性信息
		TRUE,					// 是否独占 \ R3是否可访问
		&DeviceObject);			// 创建出的设备对象被保存到的地方

	if (!NT_SUCCESS(nstatus))
	{
		KdPrint(("设备对象创建失败"));
		return nstatus;
	}

	// 设备对象的名称只能被内核程序解析,为了让R3应用识别
	//	需要设置符号链接名: \\DosDevices\\xxx 或 \\??\\xxx
	UNICODE_STRING SymLinkName = RTL_CONSTANT_STRING(L"\\??\\chengsym");

	//创建符号与设备进行连接
	nstatus = IoCreateSymbolicLink(&SymLinkName, &DeviceName);
	if (!NT_SUCCESS(nstatus))
	{
		IoDeleteDevice(DeviceObject);
		KdPrint(("符号连接失败"));
		return nstatus;
	}

	// 创建成功后,可以通过设置 Flags 指定读写的方式,如果没有指定
	//	读写方式,就会采用两者都不的形式,此时访问的直接是用户空间

	return nstatus;
}

// 用于实现默认的消息派遣函数
NTSTATUS DefaultDispatch(
	PDEVICE_OBJECT DeviceObject,		// 表示当前的消息是那个设备对象产生的
	PIRP Irp)							// IRP,对应的是三环程序的消息,保存了一些附加参数
{
	UNREFERENCED_PARAMETER(DeviceObject);

	// 设置消息的处理状态: 成功或失败 -> GetLastError
	Irp->IoStatus.Status = STATUS_SUCCESS;

	// 设置消息处理的内容长度: ReadFile 或者 WriteFIle 的实际操作长度
	Irp->IoStatus.Information = 0;

	// 通知操作已经完成,完成后不提高当前的 IRQL
	IoCompleteRequest(Irp, IO_NO_INCREMENT);

	// 返回当前处理的整体结果是成功还是失败
	return STATUS_SUCCESS;
}


// 用于实现自定义的消息派遣函数
NTSTATUS DeviceIoControlDispatch(
	PDEVICE_OBJECT DeviceObject,		// 表示当前的消息是那个设备对象产生的
	PIRP Irp)							// IRP,对应的是三环程序的消息,保存了一些附加参数
{
	UNREFERENCED_PARAMETER(DeviceObject);
	PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);

	//读取内容
	PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
	KdPrint(("Length: %d\n", IrpStack->Parameters.Read.Length));
	// 可以通过 irp 栈获取到 DeviceIoControl 的通知码
	switch (Stack->Parameters.DeviceIoControl.IoControlCode)
	{
	//使用BUFFER方式读取
	case BufferWay:
		DbgBreakPoint();
		// 当用户层调用 ReadFile 函数的时候,用户层的写入的内容
		//	会被拷贝到 Irp->AssociatedIrp.SystemBuffer 空间中
		KdPrint(("Buffer: %s\n", Irp->AssociatedIrp.SystemBuffer));
		RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, "hello", 6);


		// 设置实际读取到的字节数量
		Irp->IoStatus.Information = 14;

		// 设置消息的处理状态: 成功或失败 -> GetLastError
		Irp->IoStatus.Status = STATUS_SUCCESS;

		// 设置消息处理的内容长度: ReadFile 或者 WriteFIle 的实际操作长度
		Irp->IoStatus.Information = 4;

		// 通知操作已经完成,完成后不提高当前的 IRQL
		IoCompleteRequest(Irp, IO_NO_INCREMENT);

		// 返回当前处理的整体结果是成功还是失败
		return STATUS_SUCCESS;
		break;
		
	//使用MDL方式读取
	case DirectWay:
		DbgBreakPoint();
		// 当用户层调用 ReadFile 函数的时候,用户层传入的缓冲区
		// 会被重新通过 MDL 进行关联,需要我们自己获取地址 获取缓冲区并输出
		CHAR* Buffer = (CHAR*)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
		KdPrint(("Buffer: %s\n", Buffer));

		// 设置实际读取到的字节数量
		Irp->IoStatus.Information = strlen(Buffer) + 1;

		// 设置消息的处理状态: 成功或失败 -> GetLastError
		Irp->IoStatus.Status = STATUS_SUCCESS;

		// 通知操作已经完成,完成后不提高当前的 IRQL
		IoCompleteRequest(Irp, IO_NO_INCREMENT);

		// 返回当前处理的整体结果是成功还是失败
		return STATUS_SUCCESS;
		break;
		
	// 使用两者都不得方式读取
	case NeitherWay:
		DbgBreakPoint();
		__try {
			RtlCopyMemory(Irp->UserBuffer, "hello", 6);
		}
		__except (EXCEPTION_EXECUTE_HANDLER) {
			KdPrint(("产生了异常,捕获了就不蓝屏,否则蓝屏"));
		}

		// 设置实际读取到的字节数量
		Irp->IoStatus.Information = 14;


		UNREFERENCED_PARAMETER(DeviceObject);

		// 设置消息的处理状态: 成功或失败 -> GetLastError
		Irp->IoStatus.Status = STATUS_SUCCESS;

		// 设置消息处理的内容长度: ReadFile 或者 WriteFIle 的实际操作长度
		Irp->IoStatus.Information = 5;

		// 通知操作已经完成,完成后不提高当前的 IRQL
		IoCompleteRequest(Irp, IO_NO_INCREMENT);

		// 返回当前处理的整体结果是成功还是失败
		return STATUS_SUCCESS;
		break;
	}

	// 设置消息的处理状态: 成功或失败 -> GetLastError
	Irp->IoStatus.Status = STATUS_SUCCESS;

	// 设置消息处理的内容长度: ReadFile 或者 WriteFIle 的实际操作长度
	Irp->IoStatus.Information = 0;

	// 通知操作已经完成,完成后不提高当前的 IRQL
	IoCompleteRequest(Irp, IO_NO_INCREMENT);

	// 返回当前处理的整体结果是成功还是失败
	return STATUS_SUCCESS;
}

// 驱动卸载的回调函数,会在卸载的时候被调用
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
	// 删除设备对象的时候,必须先删符号名,再删设备对象
	UNICODE_STRING SymLinkName;
	RtlInitUnicodeString(&SymLinkName, L"\\??\\chengsym");
	//删除符号
	IoDeleteSymbolicLink(&SymLinkName);

	// 可以通过驱动对象的链表找到设备对象,进行删除
	IoDeleteDevice(DriverObject->DeviceObject);

	KdPrint(("down...\n"));
	UNREFERENCED_PARAMETER(DriverObject);
}

// 2. 提供驱动程序的入口函数
NTSTATUS DriverEntry(
	PDRIVER_OBJECT DriverObject,		// 驱动对象,类似实例句柄
	PUNICODE_STRING RegistryPath)		// 字符串指针,保存驱动在注册表的路径
{
	//	参数可以通过下面的宏解除警告。
	UNREFERENCED_PARAMETER(RegistryPath);
	UNREFERENCED_PARAMETER(DriverObject);
	
	// 通过 DbgBreakPoint 可以为驱动程序设置一个断点
	//DbgBreakPoint();
	
	//设置回调函数
	DriverObject->DriverUnload = DriverUnload;


	// 当驱动程序处于 Check 版本时,进行输出
	KdPrint(("up...\n"));

	// 创建设备对象
	CreateDevice(DriverObject);

	// 为所有的 IRP 消息设置默认的派遣函数
	for (int i = 0; i < 28; ++i)
		DriverObject->MajorFunction[i] = DefaultDispatch;

	// 创建设备对象之后,可以通过驱动对象设置消息的派遣函数(消息响应)
	//	消息是由设备对象产生的,但统一由驱动对象进行处理,对于下面的
	//	几个消息,如果不提供,就不能完成相应的函数调用
	DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceIoControlDispatch;	// DeviceIoControl(自定义消息)

	// 3. 必须需要提供一个返回值,如果返回成功
	//	那么就会加载成功,其他值都是加载失败
	return STATUS_SUCCESS;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值