`
film
  • 浏览: 231391 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

支持 PS/2 与 USB 的键盘过滤驱动(可卸载)

 
阅读更多

支持 PS/2 与 USB 的键盘过滤驱动(可卸载)
分类:键盘过滤驱动 作者:sinister
部分文章来自于网络,如有侵权请联系站长,以便及时卸下来

转自:http://www.yitsoft.com/chap_study/ch_00017/ch_00017.asp

这个键盘过滤驱动是一个定时锁定计算机程序的功能部分,以前lgx写过一个linux版的键盘过滤驱动,现在我们需要实现一个windows版的键盘过滤驱动,这部分的功能要求如下:

1、强制锁定键盘/鼠标。
2、可动态加/解锁
3、兼容所有 NT 系列的操作系统。

就这个需求而言,能马上能想到的就有7,8种方案,这些方案可以说都能够实现,但如何更合理,更稳定、更彻底的实现,如何尽量少的消耗系统资源,如何保证其兼容性,等一系列问题不得不让我们去重新评估这几种方法。首先在上层实现,一是怕被饶过,二是怕调用频繁影响系统性能。在底层实现,一是怕考虑不周有兼容性问题,二是怕过多使用未公开方法导致系统不稳定。下面就来看一下我想到的几种实现方法:

1、全局键盘/鼠标钩子
2、BlockInput() API
3、使用 setupapi 进行控制
4、全局键盘/鼠标钩子+远线程插入 WINLOGON 进程屏蔽 CTRL+AL+DEL
5、拦截 win23k!RawInputThread() 函数
6、修改 DDK 中自带的 kbfilter 的键盘(Port Driver)过滤驱动
7、拦截 kdbclass 驱动的 driver dispatch routine
8、实现一个 PS/2 与 USB 键盘过滤驱动

我们先看一下以上这些方案的实用性与通用性。第1,2套方案不在考虑之内了,因为有办法解锁,屏蔽不了 CTRL+ALT+DEL 组合键。第3套方案经过我的测试使用 Keyboard 的 CLASSID 不是什么环境都好使,存在兼容性的问题。第4套方案系统效率会大大降低,而且存在很多不稳定因素,对于全局钩子这种东西我一直很排斥。第5套方案,同样存在兼容性问题和不稳定因素。第6套方案看似完美,但无法实现动态卸载,无法卸载就意味着你需要一个开关来控制是否锁定,这样还要与应用层通讯,我的目的是不让应用层与驱动有任何交互。且使用 WDM 形式这种安装起来也很麻烦,要么 INF 要么自己 setupapi,这都不是我想看到的,还有如果仅为实现这么一个功能,就让一个核心驱动一直存在系统中的话,我有障碍。第7套方案看似实现起来很简单,其实有很多问题。如仅是拦截IRP_MJ_READ 并粗暴的返回拒绝后,系统无法恢复到初始状态。且对于USB键盘存在兼容性问题。那么最后只有自己实现一个 PS/2 与 USB 键盘过滤驱动了,既然要实现这么一个驱动,那么就必须能实现到第6套方案的全部功能且不存在它所带来的问题,否则就没有什么意义了。


我们都知道实现一个可直接使用 SERVICE API 来动态装载的 KMD 键盘过滤驱动,首先需要先 ATTACH 到 \\Device\\KeyboardClass0 设备上再进行按键过滤。但如果仅 ATTACH 这个设备的话,会存在很多问题,那就是只能过滤到 PS/2 键盘,而对于使用 USB 键盘的机器毫无作用。现在越来越多的品牌机都预配的是 USB 键盘(如:DELL)。大家可能会想,从KeyboardClass0 一直到 N 都 ATTACH 不就可以了么?总有一个是 USB 键盘设备,经过实践这是不可行的,只要是 USB 键盘设备的话,在使用IoGetDeviceObjectPointer() 函数从设备名得到设备对象都会获取失败,我还曾尝试使用 USB 键盘设备名来转换,还是一样失败。还有一个问题就是 USB 键盘设备不是都有名称的,即使有它的名称也都是动态生成的而不是固定的。那么这就带来了一个问题,既然系统提供的函数无法使用,且我们又是为了动态安装/卸载,使用的是 KMD 类型驱动,无法通过 AddDevice 例程来获得 USB 键盘的设备对象进行挂接。那么如何来屏蔽 USB 键盘按键?要达到这个目的只有自己分析下 US协议栈,通过使用 DriverTree 观察发现,所有 USB 外设都挂在了 \Driver\hidusb上面,USB 键盘驱动名为 \Driver\kbdhid,而它则是 \Driver\kbdhid 的一个 FilterDriver,且这个 \Driver\kbdhid 没有设备名称,也就意味着,我们无法 IoGetDeviceObjectPointer() 得到设备对象并 ATTACH。经过对多个系统多台使用 USB 键盘机器的分析,可以确定让我使用它们来作为得到 USB 键盘设备的依据。(这里仅是对 USB 键盘设备很功利的分析,如果想了解 WINDOWS 的 USB 设备栈如何组建,请阅读tiamo的 《Windows 的 USB 体系结构》。在此向所有公开研究成果的人致敬!)有了这些依据,下面就没什么好说的了,自己遍历 USB 设备栈,根据驱动名称获得 USB 设备对象,然后 ATTACH,过滤按键。具体流程见下面代码。

这里有必要说下动态卸载,我尝试了两种方式,一种是在 UNLOAD 例程里直接取消 IRP,这种方法在 W2K 系统下,无论是 PS/2 还是 USB 键盘都可以很好的实现。但在 XP/2003 系统上则无法成功,在 XP/2003上暂时使用一个 IRP 计数器,在 UNLOAD 例程里判断如果还有一个没有完成的 IRP 则等待,这样的话,需要在 UNLOAD 后用户按下任意键才可继续,虽然能够安全卸载但还需要一次用户介入。考虑实现的仅是一个锁定功能,这样也算是能够忍受了。以后考虑在驱动中直接模拟用户按键来实现,当然这种按键要有通用性和兼容性,支持 PS/2 与 USB 键盘。


完成了上述工作后看起来好象完美了,其实不然,当你屏蔽了当前使用的键盘时别忘了还可以再插入一个 USB 键盘,而后续插入的这个键盘是可以被识别的。这就需要我们处理 IRP_MJ_PNP 选项,对其中我们有兴趣的 IRP_MN_XXX 做相应处理,可以处理 IRP_MN_START_DEVICE,在这时我们动态挂接。还可以处理其他的 IRP,索性返回错误,让识别失效。但我们的驱动是 KMD 类型,只要加上一句对 DriverObject->DriverExtension->AddDevice 的赋值操作则无法直接使用 SERVICE API 来动态加载了。如何保持 KMD 又可以获得 PNP 的处理权呢?这可能要直接对 PnpManager 进行操作了。这个问题有待大家来完善了。


要问为什么把文章插在代码当中,那可能是我觉得,既然把全部代码都贴出来了,写文章就不如直接看代码来的真切。我这里所写也仅仅是对这些天的分析做个记录而已。我更愿意把它看做是一段注释。

最后在此代码中要

感谢:PolyMeta,它在放假前提醒我 USB 键盘的不同。

感谢:lgx,过节前给我找了些事,以至于没有让我觉得过节那么无聊。

感谢:齐佳佳,过节请我吃好吃的。

以下是 键盘过滤驱动 的源代码
/*****************************************************************
文件名 : WssLockKey.c
描述 : 键盘过滤驱动
作者 : sinister
最后修改日期 : 2007-02-26
*****************************************************************/


#include "WssLockKey.h"

NTSTATUS
DriverEntry( IN PDRIVER_OBJECT KeyDriverObject,
IN PUNICODE_STRING RegistryPath )
{
UNICODE_STRING KeyDeviceName;
PDRIVER_OBJECT KeyDriver;
PDEVICE_OBJECT UsbDeviceObject;
NTSTATUS ntStatus;
ULONG i;

//
// 保存设备名,调试使用
//
WCHAR szDeviceName[MAXLEN + MAXLEN] =
{
0
};

KeyDriverObject->DriverUnload = KeyDriverUnload;

//
// 先尝试获得 USB 键盘设备对象,如果成功则挂接 USB 键盘
//
// 注意:因为 USB 键盘设备名不固定,且即使得到名称也无法
// 使用 IoGetDeviceObjectPointer() 函数根据设备名称得到其
// 设备对象,所以这里我们只能自己枚举 USB 设备栈,并得到
// USB 键盘设备来进行挂接
//
ntStatus = GetUsbKeybordDevice( &UsbDeviceObject );
if ( NT_SUCCESS( ntStatus ) && UsbDeviceObject != NULL )
{
//
// 调试使用,USB 键盘设备 kbdhid 没有设备名只有驱动名
// 所以这里打印为空
//
RtlInitUnicodeString( &KeyDeviceName, szDeviceName ); // USB KEYBOARD
DbgPrint( "KeyDeviceName:%S\n", KeyDeviceName.Buffer );

//
// 挂接 USB 键盘设备
//
ntStatus = AttachUSBKeyboardDevice( UsbDeviceObject, KeyDriverObject );
if ( !NT_SUCCESS( ntStatus ) )
{
DbgPrint( "Attach USB Keyboard Device to failed!\n" );
return STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
//
// 如果没有 USB 键盘,则尝试挂接 PS/2 键盘设备
//
RtlInitUnicodeString( &KeyDeviceName, PS2KEYBOARDNAME );

ntStatus = AttachPS2KeyboardDevice( &KeyDeviceName,
KeyDriverObject,
&KeyDriver );
if ( !NT_SUCCESS( ntStatus ) || KeyDriver == NULL )
{
DbgPrint( "Attach PS2 Keyboard Device to failed!\n" );
return STATUS_INSUFFICIENT_RESOURCES;
}
}

//
// 这里没有过滤其他例程,仅处理了按键操作。这样处理会禁止
// 休眠。因在锁定时不允许休眠,所以也就无须处理其他例程
//
KeyDriverObject->MajorFunction[IRP_MJ_READ] = KeyReadPassThrough;

return STATUS_SUCCESS;
}

/////////////////////////////////////////////////////////////////
// 函数类型 : 系统函数
// 函数模块 : 键盘过滤模块
////////////////////////////////////////////////////////////////
// 功能 : 尝试取消队列里的异步 IRP,如果失败则等待用户按键,
// 卸载键盘过滤驱动
// 注意 : 取消 IRP 操作在 2000 系统上可以成功,在 XP / 2003 上
// 则需要等待用户按键,以后有待完善
/////////////////////////////////////////////////////////////////
// 作者 : sinister
// 发布版本 : 1.00.00
// 发布日期 : 2005.12.27
/////////////////////////////////////////////////////////////////
// 重 大 修 改 历 史
////////////////////////////////////////////////////////////////
// 修改者 :
// 修改日期 :
// 修改内容 :
/////////////////////////////////////////////////////////////////

VOID
KeyDriverUnload( PDRIVER_OBJECT KeyDriver )
{
PDEVICE_OBJECT KeyFilterDevice ;
PDEVICE_OBJECT KeyDevice ;
PDEVICE_EXTENSION KeyExtension;
PIRP Irp;
NTSTATUS ntStatus;

KeyFilterDevice = KeyDriver->DeviceObject;
KeyExtension = ( PDEVICE_EXTENSION ) KeyFilterDevice->DeviceExtension;
KeyDevice = KeyExtension->TargetDevice;

IoDetachDevice( KeyDevice );

//
// 如果还有 IRP 未完成,且当前 IRP 有效则尝试取消这个 IRP
//
if ( KeyExtension->IrpsInProgress > 0 && KeyDevice->CurrentIrp != NULL )
{
if ( CancelKeyboardIrp( KeyDevice->CurrentIrp ) )
{
//
// 成功则直接退出删除键盘过滤设备
//
DbgPrint( "CancelKeyboardIrp() is ok\n" );
goto __End;
}
}

//
// 如果取消失败,则一直等待按键
//
while ( KeyExtension->IrpsInProgress > 0 )
{
DbgPrint( "Irp Count:%d\n", KeyExtension->IrpsInProgress );
}

__End:
IoDeleteDevice( KeyFilterDevice );

return ;
}

/////////////////////////////////////////////////////////////////
// 函数类型 : 自定义工具函数
// 函数模块 : 键盘过滤模块
/////////////////////////////////////////////////////////////////
// 功能 : 取消 IRP 操作
// 注意 : 这个函数仅是为配合在 UNLOAD 例程使用,其他例程中不能
// 使用此方法来取消 IRP
/////////////////////////////////////////////////////////////////
// 作者 : sinister
// 发布版本 : 1.00.00
// 发布日期 : 2007.02.20
/////////////////////////////////////////////////////////////////
// 重 大 修 改 历 史
/////////////////////////////////////////////////////////////////
// 修改者 :
// 修改日期 :
// 修改内容 :
/////////////////////////////////////////////////////////////////

BOOLEAN
CancelKeyboardIrp( IN PIRP Irp )
{
if ( Irp == NULL )
{
DbgPrint( "CancelKeyboardIrp: Irp error\n" );
return FALSE;
}


//
// 这里有些判断应该不是必须的,比如对 CancelRoutine 字段,
// 因为 IoCancelIrp() 函数中有判断了。但只有偏执狂才能生存 :)。
// 小波说 低智、偏执、思想贫乏是最不可容忍的。我这一行代码就占
// 了两条 :D,不知 xiaonvwu 看过后会作何感想?:DDD
//

//
// 如果正在取消或没有取消例程则直接返回 FALSE
//
if ( Irp->Cancel || Irp->CancelRoutine == NULL )
{
DbgPrint( "Can't Cancel the irp\n" );
return FALSE;
}

if ( FALSE == IoCancelIrp( Irp ) )
{
DbgPrint( "IoCancelIrp() to failed\n" );
return FALSE;
}

//
// 取消后重设此例程为空
//
IoSetCancelRoutine( Irp, NULL );

return TRUE;
}

/////////////////////////////////////////////////////////////////
// 函数类型 : 自定义工具函数
// 函数模块 : 设备栈信息模块
/////////////////////////////////////////////////////////////////
// 功能 : 遍历 DEVICE_OBJECT 中 AttachedDevice 域,找到 USB 键盘
// 设备上名为 kbdhid 的过滤驱动(Upper Filter Driver)
// 注意 :
/////////////////////////////////////////////////////////////////
// 作者 : sinister
// 发布版本 : 1.00.00
// 发布日期 : 2005.06.02
/////////////////////////////////////////////////////////////////
// 重 大 修 改 历 史
/////////////////////////////////////////////////////////////////
// 修改者 : sinister
// 修改日期 : 2007.2.12
// 修改内容 : 为匹配 USB 键盘驱动做了相应的修改
/////////////////////////////////////////////////////////////////

BOOLEAN
GetAttachedDeviceInfo( IN PDEVICE_OBJECT DevObj )
{
PDEVICE_OBJECT DeviceObject;
BOOLEAN bFound = FALSE;

if ( DevObj == NULL )
{
DbgPrint( "DevObj is NULL!\n" );
return FALSE;
}

DeviceObject = DevObj->AttachedDevice;

while ( DeviceObject )
{
//
// 一些 OBJECT 的名称都存在分页区,虽然大部分时候不会被交换出去,但
// 有一次足够了。这算是经验之谈
//
if ( MmIsAddressValid( DeviceObject->DriverObject->DriverName.Buffer ) )
{
DbgPrint( "Attached Driver Name:%S,Attached Driver Address:0x%x,Attached DeviceAddress:0x%x\n",
DeviceObject->DriverObject->DriverName.Buffer,
DeviceObject->DriverObject,
DeviceObject );

//
// 找到 USB 键盘驱动的 kbdhid 设备了么?找到了就不继续了
//
if ( _wcsnicmp( DeviceObject->DriverObject->DriverName.Buffer,
KDBDEVICENAME,
wcslen( KDBDEVICENAME ) ) == 0 )
{
DbgPrint( "Found kbdhid Device\n" );
bFound = TRUE;
break;
}
}

DeviceObject = DeviceObject->AttachedDevice;
}

return bFound;
}

/////////////////////////////////////////////////////////////////
// 函数类型 : 自定义工具函数
// 函数模块 : 设备栈信息模块
/////////////////////////////////////////////////////////////////
// 功能 : 从 DEVICE_OBJECT 中得到设备与驱动名称并打印地址
// 注意 : 函数功能只是打印信息,不同环境使用中应该会做修改
/////////////////////////////////////////////////////////////////
// 作者 : sinister
// 发布版本 : 1.00.00
// 发布日期 : 2006.05.02
/////////////////////////////////////////////////////////////////
// 重 大 修 改 历 史
/////////////////////////////////////////////////////////////////
// 修改者 : sinister
// 修改日期 : 2007.2.12
// 修改内容 : 打印出 USB 键盘驱动的设备名称,仅作调试使用
/////////////////////////////////////////////////////////////////

VOID
GetDeviceObjectInfo( IN PDEVICE_OBJECT DevObj )
{
POBJECT_HEADER ObjectHeader;
POBJECT_HEADER_NAME_INFO ObjectNameInfo;

if ( DevObj == NULL )
{
DbgPrint( "DevObj is NULL!\n" );
return;
}

//
// 得到对象头
//
ObjectHeader = OBJECT_TO_OBJECT_HEADER( DevObj );

if ( ObjectHeader )
{
//
// 查询设备名称并打印
//
ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );

if ( ObjectNameInfo && ObjectNameInfo->Name.Buffer )
{
DbgPrint( "Device Name:%S - Device Address:0x%x\n",
ObjectNameInfo->Name.Buffer,
DevObj );

//
// 复制 USB 键盘设备名到一个全局 BUFFER 里,为调试时显示
// 用,没有实际的功能用途
//
RtlZeroMemory( szUsbDeviceName, sizeof( szUsbDeviceName ) );

wcsncpy( szUsbDeviceName,
ObjectNameInfo->Name.Buffer,
ObjectNameInfo->Name.Length / sizeof( WCHAR ) );
}

//
// 对于没有名称的设备,则打印 NULL
//
else if ( DevObj->DriverObject )
{
DbgPrint( "Driver Name:%S - Device Name:%S - Driver Address:0x%x - Device Address:0x%x\n",
DevObj->DriverObject->DriverName.Buffer,
L"NULL",
DevObj->DriverObject,
DevObj );
}
}
}

/////////////////////////////////////////////////////////////////
// 函数类型 : 自定义工具函数
// 函数模块 : 键盘过滤模块
/////////////////////////////////////////////////////////////////
// 功能 : 得到 USB 驱动 hidusb 的驱动对象,并遍历以上所有设备
// 对象,过滤出 USB 键盘设备,将其设备对象返回
// 注意 :
/////////////////////////////////////////////////////////////////
// 作者 : sinister
// 发布版本 : 1.00.00
// 发布日期 : 2007.02.13
/////////////////////////////////////////////////////////////////
// 重 大 修 改 历 史
/////////////////////////////////////////////////////////////////
// 修改者 :
// 修改日期 :
// 修改内容 :
/////////////////////////////////////////////////////////////////

NTSTATUS
GetUsbKeybordDevice( OUT PDEVICE_OBJECT* UsbDeviceObject )
{
UNICODE_STRING DriverName;
PDRIVER_OBJECT DriverObject = NULL;
PDEVICE_OBJECT DeviceObject = NULL;
BOOLEAN bFound = FALSE;

RtlInitUnicodeString( &DriverName, USBKEYBOARDNAME );

ObReferenceObjectByName( &DriverName,
OBJ_CASE_INSENSITIVE,
NULL,
0,
( POBJECT_TYPE ) IoDriverObjectType,
KernelMode,
NULL,
&DriverObject );

if ( DriverObject == NULL )
{
DbgPrint( "Not found USB Keyboard Device hidusb!\n" );
return STATUS_UNSUCCESSFUL;
}

DeviceObject = DriverObject->DeviceObject;

while ( DeviceObject )
{
GetDeviceObjectInfo( DeviceObject );

if ( DeviceObject->AttachedDevice )
{
//
// 查找 USB 键盘设备
//
if ( GetAttachedDeviceInfo( DeviceObject ) )
{
bFound = TRUE;
goto __End;
}
}

DeviceObject = DeviceObject->NextDevice;
}

__End:

if ( bFound )
{
//
// 找到则返回 USB 键盘设备对象
//
*UsbDeviceObject = DeviceObject;
}
else
{
*UsbDeviceObject = NULL;
}

return STATUS_SUCCESS;
}

/////////////////////////////////////////////////////////////////
// 函数类型 : 自定义工具函数
// 函数模块 : 键盘过滤模块
////////////////////////////////////////////////////////////////
// 功能 : 创建过滤设备将其附加到需要跟踪的设备上,保存设备相关
// 信息,返回附加后的驱动对象
// 注意 : 此函数仅挂接 USB 键盘设备
/////////////////////////////////////////////////////////////////
// 作者 : sinister
// 发布版本 : 1.00.00
// 发布日期 : 2005.12.27
/////////////////////////////////////////////////////////////////
// 重 大 修 改 历 史
////////////////////////////////////////////////////////////////
// 修改者 :
// 修改日期 :
// 修改内容 :
/////////////////////////////////////////////////////////////////

NTSTATUS
AttachUSBKeyboardDevice( IN PDEVICE_OBJECT UsbDeviceObject,
IN PDRIVER_OBJECT DriverObject )
{
PDEVICE_OBJECT DeviceObject;
PDEVICE_OBJECT TargetDevice;
PDEVICE_EXTENSION DevExt;
NTSTATUS ntStatus;

//
// 创建过滤设备对象
//
ntStatus = IoCreateDevice( DriverObject,
sizeof( DEVICE_EXTENSION ),
NULL,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&DeviceObject );

if ( !NT_SUCCESS( ntStatus ) )
{
DbgPrint( "IoCreateDevice() 0x%x!\n", ntStatus );
return ntStatus;
}

DevExt = ( PDEVICE_EXTENSION ) DeviceObject->DeviceExtension;

//
// 初始化自旋锁
//
KeInitializeSpinLock( &DevExt->SpinLock );

//
// 初始化 IRP 计数器
//
DevExt->IrpsInProgress = 0;

//
// 将过滤设备对象附加在目标设备对象之上,并返回附加后的原设备对象
//

TargetDevice = IoAttachDeviceToDeviceStack( DeviceObject, UsbDeviceObject );
if ( !TargetDevice )
{
IoDeleteDevice( DeviceObject );
DbgPrint( "IoAttachDeviceToDeviceStack() 0x%x!\n", ntStatus );
return STATUS_INSUFFICIENT_RESOURCES;
}

//
// 保存过滤设备信息
//
DevExt->DeviceObject = DeviceObject;
DevExt->TargetDevice = TargetDevice;

//
// 设置过滤设备相关信息与标志
//
DeviceObject->Flags |= ( DO_BUFFERED_IO | DO_POWER_PAGABLE );
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;


return STATUS_SUCCESS;
}

/////////////////////////////////////////////////////////////////
// 函数类型 : 自定义工具函数
// 函数模块 : 键盘过滤模块
////////////////////////////////////////////////////////////////
// 功能 : 创建过滤设备将其附加到需要跟踪的设备上,保存设备相关
// 信息,返回附加后的驱动对象
// 注意 : 此函数仅挂接 PS/2 键盘设备
/////////////////////////////////////////////////////////////////
// 作者 : sinister
// 发布版本 : 1.00.00
// 发布日期 : 2005.12.27
/////////////////////////////////////////////////////////////////
// 重 大 修 改 历 史
////////////////////////////////////////////////////////////////
// 修改者 :
// 修改日期 :
// 修改内容 :
/////////////////////////////////////////////////////////////////

NTSTATUS
AttachPS2KeyboardDevice( IN UNICODE_STRING* DeviceName, // 需要跟踪的设备名
IN PDRIVER_OBJECT DriverObject, // 过滤驱动也就是本驱动的驱动对象
OUT PDRIVER_OBJECT* FilterDriverObject ) // 返回附加后的驱动对象
{
PDEVICE_OBJECT DeviceObject;
PDEVICE_OBJECT FilterDeviceObject;
PDEVICE_OBJECT TargetDevice;
PFILE_OBJECT FileObject;
PDEVICE_EXTENSION DevExt;

NTSTATUS ntStatus;

//
// 根据设备名称找到需要附加的设备对象
//
ntStatus = IoGetDeviceObjectPointer( DeviceName,
FILE_ALL_ACCESS,
&FileObject,
&DeviceObject );

if ( !NT_SUCCESS( ntStatus ) )
{
DbgPrint( "IoGetDeviceObjectPointer() 0x%x\n", ntStatus );
return ntStatus;
}

//
// 创建过滤设备对象
//
ntStatus = IoCreateDevice( DriverObject,
sizeof( DEVICE_EXTENSION ),
NULL,
FILE_DEVICE_KEYBOARD,
0,
FALSE,
&FilterDeviceObject );

if ( !NT_SUCCESS( ntStatus ) )
{
ObDereferenceObject( FileObject );
DbgPrint( "IoCreateDevice() 0x%x!\n", ntStatus );
return ntStatus;
}

//
// 得到设备扩展结构,以便下面保存过滤设备信息
//
DevExt = ( PDEVICE_EXTENSION ) FilterDeviceObject->DeviceExtension;


//
// 初始化自旋锁
//
KeInitializeSpinLock( &DevExt->SpinLock );

//
// 初始化 IRP 计数器
//
DevExt->IrpsInProgress = 0;

//
// 将过滤设备对象附加在目标设备对象之上,并返回附加后的原设备对象
//
TargetDevice = IoAttachDeviceToDeviceStack( FilterDeviceObject,
DeviceObject );
if ( !TargetDevice )
{
ObDereferenceObject( FileObject );
IoDeleteDevice( FilterDeviceObject );
DbgPrint( "IoAttachDeviceToDeviceStack() 0x%x!\n", ntStatus );
return STATUS_INSUFFICIENT_RESOURCES;
}

//
// 保存过滤设备信息
//
DevExt->DeviceObject = FilterDeviceObject;
DevExt->TargetDevice = TargetDevice;
DevExt->pFilterFileObject = FileObject;

//
// 设置过滤设备相关信息与标志
//
FilterDeviceObject->DeviceType = TargetDevice->DeviceType;
FilterDeviceObject->Characteristics = TargetDevice->Characteristics;
FilterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
FilterDeviceObject->Flags |= ( TargetDevice->Flags & ( DO_DIRECT_IO |
DO_BUFFERED_IO ) );

//
// 返回附加后的驱动对象
//
*FilterDriverObject = TargetDevice->DriverObject;

ObDereferenceObject( FileObject );

return STATUS_SUCCESS;
}

/////////////////////////////////////////////////////////////////
// 函数类型 : 自定义工具函数
// 函数模块 : 键盘过滤模块
////////////////////////////////////////////////////////////////
// 功能 : 键盘过滤驱动的 IRP_MJ_READ 派遣例程,所有按键将触发
// 这个 IRP 的完成
// 注意 :
/////////////////////////////////////////////////////////////////
// 作者 : sinister
// 发布版本 : 1.00.00
// 发布日期 : 2007.2.15
/////////////////////////////////////////////////////////////////
// 重 大 修 改 历 史
////////////////////////////////////////////////////////////////
// 修改者 :
// 修改日期 :
// 修改内容 :
/////////////////////////////////////////////////////////////////

NTSTATUS
KeyReadPassThrough( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
{
NTSTATUS status;
KIRQL IrqLevel;

PDEVICE_OBJECT pDeviceObject;
PDEVICE_EXTENSION KeyExtension = ( PDEVICE_EXTENSION )
DeviceObject->DeviceExtension;


IoCopyCurrentIrpStackLocationToNext( Irp );

//
// 将 IRP 计数器加一,为支持 SMP 使用自旋锁
//
KeAcquireSpinLock( &KeyExtension->SpinLock, &IrqLevel );
InterlockedIncrement( &KeyExtension->IrpsInProgress );
KeReleaseSpinLock( &KeyExtension->SpinLock, IrqLevel );

IoSetCompletionRoutine( Irp,
KeyReadCompletion,
DeviceObject,
TRUE,
TRUE,
TRUE );

return IoCallDriver( KeyExtension->TargetDevice, Irp );
}

/////////////////////////////////////////////////////////////////
// 函数类型 :系统回调函数
// 函数模块 : 键盘过滤模块
////////////////////////////////////////////////////////////////
// 功能 : 获得键盘按键,用无效扫描码替换,以达到屏蔽键盘的目的
// 注意 :
/////////////////////////////////////////////////////////////////
// 作者 : sinister
// 发布版本 : 1.00.00
// 发布日期 : 2007.2.12
/////////////////////////////////////////////////////////////////
// 重 大 修 改 历 史
////////////////////////////////////////////////////////////////
// 修改者 :
// 修改日期 :
// 修改内容 :
/////////////////////////////////////////////////////////////////

NTSTATUS
KeyReadCompletion( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context )
{
PIO_STACK_LOCATION IrpSp;
PKEYBOARD_INPUT_DATA KeyData;
PDEVICE_EXTENSION KeyExtension = ( PDEVICE_EXTENSION )
DeviceObject->DeviceExtension;
int numKeys, i;
KIRQL IrqLevel;

IrpSp = IoGetCurrentIrpStackLocation( Irp );


if ( Irp->IoStatus.Status != STATUS_SUCCESS )
{
DbgPrint( "ntStatus:0x%x", Irp->IoStatus.Status );
goto __RoutineEnd;
}

//
// 系统在 SystemBuffer 中保存按键信息
//
KeyData = Irp->AssociatedIrp.SystemBuffer;
if ( KeyData == NULL )
{
DbgPrint( "KeyData is NULL\n" );
goto __RoutineEnd;
}

//
// 得到按键数
//
numKeys = Irp->IoStatus.Information / sizeof( KEYBOARD_INPUT_DATA );
if ( numKeys < 0 )
{
DbgPrint( "numKeys less zero\n" );
goto __RoutineEnd;
}

//
// 使用 0 无效扫描码替换,屏蔽所有按键
//
for ( i = 0; i < numKeys; i++ )
{
DbgPrint( "KeyDwon: 0x%x\n", KeyData[i].MakeCode );
KeyData[i].MakeCode = 0x00;
}


__RoutineEnd :

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

//
// 将 IRP 计数器减一,为支持 SMP 使用自旋锁
//
KeAcquireSpinLock( &KeyExtension->SpinLock, &IrqLevel );
InterlockedDecrement( &KeyExtension->IrpsInProgress );
KeReleaseSpinLock( &KeyExtension->SpinLock, IrqLevel );

return Irp->IoStatus.Status ;
}


/*****************************************************************
文件名 : WssLockKey.h
描述 : 键盘过滤驱动
作者 : sinister
最后修改日期 : 2007-02-26
*****************************************************************/

#ifndef __WSS_LOCKKEY_H_
#define __WSS_LOCKKEY_H_

#include "ntddk.h"
#include "ntddkbd.h"
#include "string.h"
#include

#define MAXLEN 256

#define KDBDEVICENAME L"\\Driver\\kbdhid"
#define USBKEYBOARDNAME L"\\Driver\\hidusb"
#define PS2KEYBOARDNAME L"\\Device\\KeyboardClass0"

typedef struct _OBJECT_CREATE_INFORMATION
{
ULONG Attributes;
HANDLE RootDirectory;
PVOID ParseContext;
KPROCESSOR_MODE ProbeMode;
ULONG PagedPoolCharge;
ULONG NonPagedPoolCharge;
ULONG SecurityDescriptorCharge;
PSECURITY_DESCRIPTOR SecurityDescriptor;
PSECURITY_QUALITY_OF_SERVICE SecurityQos;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
} OBJECT_CREATE_INFORMATION, * POBJECT_CREATE_INFORMATION;

typedef struct _OBJECT_HEADER
{
LONG PointerCount;
union
{
LONG HandleCount;
PSINGLE_LIST_ENTRY SEntry;
};
POBJECT_TYPE Type;
UCHAR NameInfoOffset;
UCHAR HandleInfoOffset;
UCHAR QuotaInfoOffset;
UCHAR Flags;
union
{
POBJECT_CREATE_INFORMATION ObjectCreateInfo;
PVOID QuotaBlockCharged;
};

PSECURITY_DESCRIPTOR SecurityDescriptor;
QUAD Body;
} OBJECT_HEADER, * POBJECT_HEADER;

#define NUMBER_HASH_BUCKETS 37

typedef struct _OBJECT_DIRECTORY
{
struct _OBJECT_DIRECTORY_ENTRY* HashBuckets[NUMBER_HASH_BUCKETS];
struct _OBJECT_DIRECTORY_ENTRY** LookupBucket;
BOOLEAN LookupFound;
USHORT SymbolicLinkUsageCount;
struct _DEVICE_MAP* DeviceMap;
} OBJECT_DIRECTORY, * POBJECT_DIRECTORY;

typedef struct _OBJECT_HEADER_NAME_INFO
{
POBJECT_DIRECTORY Directory;
UNICODE_STRING Name;
ULONG Reserved;
#if DBG
ULONG Reserved2 ;
LONG DbgDereferenceCount ;
#endif
} OBJECT_HEADER_NAME_INFO, * POBJECT_HEADER_NAME_INFO;

#define OBJECT_TO_OBJECT_HEADER( o ) \
CONTAINING_RECORD( (o), OBJECT_HEADER, Body )

#define OBJECT_HEADER_TO_NAME_INFO( oh ) ((POBJECT_HEADER_NAME_INFO) \
((oh)->NameInfoOffset == 0 ? NULL : ((PCHAR)(oh) - (oh)->NameInfoOffset)))

typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT DeviceObject;
PDEVICE_OBJECT TargetDevice;
PFILE_OBJECT pFilterFileObject;
ULONG DeviceExtensionFlags;
LONG IrpsInProgress;
KSPIN_LOCK SpinLock;
}DEVICE_EXTENSION, * PDEVICE_EXTENSION;


VOID
KeyDriverUnload( PDRIVER_OBJECT KeyDriver );

BOOLEAN
CancelKeyboardIrp( IN PIRP Irp );

extern POBJECT_TYPE* IoDriverObjectType;

NTSYSAPI
NTSTATUS
NTAPI ObReferenceObjectByName( IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN PACCESS_STATE AccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
OUT PVOID* Object );

NTSTATUS
GetUsbKeybordDevice( OUT PDEVICE_OBJECT* UsbDeviceObject );

BOOLEAN
GetAttachedDeviceInfo( IN PDEVICE_OBJECT DevObj );

VOID
GetDeviceObjectInfo( IN PDEVICE_OBJECT DevObj );

NTSTATUS
AttachUSBKeyboardDevice( IN PDEVICE_OBJECT UsbDeviceObject,
IN PDRIVER_OBJECT DriverObject );

NTSTATUS
AttachPS2KeyboardDevice( IN UNICODE_STRING* DeviceName,
IN PDRIVER_OBJECT DriverObject,
OUT PDRIVER_OBJECT* FilterDriverObject );

NTSTATUS
KeyReadCompletion( IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context );
NTSTATUS
KeyReadPassThrough( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );

WCHAR szUsbDeviceName[MAXLEN];

#endif

以上就是完整的键盘过滤驱动源代码,编译后就可运行.

 

分享到:
评论

相关推荐

    ps2标准键盘驱动程序.zip

    而“usb”标签可能让人误解,因为PS/2键盘并不直接通过USB接口连接,但可能是因为某些现代主板上的PS/2接口是由USB控制器模拟的,或者用户可能需要这个驱动来支持通过USB转PS/2适配器连接的键盘。 压缩包子文件的...

    日文键盘布局 windows8(ps2/笔记本/usb键盘)

    支持windows8(32/64位)下面的ps2或笔记本键盘 ...笔记本(ps/2):把键盘驱动更改为日文106/109键盘,然后把此附件加入注册表,重启。 usb键盘:把键盘驱动改为富士通106/109键盘,然后把此附件加入注册表,重启。

    ps/2键盘/鼠标协议英文版

    总的来说,PS/2键盘和鼠标协议虽然现在已经被USB等现代接口取代,但它们仍然是计算机历史和技术发展的重要组成部分。深入理解这些协议可以帮助我们更好地理解早期个人计算机的工作原理,同时也能在遇到老式设备或...

    keyboard ps/2键盘 单片机接口

    标题中的“keyboard ps/2键盘 单片机接口”指的是在单片机系统中与PS/2(Personal System/2)接口键盘进行通信的技术。PS/2键盘是早期个人计算机常用的外设接口,用于连接键盘,它使用的是串行通信协议,尽管现在...

    USBkey.rar_usbkey _usb虚拟键盘_模拟USBkey_模拟ps/2键盘_模拟键盘

    描述中的“该程序可以控制PC机的PS/2键盘的NUM LOCK和SCROLL LOCK的灯”进一步说明了这个模拟过程的实现,证明了单片机与电脑之间的通信是正常的。PS/2键盘是老式的一种键盘接口,虽已逐渐被USB接口取代,但仍然用于...

    inline hook 可以实现兼容PS/2键盘和USB键盘的Logge 源代码

    用DeviceTree 可以看到PS/2键盘的端口驱动是i8042prt,USB键盘的端口驱动是Kbdhid。无论是PS/2 键盘还是USB键盘,在端口驱动处理完IRP之后都会调用上层处理的回调函数,即KbdClass 处理输入数据的函数。Hook 这个回调...

    关于PS2 兼容鼠标驱动不可卸载的问题

    关于PS2 兼容鼠标驱动不可卸载的问题,因为此驱动重启电脑后 会自动安装。 所以需要我们 一点技术方面的操作。

    asd.rar_PS/2键盘_ps/2_模拟PS/2_模拟ps/2键盘

    标题中的“asd.rar_PS/2...通过模拟PS/2键盘,我们可以实现与传统PS/2接口兼容的创新应用,如自动化测试、特殊环境下的输入设备,甚至是对老式系统的支持。而这一切都离不开对PS/2协议的深入理解以及有效的编程技巧。

    ps_2键盘驱动

    基于verilog的ps_2键盘驱动,已经验证综合即可实现

    日文键盘布局(ps2/笔记本/usb键盘)

    但是,需要注意的是,某些USB键盘可能内置了自身的键盘布局设置,用户可能需要在设备管理器中手动更新驱动程序或调整设置。 压缩包中的`使用说明.txt`文件是至关重要的,它将提供详细的步骤指导,包括如何安全地...

    鼠标PS2转USB程序

    在这个案例中,"鼠标PS2转USB程序"是一个软件解决方案,它允许PS/2鼠标通过USB接口与计算机通信。以下是对这个程序和相关知识点的详细解释: 首先,我们要理解PS/2鼠标和USB鼠标的差异。PS/2鼠标是早期个人电脑的...

    支持 PS-2 与 USB 的键盘过滤驱动

    打开之后可以得到相关的键盘驱动程序的设计方法

    黑苹果通用ps2键盘驱动

    总的来说,“黑苹果通用ps2键盘驱动”是解决黑苹果系统中PS/2键盘兼容性问题的关键。虽然它提供了便利,但使用时也需谨慎,确保遵循正确的安装步骤,并随时关注社区的最新动态,以获取最新的驱动版本和问题解决方案...

    mac10.9键盘鼠标ps2驱动

    在Mac OS X 10.9(Mavericks)系统中,PS/2接口的键盘和鼠标不再直接支持,因为自OS X Lion开始,苹果已转向全USB接口的输入设备。然而,对于那些拥有PS/2键盘或鼠标的用户,仍有可能通过特定的驱动程序来实现兼容性...

    PS/2的键盘解码程序

    PS/2键盘的工作原理是基于中断驱动的,它通过一个6针微型DIN连接器与主机通信。键盘发送的数据是8位的,包括一个起始位、7个数据位、一个奇偶校验位和一个停止位。数据的传输方向是从键盘到主机,由键盘主动发起。当...

    苹果PS/2接口鼠标驱动

    苹果电脑上的PS/2接口鼠标驱动程序主要用于支持那些使用传统PS/2接口的鼠标在Mac系统上正常工作。PS/2接口是一种早期的输入设备接口,常见于键盘和鼠标,但在现代苹果计算机中已不再使用,取而代之的是USB接口。然而...

    PS2转USB键盘程序源码

    - **关键部分**:包括初始化CY7C63803,设置中断处理程序,读取PS/2键盘输入并转换为USB报告,以及与主机的USB通信。 - **调试与测试**:开发过程中需要对源码进行编译、烧录和测试,确保PS/2键盘输入能正确转换并...

    The PS/2 Mouse/Keyboard Protocol

    《PS/2鼠标/键盘协议》是一份详细阐述PS/2接口设备,包括鼠标、键盘和触摸板,如何与主机进行通信的技术文档。该协议由Adam Chapweske撰写,并在2003年5月9日进行了最后更新。本文档提供“原样”信息,不包含任何...

    USB转PS2方案资料-芯片选型-硬件攻城狮必备

    USB转PS2技术是将传统的PS/2接口与现代USB接口进行转换的一种解决方案,使得旧式的PS/2设备(如键盘和鼠标)能在只配备USB接口的计算机上使用。本资料包提供了关于USB转PS2的芯片选型及硬件设计方面的详细信息,对于...

    PS2技术参考.rar_PS/2技术参考_PS2技术参考_ps/2协议_ps2_ps2 键盘

    同时,许多USB键盘和鼠标也提供了PS/2转接头,以便兼容那些只有PS/2接口的设备。 总结来说,PS/2接口及其协议虽然已经历史悠久,但其简洁性和实用性使其在特定领域中依然有其价值。理解并掌握PS/2技术对于进行复古...

Global site tag (gtag.js) - Google Analytics