欢迎光临
我们一直在努力

Windows键盘过滤

Windows键盘过滤

本文记录我复现谭文老师的《Windows内核编程》一书中相关章节的实验的过程。

Windows键鼠驱动体系结构

在这里插入图片描述

微软官方文档

在这里插入图片描述

何为PS/2

Windows 设备管理器里显示的 PS/2,其实是一个键盘和鼠标的接口标准,而不是指 IBM 的 PS/2 电脑型号。它诞生于 1987 年,是早期 PC 连接键盘和鼠标的主流方式。

虽然现在大家普遍使用 USB 接口,但 PS/2 在系统底层依然有“特权”,所以 Windows 为了兼容老设备或主板接口,依然保留了这套驱动和标识。

特征PS/2 接口(圆孔)USB 接口(方口)
外观 圆形 6 针接口,分紫(键盘)绿(鼠标) 扁平方形接口
连接 不支持热插拔(开机前必须插好) 支持热插拔
系统占用 独占中断(IRQ),不占用 USB 资源 共享 USB 总线资源

即使你用的是 USB 键盘,设备管理器也可能显示“PS/2 标准键盘”。这是因为主板 BIOS 或系统驱动进行了协议转换:

  • 硬件层面:主板内部将 USB 设备模拟成 PS/2 设备进行识别。
  • 驱动层面:Windows 使用 i8042prt.sys 驱动来统一处理这类输入信号。

那么现在PS/2类型的键盘还有用吗:

  • 普通用户:基本被 USB 取代。
  • 特殊场景:部分服务器、工控机或键盘发烧友仍偏好 PS/2,因为它能实现 NKRO(全键无冲) 且延迟极低,不依赖操作系统驱动,在 BIOS 环境下兼容性更好。

所以,PS/2 在 Windows 里更像是一个“历史遗留的兼容层”,确保那些老式圆口键盘在最新系统上也能即插即用。

关于i8042

i8042prt.sys 这个看似晦涩的名字,其实是 Windows 驱动命名惯例与硬件历史的结合体。它由两部分构成:i8042 是硬件芯片的型号,prt 是微软的驱动类型后缀。

i8042这部分名字直接来源于硬件。

  • 历史背景:在早期的 IBM PC/AT(1984年)及兼容机上,键盘控制任务并非由 CPU 直接处理,而是交给一块独立的、廉价的微控制器芯片。
  • 芯片型号:这块芯片通常就是 Intel 8042(或兼容芯片)。它的职责非常专一:负责扫描键盘矩阵、处理按键信号,并通过特定端口与主板通信。
  • 命名继承:微软在编写驱动时,直接沿用了这个硬件控制器的型号名“8042”,并在前面加上了制造商“i”(Intel)作为前缀。这遵循了 Windows 底层驱动常以硬件型号命名的传统(如 i8042prt.sys、atapi.sys)。

prt这是微软定义的驱动类型后缀。

  • PRT 的含义:在 Windows NT 架构中,prt 通常代表 Port Driver(端口驱动)。这类驱动位于驱动栈的底层,直接与硬件端口(Port)或总线打交道,负责最基础的输入输出控制。
  • 角色定位:i8042prt.sys 正是充当了键盘和鼠标的“交通警察”。它直接与主板上的 8042 芯片(或模拟该芯片的硬件)交互,读取端口 0x60 和 0x64 的数据,然后将原始的扫描码(Scan Code)转换为标准格式,再向上层驱动传递。

为什么 USB 键盘也显示 PS/2?这就涉及到一个关键机制:PS/2 仿真(Emulation)。

  • 虽然现代键盘多使用 USB 接口,但为了确保在 BIOS 阶段或极低层级的系统环境下(如安装系统时)键盘依然可用,主板 BIOS 或 USB 控制器会将 USB 键盘模拟成传统的 PS/2 设备。
  • 此时,Windows 加载的仍然是 i8042prt.sys 这个“老管家”来接收信号。因此,你在设备管理器里看到的依然是“PS/2 标准键盘”,尽管物理连接是 USB。
名称部分来源含义
i8042 硬件历史 Intel 8042 键盘控制器芯片型号
prt 系统架构 Port Driver,表示底层端口驱动

所以,i8042prt.sys 本质上是一个为了兼容 40 年前硬件而存在的“古董级”驱动。它的存在确保了从古老的 AT 键盘到现代 USB 设备,都能在 Windows 上被统一识别和处理。

键盘信号处理过程

简单地说,win32k!RawInputThread线程总是调用nt!ZwReadFile函数要求读入数据,然后等待键盘上的键被按下。当键盘上的键被按下时 , win32k!RawInputThread 处 理 nt!ZwReadFile 得到的数据 , 然后nt!ZwReadFile要求读入数据,再等待键盘上的键被按下。

键盘上的每一个键都有扫描码,驱动程序会读取扫描码,将其翻译为正确的动作。键盘和CPU的交互方式是中断和读取端口,这个操作是串行的。

示例代码

头文件

#pragma once

#include <wdm.h>
#include <ntddkbd.h>

// Kbdclass驱动的名字
#define KBD_DRIVER_NAME L"\\\\Driver\\\\Kbdclass"

// 未文档化的函数,声明之后可以直接使用
extern "C" NTKERNELAPI NTSTATUS ObReferenceObjectByName(
PUNICODE_STRING objName,
ULONG attributes,
PACCESS_STATE accessState,
ACCESS_MASK desiredAccess,
POBJECT_TYPE objType,
KPROCESSOR_MODE accessMode,
PVOID parseContext,
PVOID* obj);

extern "C" POBJECT_TYPE* IoDriverObjectType;

struct DEV_EXT
{
// 下层设备
PDEVICE_OBJECT pLowerDevObj;
// 目标设备
PDEVICE_OBJECT pTargetDevObj;
};

源文件

#include "KbFilter_3.h"

static ULONG sg_cnt = 0; // 全局计数

VOID MyDetachDevice(PDEVICE_OBJECT pDevObj)
{
DEV_EXT* pDevExt = (DEV_EXT*)pDevObj->DeviceExtension;
IoDetachDevice(pDevExt->pTargetDevObj);
pDevExt = nullptr;
IoDeleteDevice(pDevObj);

DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Detached Lower Device\\n");
}

/*
卸载驱动例程
*/

VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
{
// 把当前线程设置为低实时模式
PKTHREAD currentThread = KeGetCurrentThread();
KeSetPriorityThread(currentThread, LOW_REALTIME_PRIORITY);

// 遍历设备链,解绑设备
PDEVICE_OBJECT pDevObj = pDriverObj->DeviceObject;
while (pDevObj)
{
MyDetachDevice(pDevObj);
pDevObj = pDevObj->NextDevice;
}

ASSERT(pDevObj == nullptr);

// 等待未完成的IRP
LARGE_INTEGER lg;
lg.QuadPart = (__int64)1000 * 100;
while (sg_cnt)
{
KeDelayExecutionThread(KernelMode, false, &lg);
}

DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Uninstalled Keyboard Filter Driver\\n");
return;
}

// 打开驱动对象KbdClass,并绑定它下面的所有驱动设备
NTSTATUS AttachKeybroadObj(PDRIVER_OBJECT pDriverObj)
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"Attaching keybroad device object\\n");

// 打开驱动对象KbdClass
UNICODE_STRING KbdclassDriverName;
RtlInitUnicodeString(&KbdclassDriverName, KBD_DRIVER_NAME);

PDRIVER_OBJECT pKbdDriverObject = nullptr;
NTSTATUS status = ObReferenceObjectByName(
&KbdclassDriverName,
OBJ_CASE_INSENSITIVE,
NULL,
0,
*IoDriverObjectType,
KernelMode,
NULL,
(PVOID*)&pKbdDriverObject);

if (!NT_SUCCESS(status)) // 打开失败了,直接退出
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"Open KbdClass driver object failed\\n");

return status;
}

// 开始遍历键盘驱动对象的设备链,绑定它的所有设备
PDEVICE_OBJECT pTargetDeviceObj = pKbdDriverObject->DeviceObject;
PDEVICE_OBJECT pFilterDevObj = nullptr, pLowerDevObj = nullptr;
DEV_EXT* devExt = nullptr;
while (pTargetDeviceObj)
{
// 创建过滤设备
status = IoCreateDevice(pDriverObj, sizeof(DEV_EXT), nullptr, pTargetDeviceObj->DeviceType,
pTargetDeviceObj->Characteristics, false, &pFilterDevObj);
if (!NT_SUCCESS(status))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Create filter device object failed\\n");
return status;
}

pLowerDevObj = IoAttachDeviceToDeviceStack(pFilterDevObj, pTargetDeviceObj);
if (!pLowerDevObj) // 绑定失败
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Attached target device failed\\n");
IoDeleteDevice(pFilterDevObj);
pFilterDevObj = nullptr;
return (status);
}

// 初始化设备拓展
devExt = (DEV_EXT*)pFilterDevObj->DeviceExtension;
devExt->pLowerDevObj = pLowerDevObj;
devExt->pTargetDevObj = pTargetDeviceObj;

// 过滤设备的基本设置
pFilterDevObj->DeviceType = pLowerDevObj->DeviceType;
pFilterDevObj->Characteristics = pLowerDevObj->Characteristics;
pFilterDevObj->StackSize = pLowerDevObj->StackSize + 1;
pFilterDevObj->Flags |= pLowerDevObj->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE);
// next device
pTargetDeviceObj = pTargetDeviceObj->NextDevice;
}

ObDereferenceObject(pKbdDriverObject); // 记得释放对象

return STATUS_SUCCESS;
}

// 通用处理例程,不感兴趣的直接跳过
NTSTATUS DispatchGeneral(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
IoSkipCurrentIrpStackLocation(pIrp);
return IoCallDriver(((DEV_EXT*)pDevObj->DeviceExtension)->pLowerDevObj, pIrp);
}

// PNP处理函数。pnp请求不能像其它IRP一样简单跳过交给下一驱动处理
NTSTATUS DispatchPnp(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
PIO_STACK_LOCATION pStLoc = IoGetCurrentIrpStackLocation(pIrp);
DEV_EXT* pDevExt = (DEV_EXT*)pDevObj->DeviceExtension;

NTSTATUS status;
switch (pStLoc->MinorFunction)
{
case IRP_MN_REMOVE_DEVICE: // 设备移出请求
{
IoSkipCurrentIrpStackLocation(pIrp); // 先下发请求
IoCallDriver(pDevExt->pLowerDevObj, pIrp);

IoDetachDevice(pDevExt->pLowerDevObj); // 解除绑定
IoDeleteDevice(pDevObj); // 删除过滤设备

status = STATUS_SUCCESS;
break;
}
default:
{
IoSkipCurrentIrpStackLocation(pIrp);
status = IoCallDriver(pDevExt->pLowerDevObj, pIrp);
}
}

return status;
}

// POWER处理函数
NTSTATUS DispatchPower(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DEV_EXT* pDevExt = (DEV_EXT*)pDevObj->DeviceExtension;

PoStartNextPowerIrp(pIrp);
IoSkipCurrentIrpStackLocation(pIrp);
return PoCallDriver(pDevExt->pLowerDevObj, pIrp);
}

// 请求完成回调函数
NTSTATUS ReadComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext)
{
UNREFERENCED_PARAMETER(pDevObj);
UNREFERENCED_PARAMETER(pContext);

//PIO_STACK_LOCATION pStLoc = IoGetCurrentIrpStackLocation(pIrp);

// 判断下层驱动返回的状态,如果下层驱动返回了失败的结果,那么我们也没有必要继续处理该请求了
if (NT_SUCCESS(pIrp->IoStatus.Status))
{
// 读请求完成后返回的系统缓冲区
auto buf = (PKEYBOARD_INPUT_DATA)pIrp->AssociatedIrp.SystemBuffer;
ULONG_PTR buf_len = pIrp->IoStatus.Information;
ULONG numberKey = buf_len / sizeof(KEYBOARD_INPUT_DATA);
for (size_t i = 0; i < numberKey; i++)
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"number key: %d | scancode: %x | Flags: %s\\n",
numberKey, buf[i].MakeCode, buf[i].Flags ? "Up" : "Down");
}
}

sg_cnt; // 完成了一个读请求,全局计数器-1

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

return pIrp->IoStatus.Status;
}

// 读取请求处理例程
NTSTATUS DispatchRead(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
// 到达最后一层了,不能继续往下传递了
if (pIrp->CurrentLocation == 1)
{
pIrp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
pIrp->IoStatus.Information = 0;

IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_INVALID_DEVICE_REQUEST;
}

// 全局计数器+1
sg_cnt++;

// 继续下发
DEV_EXT* pDevExt = (DEV_EXT*)pDevObj->DeviceExtension;

// PIO_STACK_LOCATION pStLoc = IoGetCurrentIrpStackLocation(pIrp);
IoCopyCurrentIrpStackLocationToNext(pIrp);
IoSetCompletionRoutine(pIrp, ReadComplete, pDevObj, true, true, true);
return IoCallDriver(pDevExt->pLowerDevObj, pIrp);
}

EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegPath)
{
UNREFERENCED_PARAMETER(pRegPath);
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"Installing keyboard-filter driver\\n");

pDriverObj->DriverUnload = DriverUnload;

for (size_t i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
pDriverObj->MajorFunction[i] = DispatchGeneral;
}

pDriverObj->MajorFunction[IRP_MJ_READ] = DispatchRead;
pDriverObj->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
pDriverObj->MajorFunction[IRP_MJ_POWER] = DispatchPower;

NTSTATUS status = AttachKeybroadObj(pDriverObj);

return status;
}

赞(0)
未经允许不得转载:171主机测评 » Windows键盘过滤
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址