模拟笔记本键盘的Fn键

psbeond 2022-04-21 14:20:54

原始需求:按下键盘的Fn+Q键,可以切换系统的散热模式。现在做自动化测试,需要模拟按下Fn+Q键。

试了键盘过滤驱动,监控不到笔记本内置键盘的Fn键,其它键可以监控到。过滤驱动代码如下:

#include <ntddk.h>
#include <ntddkbd.h>


// 设备扩展结构
typedef struct _DEV_EXTENSION
{
	ULONG Size;						// 该结构大小
	KSPIN_LOCK IoRequestSpinLock;	// 自旋锁
	KEVENT IoInProgressEvent;		// 事件
	PIRP pIrp;						// IRP
} DEV_EXTENSION, *PDEV_EXTENSION;

// 声明微软未公开的ObReferenceObjectByName()函数
NTSTATUS ObReferenceObjectByName(
	PUNICODE_STRING ObjectName,
	ULONG Attributes,
	PACCESS_STATE AccessState,
	ACCESS_MASK DesiredAccess,
	POBJECT_TYPE ObjectType,
	KPROCESSOR_MODE AccessMode,
	PVOID ParseContest,
	PVOID *Object
);

extern POBJECT_TYPE *IoDriverObjectType;


NTSTATUS GeneralDispatch(PDEVICE_OBJECT pThisDevice, PIRP pIrp)
{
	DbgPrint("General Dispatch.\r\n");

	IoSkipCurrentIrpStackLocation(pIrp);
	NTSTATUS status = IoCallDriver(pThisDevice->DeviceObjectExtension->AttachedTo, pIrp);

	return status;
}

NTSTATUS PowerDispatch(PDEVICE_OBJECT pThisDevice, PIRP pIrp)
{
	DbgPrint("Power Dispatch.\r\n");

	PoStartNextPowerIrp(pIrp);
	IoSkipCurrentIrpStackLocation(pIrp);
	NTSTATUS status = PoCallDriver(pThisDevice->DeviceObjectExtension->AttachedTo, pIrp);

	return status;
}

NTSTATUS PnpDispatch(PDEVICE_OBJECT pThisDevice, PIRP pIrp)
{
	DbgPrint("Power Dispatch.\r\n");
	NTSTATUS status = STATUS_SUCCESS;
	PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);

	switch (pStack->MajorFunction)
	{
	case IRP_MN_REMOVE_DEVICE:
		IoSkipCurrentIrpStackLocation(pIrp);
		IoCallDriver(pThisDevice->DeviceObjectExtension->AttachedTo, pIrp);
		IoDetachDevice(pThisDevice->DeviceObjectExtension->AttachedTo);
		IoDeleteDevice(pThisDevice);
		status = STATUS_SUCCESS;
		break;

	default:
		IoSkipCurrentIrpStackLocation(pIrp);
		IoCallDriver(pThisDevice->DeviceObjectExtension->AttachedTo, pIrp);
		break;
	}

	return status;
}

// 初始化扩展设备
NTSTATUS DevExtInit(PDEV_EXTENSION pDevExt)
{
	memset(pDevExt, 0, sizeof(DEV_EXTENSION));
	pDevExt->Size = sizeof(DEV_EXTENSION);
	KeInitializeSpinLock(&pDevExt->IoRequestSpinLock);
	KeInitializeEvent(&pDevExt->IoInProgressEvent, NotificationEvent, FALSE);

	return STATUS_SUCCESS;
}

NTSTATUS AttachDevice(PDRIVER_OBJECT pThisDriver, PUNICODE_STRING pRegPath)
{
	UNREFERENCED_PARAMETER(pRegPath);

	UNICODE_STRING strKbdclass = RTL_CONSTANT_STRING(L"\\Driver\\kbdclass");

	// 我们创建的过滤设备
	PDEVICE_OBJECT pFilterDevice = NULL;
	// 真正附加到的目标设备
	PDEVICE_OBJECT pLowerDevice = NULL;
	// \Driver\kbdclass驱动对象
	PDRIVER_OBJECT pKbdclassDriver = NULL;

	// 通过驱动名得到驱动对象
	NTSTATUS status = ObReferenceObjectByName(&strKbdclass, OBJ_CASE_INSENSITIVE, NULL, 0,
		*IoDriverObjectType, KernelMode, NULL, &pKbdclassDriver);
	if (!NT_SUCCESS(status))
	{
		DbgPrint("Open keyboard Driver failed.\r\n");
		return status;
	}

	// 绑定到下层驱动创建的每一个设备上,这样我们想要过滤的键盘设备一定在其中。
	PDEVICE_OBJECT pTargetDevice = pKbdclassDriver->DeviceObject;
	while (NULL != pTargetDevice)
	{
		status = IoCreateDevice(pThisDriver, sizeof(DEV_EXTENSION), NULL,
			pTargetDevice->DeviceType, pTargetDevice->Characteristics,
			FALSE, &pFilterDevice);
		if (!NT_SUCCESS(status))
		{
			DbgPrint("Create new Filter device failed.\r\n");
			pFilterDevice = NULL;
			pTargetDevice = NULL;
			break;
		}

		// 绑定。
		pLowerDevice = IoAttachDeviceToDeviceStack(pFilterDevice, pTargetDevice);
		if (NULL == pLowerDevice)
		{
			DbgPrint("Attach Failed.\r\n");
			pFilterDevice = NULL;
			break;
		}
		ASSERT(pLowerDevice == pFilterDevice->DeviceObjectExtension->AttachedTo);

		// 初始化扩展设备
		DevExtInit((PDEV_EXTENSION)pFilterDevice->DeviceExtension);

		pFilterDevice->DeviceType = pLowerDevice->DeviceType;
		pFilterDevice->Characteristics = pLowerDevice->Characteristics;
		pFilterDevice->StackSize = pLowerDevice->StackSize + 1;
		pFilterDevice->Flags |= pLowerDevice->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);

		pTargetDevice = pTargetDevice->NextDevice;
	}

	// 如果有任何一个绑定不成功,把绑定成功的解绑
	if (!NT_SUCCESS(status))
	{

	}

	// 如果在绑定之前就调用ObDereferenceObject,,有可能外部在调用ObDereferenceObject之后,
	// 且在绑定之前的极短时间内卸载了\Driver\kbdclass驱动,导致后面的pKbdclassDriver无效,从而引发蓝屏
	ObDereferenceObject(pKbdclassDriver);

	return status;
}

// 解除绑定
NTSTATUS DetachDevice(PDEVICE_OBJECT pThisDevice)
{
	IoDetachDevice(pThisDevice->DeviceObjectExtension->AttachedTo);
	ASSERT(NULL == pThisDevice->DeviceObjectExtension->AttachedTo);
	IoDeleteDevice(pThisDevice);

	return STATUS_SUCCESS;
}

// IRP读操作完成回调
NTSTATUS ReadComplate(PDEVICE_OBJECT pThisDevice, PIRP pIrp, PVOID pContext)
{
	UNREFERENCED_PARAMETER(pThisDevice);
	UNREFERENCED_PARAMETER(pContext);

	PIO_STACK_LOCATION pStack = NULL;
	ULONG ulKeyNumber = 0;
	PKEYBOARD_INPUT_DATA pData = NULL;

	pStack = IoGetCurrentIrpStackLocation(pIrp);
	if (NT_SUCCESS(pIrp->IoStatus.Status))
	{
		// 获取键盘数据
		pData = pIrp->AssociatedIrp.SystemBuffer;

		DbgPrint("IRP read %d.\r\n", pData->MakeCode);

		ulKeyNumber = (ULONG)(pIrp->IoStatus.Information / sizeof(PKEYBOARD_INPUT_DATA));
		for (ULONG i = 0; i < ulKeyNumber; ++i)
		{
			if (0x1F == pData->MakeCode)
			{
				pData->MakeCode = 0x20;
			}
		}
	}

	if (pIrp->PendingReturned)
	{
		IoMarkIrpPending(pIrp);
	}

	return pIrp->IoStatus.Status;
}

// 因为中断处理要求的是快,我们不能在读IRP分发函数做一些耗时的工作(比如获取、替换键值等操作),
// 那样的话会导致系统变卡,所以我们只在ReadDispatch中做一个关键操作,设置IRP完成回调函数,
// 意思是指读IRP完成后,再由系统来调用操作键值的函数,那样就不会耽误IRP的传递了。
NTSTATUS ReadDispatch(PDEVICE_OBJECT pThisDevice, PIRP pIrp)
{
	NTSTATUS status = STATUS_SUCCESS;
	PIO_STACK_LOCATION pStack = NULL;

	if (1 == pIrp->CurrentLocation)
	{
		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;
	}

	//得到扩展设备
	pStack = IoGetCurrentIrpStackLocation(pIrp);

	IoCopyCurrentIrpStackLocationToNext(pIrp);
	IoSetCompletionRoutine(pIrp, ReadComplate, pThisDevice, TRUE, TRUE, TRUE);
	status = IoCallDriver(pThisDevice->DeviceObjectExtension->AttachedTo, pIrp);

	return status;
}


//////////////////////////////////////////////////////////////////////////


void DriverUnload(PDRIVER_OBJECT pDriverObject)
{
	PDEVICE_OBJECT pDevice = NULL;

	UNREFERENCED_PARAMETER(pDriverObject);
	DbgPrint("Keyboard driver unload.\r\n");

	pDevice = pDriverObject->DeviceObject;
	while (NULL != pDevice)
	{
		DetachDevice(pDevice);
		pDevice = pDevice->NextDevice;
	}

	ASSERT(NULL == pDriverObject->DeviceObject);
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegPath)
{
	//DbgBreakPoint();

	DbgPrint("hello lsw driver.");

	UNREFERENCED_PARAMETER(pDriverObject);
	UNREFERENCED_PARAMETER(pRegPath);

	if (NULL != pDriverObject)
	{
		pDriverObject->DriverUnload = DriverUnload;
	}

	for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i)
	{
		pDriverObject->MajorFunction[i] = GeneralDispatch;
	}

	pDriverObject->MajorFunction[IRP_MJ_READ] = ReadDispatch;
	pDriverObject->MajorFunction[IRP_MJ_POWER] = PowerDispatch;
	pDriverObject->MajorFunction[IRP_MJ_PNP] = PnpDispatch;

	AttachDevice(pDriverObject, pRegPath);

	return STATUS_SUCCESS;
}

如何才能模拟Fn+Q呢?谢谢。

...全文
202 回复 打赏 收藏 举报
写回复
回复
切换为时间正序
请发表友善的回复…
发表回复
相关推荐
发帖
驱动开发/核心开发

2.1w+

社区成员

硬件/嵌入开发 驱动开发/核心开发
社区管理员
  • 驱动开发/核心开发社区
加入社区
帖子事件
创建了帖子
2022-04-21 14:20
社区公告
暂无公告