当前位置: 首页 > article >正文

阶段性demo 键盘信息过滤

写在前面的话

本来想按照理论+实践的方式继续向下写文章,但是写来写去发现这一阶段涉及的知识实在是太多太杂,如果什么都事无巨细的去讲解,那么这样反倒会让文章变得杂乱,所以我打算直接上一个demo代码,然后根据这个demo代码来补全我们现阶段应该知道的知识(不一定理解深入,先会用

demo

代码先放在最上面,但是这个代码是有问题的

重要的话说三遍!但是这个代码是有问题的!

重要的话说三遍!但是这个代码是有问题的!

重要的话说三遍!但是这个代码是有问题的!

如果是正常的键盘记录,安装驱动和运行服务都是没有问题的,但是一旦停止服务,就会蓝屏(访问缺页),疑似是因为没有正确的处理Irp导致卸载了驱动之后还在访问缺页地址,但是限于本人目前水平,没有办法得出具体的原因,这也不会是教学的终点,所以就先把代码作为示例来讲解即可

#include<ntifs.h>
#define MY_COMPONENT_ID 123
#define MY_INFO_LEVEL DPFLTR_INFO_LEVEL
#define MY_ERROR_LEVEL DPFLTR_ERROR_LEVEL

//键盘输入包
typedef struct _KEYBOARD_INPUT_DATA {
	USHORT UnitId;
	USHORT MakeCode;
	USHORT Flags;
	USHORT Reserved;
	ULONG  ExtraInformation;
} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;

//设备扩展结构
typedef struct _Dev_exten {
	ULONG Size;						//该结构的大小
	PDEVICE_OBJECT FilterDevice;	//过滤设备对象
	PDEVICE_OBJECT TargetDevice;	//目标设备对象
	PDEVICE_OBJECT LowDevice;		//低级设备对象
	KSPIN_LOCK IoRequestSpinLock;	//自旋锁
	KEVENT IoProgressEvent;			//事件
	PIRP pIrp;						//IRP
}DEV_EXTENSION, *PDEV_EXTENSION;

NTSTATUS DeAttach(PDEVICE_OBJECT pDevice) {
	PDEV_EXTENSION devExt;
	devExt = (PDEV_EXTENSION)pDevice->DeviceExtension;

	if (devExt->TargetDevice) {
		IoDetachDevice(devExt->TargetDevice);
		devExt->TargetDevice = NULL;
	}

	if (devExt->FilterDevice) {
		devExt->FilterDevice = NULL;
	}

	if (devExt->pIrp != NULL) {
#ifdef DEBUG
		DbgBreakPoint();
#endif
		if (IoCancelIrp(devExt->pIrp)) {
			DbgPrintEx(MY_COMPONENT_ID, MY_INFO_LEVEL, "取消成功...\r\n");
		}
		else {
			DbgPrintEx(MY_COMPONENT_ID, MY_ERROR_LEVEL, "取消失败...\r\n");
		}

		// 释放 IRP 的内存
		IoFreeIrp(devExt->pIrp);
		devExt->pIrp = NULL;
	}

	// 删除设备对象
	IoDeleteDevice(pDevice);

	return STATUS_SUCCESS;
}

//设备卸载函数
NTSTATUS DriverUnload(PDRIVER_OBJECT pDriver) {
	PDEVICE_OBJECT pDevice;
	PDEV_EXTENSION devExt;

	UNREFERENCED_PARAMETER(pDriver);
	DbgPrint("Driver Unloading..\r\n");
	//DbgBreakPoint();

	pDevice = pDriver->DeviceObject;
	while (pDevice) {
		DeAttach(pDevice);
		pDevice = pDevice->NextDevice;
	}
	pDriver->DeviceObject = NULL;
	//DbgBreakPoint();
	return STATUS_SUCCESS;
}


extern POBJECT_TYPE *IoDriverObjectType;

NTSTATUS ObReferenceObjectByName(
	PUNICODE_STRING   ObjectName,
	ULONG            Attributes,
	PACCESS_STATE    PassedAccessState,
	ACCESS_MASK      DesiredAccess,
	POBJECT_TYPE     ObjectType,
	KPROCESSOR_MODE  AccessMode,
	PVOID            ParseContext,
	PVOID            *Object
);


//设备操作通用分发函数
NTSTATUS GeneralDispatch(PDEVICE_OBJECT pDevice, PIRP pIrp) {
	NTSTATUS status;

	PDEV_EXTENSION devExt = (PDEV_EXTENSION)pDevice->DeviceExtension;
	PDEVICE_OBJECT lowDevice = devExt->LowDevice;
	IoSkipCurrentIrpStackLocation(pIrp);
	status = IoCallDriver(lowDevice, pIrp);
	return status;
}

//即插即用IRP分发函数
NTSTATUS PnpDispatch(PDEVICE_OBJECT pDevice, PIRP pIrp) {
	PDEV_EXTENSION devExt;
	PIO_STACK_LOCATION stack;
	NTSTATUS status = STATUS_SUCCESS;

	devExt = (PDEV_EXTENSION)pDevice->DeviceExtension;
	stack = IoGetCurrentIrpStackLocation(pIrp);
	switch (stack->MinorFunction)
	{
	case IRP_MN_REMOVE_DEVICE:
		//向下继续分发请求
		IoSkipCurrentIrpStackLocation(pIrp);
		IoCallDriver(devExt->LowDevice, pIrp);
		//解除绑定
		IoDetachDevice(devExt->LowDevice);
		//删除我们自己生成的设备
		IoDeleteDevice(pDevice);
		status = STATUS_SUCCESS;
		break;
	default:
		//其他类型的IRP,全部正常下发
		IoSkipCurrentIrpStackLocation(pIrp);
		status = IoCallDriver(devExt->LowDevice, pIrp);



	}
	return status;
}

//电源IRP分发函数
NTSTATUS PowerDispatch(PDEVICE_OBJECT pDevice, PIRP pIrp) {
	PDEV_EXTENSION devExt;
	devExt = (PDEV_EXTENSION)pDevice->DeviceExtension;


	PoStartNextPowerIrp(pIrp);
	IoSkipCurrentIrpStackLocation(pIrp);
	return PoCallDriver(devExt->TargetDevice, pIrp);
}


NTSTATUS DevExtInit(PDEV_EXTENSION devExt, PDEVICE_OBJECT filterDevice, PDEVICE_OBJECT targetDevice, PDEVICE_OBJECT lowDevice) {
	memset(devExt, 0, sizeof(DEV_EXTENSION));
	devExt->FilterDevice = filterDevice;
	devExt->TargetDevice = targetDevice;
	devExt->LowDevice = lowDevice;
	devExt->Size = sizeof(DEV_EXTENSION);
	KeInitializeSpinLock(&devExt->IoRequestSpinLock);
	KeInitializeEvent(&devExt->IoProgressEvent, NotificationEvent, FALSE);
	return STATUS_SUCCESS;
}


NTSTATUS AttachDevice(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegPatch) {
	UNICODE_STRING KeyBoardName = RTL_CONSTANT_STRING(L"\\Driver\\KbdClass");
	NTSTATUS status = 0;
	PDEVICE_OBJECT filterDevice;//过滤设备
	PDEV_EXTENSION devExt;//过滤设备的扩展
	PDEVICE_OBJECT targetDevice;// 目标设备
	PDEVICE_OBJECT	lowDevice;// 低级设备
	PDRIVER_OBJECT khdDriver;//用于接受所查询对象的指针


	//获取键盘的驱动对象,保存在khdDriver里面
	status = ObReferenceObjectByName(&KeyBoardName, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, &khdDriver);

	if (!NT_SUCCESS(status)){
		DbgPrint("Open KeyBoard Driver Failed\r\n");
		return status;
	}
	else {
		ObDereferenceObject(khdDriver);

		//获取第一个设备
		targetDevice = khdDriver->DeviceObject;
		//遍历驱动中的设备
		while (targetDevice) {
			//创建一个过滤设备
			status = IoCreateDevice(pDriver, sizeof(DEV_EXTENSION), NULL, targetDevice->DeviceType, targetDevice->Characteristics, FALSE, &filterDevice);
			if (!NT_SUCCESS(status)) {
				DbgPrint("Create FilterDevice Error\r\n");
				filterDevice = targetDevice = NULL;
				return status;
			}
			//绑定下一个设备
			lowDevice = IoAttachDeviceToDeviceStack(filterDevice, targetDevice);
			if (!lowDevice) {
				DbgPrint("Attach targetDevice Error\r\n");
				IoDeleteDevice(filterDevice);
				filterDevice = NULL;
				return STATUS_UNSUCCESSFUL;
			}
			//初始化设备的扩展
			devExt = (PDEV_EXTENSION)filterDevice->DeviceExtension;
			DevExtInit(devExt, filterDevice, targetDevice, lowDevice);
			
			filterDevice->StackSize = lowDevice->StackSize + 1;//栈大小要自己加一,因为没有框架帮我们,所以要手工来
			filterDevice->Flags |= lowDevice->Flags&(DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);
			//遍历下一个设备
			targetDevice = targetDevice->NextDevice;
		}
	}
	

}

//读取回调函数
NTSTATUS ReadComp(PDEVICE_OBJECT pDriver, PIRP pIrp, PVOID Context) {
	NTSTATUS status;
	PIO_STACK_LOCATION stack;
	ULONG KeyNumber;
	PKEYBOARD_INPUT_DATA MyData;
	KAPC_STATE kApcState = { 0 };
	PEPROCESS Process = NULL;
	stack = IoGetCurrentIrpStackLocation(pIrp);
	if (NT_SUCCESS(pIrp->IoStatus.Status)) {
		//获取键盘数据
		do {
			MyData = pIrp->AssociatedIrp.SystemBuffer;
			KeyNumber = (ULONG)(pIrp->IoStatus.Information / sizeof(PKEYBOARD_INPUT_DATA));
			for (ULONG i = 0; i < KeyNumber; i++) {
				DbgPrint("NumKey:%d,code:%x,%s\n", KeyNumber, MyData->MakeCode, MyData->Flags?"Up":"Down");

			}
			MyData++;
		} while (0);
		if (pIrp->PendingReturned) {
			IoMarkIrpPending(pIrp);
		}
		return pIrp->IoStatus.Status;
	}
}

//IRP读分发函数
NTSTATUS ReadDispatch(PDEVICE_OBJECT pDevice, PIRP pIrp) {
	NTSTATUS status = STATUS_SUCCESS;
	PDEV_EXTENSION devExt;
	PDEVICE_OBJECT lowDevice;
	PIO_STACK_LOCATION stack;
	//如果当前栈为1,那么说明我们是最底层的一个栈,所以对这种请求我们直接过滤
	if (pIrp->CurrentLocation == 1) {
		DbgPrint("Irp send Error\r\n");
		status = STATUS_INVALID_DEVICE_REQUEST;
		pIrp->IoStatus.Status = status;
		pIrp->IoStatus.Information = 0;
		IoCompleteRequest(pIrp, IO_NO_INCREMENT);
		return status;
	}
	 //得到设备扩展,目的是为了得到下一个设备的指针
	devExt = pDevice->DeviceExtension;
	if (!devExt) {
		DbgPrint("Get Extension Error\r\n");
		return STATUS_UNSUCCESSFUL;
	}
	lowDevice = devExt->LowDevice;

	//复制Irp栈
	IoCopyCurrentIrpStackLocationToNext(pIrp);
	//设置IRP完成回调函数
	IoSetCompletionRoutine(pIrp, ReadComp, pDevice, TRUE, TRUE, TRUE);
	status = IoCallDriver(lowDevice, pIrp);
	return status;

}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegPath) {
	ULONG Num;
	NTSTATUS status = STATUS_SUCCESS;
	pDriver->DriverUnload = DriverUnload;

	for (Num = 0; Num < IRP_MJ_MAXIMUM_FUNCTION; Num++) {//首先将所有的IRP派遣函数赋一个默认分发的值
		pDriver->MajorFunction[Num] = GeneralDispatch;
	}

	pDriver->MajorFunction[IRP_MJ_READ] = ReadDispatch; // 注册IRP读时函数
	pDriver->MajorFunction[IRP_MJ_POWER] = PowerDispatch;// 注册电源IRP分发函数
	pDriver->MajorFunction[IRP_MJ_PNP] = PnpDispatch; // 注册即插即用IRP分发函数
	
	AttachDevice(pDriver, RegPath);
	//绑定设备
	return STATUS_SUCCESS;
}

上面就是完整代码了,之后我会从驱动入口开始介绍,中途补充理论知识

代码分析

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegPath) {
	ULONG Num;
	NTSTATUS status = STATUS_SUCCESS;
	pDriver->DriverUnload = DriverUnload;

	for (Num = 0; Num < IRP_MJ_MAXIMUM_FUNCTION; Num++) {//首先将所有的IRP派遣函数赋一个默认分发的值
		pDriver->MajorFunction[Num] = GeneralDispatch;
	}

	pDriver->MajorFunction[IRP_MJ_READ] = ReadDispatch; // 注册IRP读时函数
	pDriver->MajorFunction[IRP_MJ_POWER] = PowerDispatch;// 注册电源IRP分发函数
	pDriver->MajorFunction[IRP_MJ_PNP] = PnpDispatch; // 注册即插即用IRP分发函数
	
	AttachDevice(pDriver, RegPath);
	//绑定设备
	return STATUS_SUCCESS;
}

上面的代码中,我们首先看见了这么一段代码,他把我们之前所介绍的回调函数全部先填入了一个默认值,为什么要这么做呢?

	for (Num = 0; Num < IRP_MJ_MAXIMUM_FUNCTION; Num++) {//首先将所有的IRP派遣函数赋一个默认分发的值
		pDriver->MajorFunction[Num] = GeneralDispatch;
	}

GeneralDispatch

让我们把目光集中到这里面所赋予的默认函数上,下面用到了一个我们自己定义的DEV_EXTENSION结构,为了方便理解我也先贴出来

//设备操作通用分发函数
NTSTATUS GeneralDispatch(PDEVICE_OBJECT pDevice, PIRP pIrp) {
	NTSTATUS status;

	PDEV_EXTENSION devExt = (PDEV_EXTENSION)pDevice->DeviceExtension;
	PDEVICE_OBJECT lowDevice = devExt->LowDevice;
	IoSkipCurrentIrpStackLocation(pIrp);
	status = IoCallDriver(lowDevice, pIrp);
	return status;
}
//设备扩展结构
typedef struct _Dev_exten {
	ULONG Size;						//该结构的大小
	PDEVICE_OBJECT FilterDevice;	//过滤设备对象
	PDEVICE_OBJECT TargetDevice;	//目标设备对象
	PDEVICE_OBJECT LowDevice;		//低级设备对象
	KSPIN_LOCK IoRequestSpinLock;	//自旋锁
	KEVENT IoProgressEvent;			//事件
	PIRP pIrp;						//IRP
}DEV_EXTENSION, *PDEV_EXTENSION;

先从这个默认结构说起,为了方便我们寻找到当前的设备,当前设备更加低一级的设备,我们自创建的设备这些对象,我们可以创建一个结构把这些信息存储起来,方便我们后面的取用,画个图理解一下

在这里插入图片描述

在理解上面的图之前,首先我们先简单的理解驱动和设备的关系,对于当前阶段的我们来说,只需要知道设备是由驱动创建的就行了。

我们的电脑在底层会适配各种外部设备,但是这些外部设备可能千奇百怪,所遵循的协议等也不一定相同,所以为了能够正确的处理各种外设的I /O请求,win自己是会创建设备来“翻译”各种外设输入的

对于这些设备,他们像是串在一起一样,信息从顶层的设备向下传递(外设->win虚拟设备->win物理设备),所以如果我们能够附加在win的这个“翻译”设备上面,比它更早的收到外设传递来的信息,是不是就可以拦截外设信息了呢?

由此我们便可以讲清楚为什么要记录 _Dev_exten这个结构里面的信息了

回到我们的代码,可以看见我们从这个扩展结构里面取出了当前设备的下级指针,然后我们碰见了一个新函数IoSkipCurrentIrpStackLocation

//设备操作通用分发函数
NTSTATUS GeneralDispatch(PDEVICE_OBJECT pDevice, PIRP pIrp) {
	NTSTATUS status;

	PDEV_EXTENSION devExt = (PDEV_EXTENSION)pDevice->DeviceExtension;
	PDEVICE_OBJECT lowDevice = devExt->LowDevice;
	IoSkipCurrentIrpStackLocation(pIrp);
	status = IoCallDriver(lowDevice, pIrp);
	return status;
}

我们刚刚提到,“信息”是由“设备串”顶端向下一层一层传递的,这是抽象的说法

在具体的代码中,“信息”就是Irp包,“设备串”就是我们的I/O设备栈

Irp和I/O设备栈

I/O request packets,简称IRP。即输入输出请求包。它是WINDOWS内核中的一种非常重要的数据结构。上层应用程序与底层驱动程序通信时,应用程序会发出I/O请求。操作系统将相应的I/O请求转换为相应的IRP。不同的IRP会根据类型被分派的不同的派遣历程中进行处理。

typedef
NTSTATUS
DRIVER_DISPATCH (
    _In_ struct _DEVICE_OBJECT *DeviceObject,
    _Inout_ struct _IRP *Irp
    );

上面是公式化的表达,简单的说,这个Irp包是一个结构体,会带着我们的信息在设备栈中传递

回到我们碰见的陌生函数IoSkipCurrentIrpStackLocation,从意思上我们就可以知道,这是让我们的Irp包从当前设备栈,向下级设备传递。

IoSkipCurrentIrpStackLocation

它是有源码的

VOID
IoSkipCurrentIrpStackLocation (
    _Inout_ PIRP Irp
)
{
    NT_ASSERT(Irp->CurrentLocation <= Irp->StackCount);
    Irp->CurrentLocation++;//本质上就是让Irp包中结构加1
    Irp->Tail.Overlay.CurrentStackLocation++;
}

IoCallDriver

继续看会我们的默认分发函数,又看见一个IoCallDriver函数,它的作用是将 IRP 发送到与指定设备对象关联的驱动程序。

NTSTATUS IofCallDriver(
  PDEVICE_OBJECT        DeviceObject,
  __drv_aliasesMem PIRP Irp
);

所以到现在,我们理清了这个默认函数的逻辑,这就是直接让自己的设备收到Irp包(信息),然后什么也不做,直接转发给下级设备

不难理解,这是因为对于我们当前的需求(读取键盘输入信息)来说,大部分回调我们是不需要改的,就原封不动的将这些调用传给我们的下级设备就行了

回到我们的DriverEntry,我们看见我们改了三个回调的位置,接下来一个一个看

pDriver->MajorFunction[IRP_MJ_READ] = ReadDispatch; // 注册IRP读时函数
	pDriver->MajorFunction[IRP_MJ_POWER] = PowerDispatch;// 注册电源IRP分发函数
	pDriver->MajorFunction[IRP_MJ_PNP] = PnpDispatch; // 注册即插即用IRP分发函数

ReadDispatch

首先是我们的最重要的读分发函数,可以看见,除了常规的初始化之外,我们写了一个错误处理,这个错误处理的意思就是如果当前栈为1(也就是说明我们的接收到的Irp已经发到了底层,但是作为附加设备的我们肯定在栈顶),那么就忽略掉这个I/O请求,用到了IoCompleteRequest这个函数,它就是用来完成Irp包的,没什么好说

//IRP读分发函数
NTSTATUS ReadDispatch(PDEVICE_OBJECT pDevice, PIRP pIrp) {
	NTSTATUS status = STATUS_SUCCESS;
	PDEV_EXTENSION devExt;
	PDEVICE_OBJECT lowDevice;
	PIO_STACK_LOCATION stack;
	//如果当前栈为1,那么说明我们是最底层的一个栈,所以对这种请求我们直接过滤
	if (pIrp->CurrentLocation == 1) {
		DbgPrint("Irp send Error\r\n");
		status = STATUS_INVALID_DEVICE_REQUEST;
		pIrp->IoStatus.Status = status;
		pIrp->IoStatus.Information = 0;
		IoCompleteRequest(pIrp, IO_NO_INCREMENT);
		return status;
	}
	 //得到设备扩展,目的是为了得到下一个设备的指针
	devExt = pDevice->DeviceExtension;
	if (!devExt) {
		DbgPrint("Get Extension Error\r\n");
		return STATUS_UNSUCCESSFUL;
	}
	lowDevice = devExt->LowDevice;
	//复制Irp栈
	IoCopyCurrentIrpStackLocationToNext(pIrp);
	//设置IRP完成回调函数
	IoSetCompletionRoutine(pIrp, ReadComp, pDevice, TRUE, TRUE, TRUE);
	status = IoCallDriver(lowDevice, pIrp);
	return status;

}

继续向下看代码,照常取出了设备扩展,取到了下级设备的指针,然后用IoCopyCurrentIrpStackLocationToNext函数将当前I/O栈里面的信息放到下一个栈中,这是因为我们的键盘Irp包是带参数的,所以要整个复制过去,这就是和之前IoSkipCurrentIrpStackLocation的区别。

然后设置回调函数ReadComp,指定好pIrp和下级设备,然后就是三个True

  • TRUE, TRUE, TRUE
    

    : 这三个布尔参数分别表示:

    • TRUE: 如果I/O操作成功,调用回调函数。
    • TRUE: 如果I/O操作失败,调用回调函数。
    • TRUE: 如果I/O操作被取消,调用回调函数。

做好这些处理之后,我们向下Call设备,当完成I/O请求后,我们的回调函数会被触发

ReadComp

来继续看看我们的回调函数是怎么写的,首先用IoGetCurrentIrpStackLocation函数拿到了当前得到设备栈,然后从我们的Irp的参数区里面的缓冲区拿到了书接,把这个数据打印出来,然后处理挂起的I/O请求,这样就能正确的读取键盘输入了

//读取回调函数
NTSTATUS ReadComp(PDEVICE_OBJECT pDriver, PIRP pIrp, PVOID Context) {
	NTSTATUS status;
	PIO_STACK_LOCATION stack;
	ULONG KeyNumber;
	PKEYBOARD_INPUT_DATA MyData;
	KAPC_STATE kApcState = { 0 };
	PEPROCESS Process = NULL;
	stack = IoGetCurrentIrpStackLocation(pIrp);
	if (NT_SUCCESS(pIrp->IoStatus.Status)) {
		//获取键盘数据
		do {
			MyData = pIrp->AssociatedIrp.SystemBuffer;
			KeyNumber = (ULONG)(pIrp->IoStatus.Information / sizeof(PKEYBOARD_INPUT_DATA));
			for (ULONG i = 0; i < KeyNumber; i++) {
				DbgPrint("NumKey:%d,code:%x,%s\n", KeyNumber, MyData->MakeCode, MyData->Flags?"Up":"Down");

			}
			MyData++;
		} while (0);
		if (pIrp->PendingReturned) {
			IoMarkIrpPending(pIrp);
		}
		return pIrp->IoStatus.Status;
	}
}

PowerDispatch

再看当初DriverEntry里面的另外两个回调函数,这里我们有了之前默认分发的知识后,就可以看出来,结构是差不多的,只不过对于电源I/O请求,Win有自己独特的API,里面的处理更加细致,这里先暂时不细说(我也不会

//电源IRP分发函数
NTSTATUS PowerDispatch(PDEVICE_OBJECT pDevice, PIRP pIrp) {
	PDEV_EXTENSION devExt;
	devExt = (PDEV_EXTENSION)pDevice->DeviceExtension;


	PoStartNextPowerIrp(pIrp);
	IoSkipCurrentIrpStackLocation(pIrp);
	return PoCallDriver(devExt->TargetDevice, pIrp);
}

PnpDispatch

最后一个回调,这是为了方便我们的设备(键盘)在拔掉的时候,同时删除掉我们之前创建的附加的设备

//即插即用IRP分发函数
NTSTATUS PnpDispatch(PDEVICE_OBJECT pDevice, PIRP pIrp) {
	PDEV_EXTENSION devExt;
	PIO_STACK_LOCATION stack;
	NTSTATUS status = STATUS_SUCCESS;

	devExt = (PDEV_EXTENSION)pDevice->DeviceExtension;
	stack = IoGetCurrentIrpStackLocation(pIrp);
	switch (stack->MinorFunction)
	{
	case IRP_MN_REMOVE_DEVICE:
		//向下继续分发请求
		IoSkipCurrentIrpStackLocation(pIrp);
		IoCallDriver(devExt->LowDevice, pIrp);
		//解除绑定
		IoDetachDevice(devExt->LowDevice);
		//删除我们自己生成的设备
		IoDeleteDevice(pDevice);
		status = STATUS_SUCCESS;
		break;
	default:
		//其他类型的IRP,全部正常下发
		IoSkipCurrentIrpStackLocation(pIrp);
		status = IoCallDriver(devExt->LowDevice, pIrp);
	}
	return status;
}

AttachDevice

最后一个在DriverEntry里面的函数,他就是首先用ObReferenceObjectByName(未导出函数,要额外声明),来根据我们的键盘驱动的名字来查询这个驱动对象,然后调用ObDereferenceObject来给我们的对象引用减1,为了后面对象的自动销毁,我们之前说过,在内核层面是不会像R3一样自动释放内存的

NTSTATUS AttachDevice(PDRIVER_OBJECT pDriver, PUNICODE_STRING RegPatch) {
	UNICODE_STRING KeyBoardName = RTL_CONSTANT_STRING(L"\\Driver\\KbdClass");
	NTSTATUS status = 0;
	PDEVICE_OBJECT filterDevice;//过滤设备
	PDEV_EXTENSION devExt;//过滤设备的扩展
	PDEVICE_OBJECT targetDevice;// 目标设备
	PDEVICE_OBJECT	lowDevice;// 低级设备
	PDRIVER_OBJECT khdDriver;//用于接受所查询对象的指针


	//获取键盘的驱动对象,保存在khdDriver里面
	status = ObReferenceObjectByName(&KeyBoardName, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, &khdDriver);

	if (!NT_SUCCESS(status)){
		DbgPrint("Open KeyBoard Driver Failed\r\n");
		return status;
	}
	else {
		ObDereferenceObject(khdDriver);

		//获取第一个设备
		targetDevice = khdDriver->DeviceObject;
		//遍历驱动中的设备
		while (targetDevice) {
			//创建一个过滤设备
			status = IoCreateDevice(pDriver, sizeof(DEV_EXTENSION), NULL, targetDevice->DeviceType, targetDevice->Characteristics, FALSE, &filterDevice);
			if (!NT_SUCCESS(status)) {
				DbgPrint("Create FilterDevice Error\r\n");
				filterDevice = targetDevice = NULL;
				return status;
			}
			//绑定下一个设备
			lowDevice = IoAttachDeviceToDeviceStack(filterDevice, targetDevice);
			if (!lowDevice) {
				DbgPrint("Attach targetDevice Error\r\n");
				IoDeleteDevice(filterDevice);
				filterDevice = NULL;
				return STATUS_UNSUCCESSFUL;
			}
			//初始化设备的扩展
			devExt = (PDEV_EXTENSION)filterDevice->DeviceExtension;
			DevExtInit(devExt, filterDevice, targetDevice, lowDevice);
			
			filterDevice->StackSize = lowDevice->StackSize + 1;//栈大小要自己加一,因为没有框架帮我们,所以要手工来
			filterDevice->Flags |= lowDevice->Flags&(DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);
			//遍历下一个设备
			targetDevice = targetDevice->NextDevice;
		}
	}
	

}

然后我们使用IoCreateDevice函数来创建这个附加设备,需要注意的是,这里我们不能像之前在驱动通信里面介绍的一样,随便给我们的驱动来赋予类型了,为了确保I/O请求的正确传递,我们必须要保证这些属性和被附加的设备一致

接着我们使用IoAttachDeviceToDeviceStack来绑定设备,然后初始化设备(这个初始化函数我就不单独讲了)


NTSTATUS DevExtInit(PDEV_EXTENSION devExt, PDEVICE_OBJECT filterDevice, PDEVICE_OBJECT targetDevice, PDEVICE_OBJECT lowDevice) {
	memset(devExt, 0, sizeof(DEV_EXTENSION));
	devExt->FilterDevice = filterDevice;
	devExt->TargetDevice = targetDevice;
	devExt->LowDevice = lowDevice;
	devExt->Size = sizeof(DEV_EXTENSION);
	KeInitializeSpinLock(&devExt->IoRequestSpinLock);
	KeInitializeEvent(&devExt->IoProgressEvent, NotificationEvent, FALSE);
	return STATUS_SUCCESS;
}

再接下来有个细节,就是我们的设备的Flags要加上DO_BUFFERED_IO和DO_DIRECT_IO 以及

DO_POWER_PAGABLE

设备对象标志:

  • DO_BUFFERED_IO: 指示设备对象支持缓冲 I/O。在这种模式下,系统会为每次 I/O 操作分配一个缓冲区,驱动程序可以使用这个缓冲区进行数据传输。
  • DO_DIRECT_IO: 指示设备对象支持直接 I/O。在这种模式下,系统会将用户模式缓冲区直接映射到内核模式,驱动程序可以直接访问用户模式缓冲区。
  • DO_POWER_PAGABLE: 指示设备对象的驱动程序可以在电源管理操作中被分页。这个标志通常用于驱动程序的优化,特别是在电源管理场景中。

这里我专门说一下前两个参数

typedef struct _IRP {
    PMDL              MdlAddress;
    ULONG             Flags;
    union {
        struct _IRP*   MasterIrp;
        PVOID          SystemBuffer;//这里就是系统所创建的缓冲区
    } AssociatedIrp;
    IO_STATUS_BLOCK   IoStatus;
    KPROCESSOR_MODE   RequestorMode;
    BOOLEAN           PendingReturned;
    BOOLEAN           Cancel;
    KIRQL             CancelIrql;
    PDRIVER_CANCEL    CancelRoutine;
    PVOID             UserBuffer;//这里就是DO_DIRECT_IO说的驱动直接访问的用户模式的缓冲区
    union {
        struct {
            union {
                KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
                struct {
                    PVOID    DriverContext[4];
                };
            };
            PETHREAD     Thread;
            LIST_ENTRY   ListEntry;
        } Overlay;
    } Tail;
} IRP, *PIRP;

最后结果

在这里插入图片描述

出问题的地方

经过调试,在停止服务后,任意按下一个按键就蓝屏,所以问题就在这个驱动卸载函数里面。更加精确的定位应该在这个DeAttach函数中,但是反正也不影响我们使用,就懒得搞了(菜

这也是为后来的自己或者其他人指个路

//设备卸载函数
NTSTATUS DriverUnload(PDRIVER_OBJECT pDriver) {
	PDEVICE_OBJECT pDevice;
	PDEV_EXTENSION devExt;

	UNREFERENCED_PARAMETER(pDriver);
	DbgPrint("Driver Unloading..\r\n");
	//DbgBreakPoint();

	pDevice = pDriver->DeviceObject;
	while (pDevice) {
		DeAttach(pDevice);
		pDevice = pDevice->NextDevice;
	}
	pDriver->DeviceObject = NULL;
	//DbgBreakPoint();
	return STATUS_SUCCESS;
}
NTSTATUS DeAttach(PDEVICE_OBJECT pDevice) {
	PDEV_EXTENSION devExt;
	devExt = (PDEV_EXTENSION)pDevice->DeviceExtension;

	if (devExt->TargetDevice) {
		IoDetachDevice(devExt->TargetDevice);
		devExt->TargetDevice = NULL;
	}

	if (devExt->FilterDevice) {
		devExt->FilterDevice = NULL;
	}

	if (devExt->pIrp != NULL) {
#ifdef DEBUG
		DbgBreakPoint();
#endif
		if (IoCancelIrp(devExt->pIrp)) {
			DbgPrintEx(MY_COMPONENT_ID, MY_INFO_LEVEL, "取消成功...\r\n");
		}
		else {
			DbgPrintEx(MY_COMPONENT_ID, MY_ERROR_LEVEL, "取消失败...\r\n");
		}

		// 释放 IRP 的内存
		IoFreeIrp(devExt->pIrp);
		devExt->pIrp = NULL;
	}

	// 删除设备对象
	IoDeleteDevice(pDevice);

	return STATUS_SUCCESS;
}


http://www.kler.cn/a/441575.html

相关文章:

  • Docker网段和服务器ip冲突导致无法访问网络的解决方法
  • 嵌入式 工程配置
  • IOS 安全机制拦截 window.open
  • DRG_DIP 2.0时代医院程序结构转型与数据结构优化研究
  • transformers使用过程问题
  • 基于微信小程序高校订餐系统的设计与开发ssm+论文源码调试讲解
  • 天猫魔盒M17/M17S_超级UI 线刷固件包-可救砖(刷机取消双勾)
  • 【HF设计模式】03-装饰者模式
  • Transformer 中 Self-Attention 的二次方复杂度(Quadratic Complexity )问题及改进方法:中英双语
  • 【Flink-scala】DataStream编程模型总结
  • 2025山东科技大学考研专业课复习资料一览
  • Java设计模式实战:策略模式、工厂模式、模板模式组合使用
  • 人工智能浪潮来袭:2024年技术革命与产业变革深度解析@附64页PDF文件下载
  • 基于Android的生活记录app的设计与实现
  • 【教程】让Jupyter支持打开CSV和Excel(xlsx)文件
  • 死信队列概述
  • 【Leetcode】滑动窗口算法-编程苍穹下划破数据暗夜的高效光弧
  • pytest入门九:feature
  • 【Hive 如何进行update更新?】
  • “MODAS: 利用多组学数据关联研究探索玉米种质资源“
  • elasticsearch 版本
  • [机器学习]AdaBoost(数学原理 + 例子解释 + 代码实战)
  • k8s集群安装keepalive+haproxy
  • M4Pro内核MacOS brew安装docker爬坑
  • 给新ubuntu电脑配置远程控制环境和c++版本的opencv环境
  • 如何运用 HTM?