键盘过滤驱动

概述

irp请求会从io管理器中传递到设备栈中依次向下发送,当到达底层真实设备处理完成后,会依次返回,这时如果在设备栈中有我们自己注册的设备,就可以起到一个过滤的功能。键盘过滤驱动就是如此,通过附加到原本存在的设备KeyboardClass0中,这时当发生按键时irp也会返回到我们的设备中,进而实现起响应。

注意

在整个键盘响应过程中,有一个名为csrss.exe的程序,其会不断的向KeyboardClass0发送读取类型的irp请求,如果我们直接通过断开设备附加再卸载我们的驱动时,就会蓝屏。原因是csrss.exe最终发送的读irp请求已经通过我们最先注册的过滤驱动向下传递了,这时我们卸载了我们自己的驱动,底层返回的irp找不到回调,这时就会蓝屏,所以代码中应该对irp的数量做一个统计,保证其正确处理。

源码

c 复制代码
#include<ntifs.h>

typedef struct
{
	PDEVICE_OBJECT LowerKbdDevice;
}DEVICE_EXTENTION,*PDEVICE_EXTENTION;

PDEVICE_OBJECT myKbdDevice = NULL;

typedef struct _KEYBOARD_INPUT_DATA {
	USHORT UnitId;
	USHORT MakeCode;
	USHORT Flags;
	USHORT Reserved;
	ULONG  ExtraInformation;
} KEYBOARD_INPUT_DATA, * PKEYBOARD_INPUT_DATA;
//IRP等待处理的数量
ULONG pendingkey = 0;

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
	LARGE_INTEGER interval = { 0 };
	PDEVICE_OBJECT DeviceObject = DriverObject->DeviceObject;
	IoDetachDevice(((PDEVICE_EXTENTION)DeviceObject->DeviceExtension)->LowerKbdDevice);
	interval.QuadPart = -10 * 1000 * 1000;
	//判断是否还有未处理的IRP
	while (pendingkey)
	{
		KeDelayExecutionThread(KernelMode, FALSE, &interval);
	}

	IoDeleteDevice(myKbdDevice);

	KdPrint(("驱动卸载结束!\n"));
}

NTSTATUS ReadComplete(PDEVICE_OBJECT DeviceObject, PIRP irp, PVOID Context)
{
	PKEYBOARD_INPUT_DATA KeyBuffer = irp->AssociatedIrp.SystemBuffer;
	char* key[4] = { "KeyDown","KeyUp","E0","E1" };
	int structnum = irp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);
	if (irp->IoStatus.Status == STATUS_SUCCESS)
	{
		for (int i = 0; i < structnum; i++)
		{
			KdPrint(("MakeCode = %x,Flags = %s\n", KeyBuffer->MakeCode, key[KeyBuffer->Flags]));
		}
	}
	//处理完成需要将标志位设置,以声明以完成此irp的返回处理
	if (irp->PendingReturned)
	{
		IoMarkIrpPending(irp);
	}
	pendingkey--;
	return irp->IoStatus.Status;
}

NTSTATUS DispatchPass(PDEVICE_OBJECT pDeviceObject,PIRP irp)
{
	IoCopyCurrentIrpStackLocationToNext(irp);
	return IoCallDriver(((PDEVICE_EXTENTION)pDeviceObject->DeviceExtension)->LowerKbdDevice, irp);
}

NTSTATUS DispatchRead(PDEVICE_OBJECT pDeviceObject, PIRP irp)
{
	IoCopyCurrentIrpStackLocationToNext(irp);

	IoSetCompletionRoutine(irp,ReadComplete,NULL,TRUE,TRUE,TRUE,TRUE);

	pendingkey++;
	return IoCallDriver(((PDEVICE_EXTENTION)pDeviceObject->DeviceExtension)->LowerKbdDevice, irp);
}

NTSTATUS MyAttachDevice(PDRIVER_OBJECT pDriverObject)
{
	UNICODE_STRING kbdName = RTL_CONSTANT_STRING(L"\\Device\\KeyboardClass0");

	NTSTATUS status = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENTION), NULL, 0, FILE_DEVICE_KEYBOARD, NULL, &myKbdDevice);
	if (!NT_SUCCESS(status))
	{
		return status;
	}

	myKbdDevice->Flags |= DO_BUFFERED_IO;
	myKbdDevice->Flags &= ~DO_DEVICE_INITIALIZING;
	
	RtlZeroMemory(myKbdDevice->DeviceExtension,sizeof(DEVICE_EXTENTION));

	status = IoAttachDevice(myKbdDevice, &kbdName, &((PDEVICE_EXTENTION)myKbdDevice->DeviceExtension)->LowerKbdDevice);
	if (!NT_SUCCESS(status))
	{
		IoDeleteDevice(myKbdDevice);
		return status;
	}
	return STATUS_SUCCESS;
}


NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
	int i = 0;
	NTSTATUS Status = STATUS_SUCCESS;
	
	for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
	{
		pDriverObject->MajorFunction[i] = DispatchPass;
	}

	pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;

	Status = MyAttachDevice(pDriverObject);
	if (!NT_SUCCESS(Status))
	{
		KdPrint(("AttachDevice ERROR!\n"));
	}
	else
	{
		KdPrint(("AttachDevice SUCCESS!\n"));
	}

	pDriverObject->DriverUnload = DriverUnload;
	return Status;
}

参考资料

Windows Driver Development Tutorial

相关推荐
神经智研社1 小时前
ROS2-5章:节点参数parameter详细讲解
windows·microsoft·机器人环境搭建·win11 ros2 开发环境
诚信定制8394 小时前
PrivaZer逆向解析:深度清理背后的三大隐患
windows
love530love8 小时前
WorkBuddy + 本地 ComfyUI Wan2.1 文生视频实战:从连续报错到成功出片的完整踩坑记录
人工智能·windows·python·音视频·devops·comfyui·mcp
幽络源小助理9 小时前
Windows11下的Cheat Engine+驱动源码编译并开启DBVM模式-幽络源技术
驱动开发·ce
枳实-叶9 小时前
【Linux驱动开发】第22天:SPI 设备树 + spi_driver
linux·运维·驱动开发
TrisighT9 小时前
Electron 跑鸿蒙 PC 上,这 4 个 API 的行为跟 Windows 完全不一样——但文档一行都没写
windows·electron·harmonyos
qq_3692243317 天前
Windows全系通用!ntdll.dll文件丢失、报错、闪退问题的完整排查与修复教程
windows·dll·dll修复·dll丢失·dll错误
阿米亚波17 天前
【Windows】QEMU 启动 openEuler aarch64/arm64 架构系统 + 离线软件源
linux·windows·经验分享·笔记·架构·arm
HackTwoHub17 天前
Sqli-Scanner SQL注入SKILL自动化挖掘SQL注入,零依赖自动化SQL注入挖掘,赏金猎人
数据库·人工智能·sql·web安全·网络安全·自动化·系统安全
世微 如初17 天前
AP5125大功率LED恒流驱动实战:地摊灯项目从原理图到调试笔记
驱动开发·芯片·led电源驱动·降压恒流ic