21,616
社区成员




原始需求:按下键盘的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呢?谢谢。