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

来源:互联网 发布:丁威特电子狗升级软件 编辑:程序博客网 时间:2024/04/27 16:28
导读: 
  /******************************************************************* 
  这个键盘过滤驱动是一个定时锁定计算机程序的功能部分,以前 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;
  { 
  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精神,追求技术的精纯。