本文整理自 Microsoft WDK 範例程式碼 kbfiltr.c,展示一個鍵盤類別過濾驅動的完整實作,涵蓋裝置掛載、IRP 分派、電源管理、PnP 處理、鍵盤連接鏈攔截與資料過濾等核心機制。
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
ULONG i;
UNREFERENCED_PARAMETER(RegistryPath);
// 所有 MajorFunction 預設導向 PassThrough
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
DriverObject->MajorFunction[i] = KbFilter_DispatchPassThrough;
// 明確攔截需要處理的 IRP
DriverObject->MajorFunction[IRP_MJ_CREATE] =
DriverObject->MajorFunction[IRP_MJ_CLOSE] = KbFilter_CreateClose;
DriverObject->MajorFunction[IRP_MJ_PNP] = KbFilter_PnP;
DriverObject->MajorFunction[IRP_MJ_POWER] = KbFilter_Power;
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = KbFilter_InternIoCtl;
DriverObject->DriverUnload = KbFilter_Unload;
DriverObject->DriverExtension->AddDevice = KbFilter_AddDevice;
return STATUS_SUCCESS;
}
關鍵點:先將所有分派函數設為通用的 KbFilter_DispatchPassThrough,再覆寫需特殊處理的 IRP 類型。AddDevice 回呼由 PnP 管理器在偵測到裝置時調用。
NTSTATUS KbFilter_AddDevice(PDRIVER_OBJECT Driver, PDEVICE_OBJECT PDO) {
PDEVICE_OBJECT device;
PDEVICE_EXTENSION devExt;
NTSTATUS status = IoCreateDevice(Driver, sizeof(DEVICE_EXTENSION), NULL,
FILE_DEVICE_KEYBOARD, 0, FALSE, &device);
if (!NT_SUCCESS(status)) return status;
RtlZeroMemory(device->DeviceExtension, sizeof(DEVICE_EXTENSION));
devExt = (PDEVICE_EXTENSION)device->DeviceExtension;
devExt->TopOfStack = IoAttachDeviceToDeviceStack(device, PDO); // 掛載至裝置堆疊
if (!devExt->TopOfStack) { IoDeleteDevice(device); return STATUS_DEVICE_NOT_CONNECTED; }
devExt->Self = device; devExt->PDO = PDO; devExt->DeviceState = PowerDeviceD0;
devExt->SurpriseRemoved = FALSE; devExt->Removed = FALSE; devExt->Started = FALSE;
device->Flags |= (DO_BUFFERED_IO | DO_POWER_PAGABLE);
device->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
}
IoAttachDeviceToDeviceStack 將過濾裝置物件掛載到鍵盤裝置堆疊頂端,TopOfStack 指向下一層驅動(通常是 i8042 埠驅動)。
KbFilter_InternIoCtl 處理兩個關鍵 IOCTL:
ClassService 回呼,並替換為自身的 KbFilter_ServiceCallback,從而攔截所有鍵盤輸入封包。VOID KbFilter_ServiceCallback(PDEVICE_OBJECT DeviceObject,
PKEYBOARD_INPUT_DATA InputDataStart,
PKEYBOARD_INPUT_DATA InputDataEnd,
PULONG InputDataConsumed) {
PDEVICE_EXTENSION devExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
// 呼叫原始 ClassService,可在此修改、刪除或插入封包
(*(PSERVICE_CALLBACK_ROUTINE)devExt->UpperConnectData.ClassService)(
devExt->UpperConnectData.ClassDeviceObject,
InputDataStart, InputDataEnd, InputDataConsumed);
}
此處可實作按鍵重映射、巨集記錄、遊戲模式禁用特定鍵等功能。
IRP_MN_START_DEVICE:同步等待下層驅動完成啟動,設定 Started = TRUE。IRP_MN_SURPRISE_REMOVAL / IRP_MN_REMOVE_DEVICE:標記移除狀態,傳遞 IRP 後分離並刪除裝置物件。DevicePowerState),呼叫 PoCallDriver 向下傳遞(Vista 以上需用 PoCallDriver 處理電源 IRP)。若處理 IOCTL_INTERNAL_I8042_HOOK_KEYBOARD,驅動可攔截鍵盤控制器初始化(KbFilter_InitializationRoutine)與中斷服務例程(KbFilter_IsrHook),實現更底層的硬體控制(如修改 LED、掃描碼轉換等)。
VOID KbFilter_Unload(PDRIVER_OBJECT Driver) {
PAGED_CODE();
UNREFERENCED_PARAMETER(Driver);
ASSERT(NULL == Driver->DeviceObject); // 確認所有裝置已刪除
}
此範例展示了標準的鍵盤過濾驅動架構,是開發鍵盤相關過濾、記錄、重映射驅動的絕佳參考基礎。