Author: sinister
Email: sinister@whitecell.org
Homepage:http://www.whitecell.org
Date: 2007-02-26
/******************************************************************* 这个键盘过滤驱动是一个定时锁定计算机程序的功能部分,以前 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 键盘按键?要达到这个目的只有自己分析下 USB 协议栈,通 过使用 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 |
WSS(Whitecell Security Systems),一个非营利性民间技术组织,致力于各种系统安全技术的研究。坚持传统的hacker精神,追求技术的精纯。
WSS 主页:http://www.whitecell.org/
WSS 论坛:http://www.whitecell.org/forums/
技术交流,欢迎转载。转载请注明出处 http://evlon.cnblogs.com QQ:evlion@qq.com