支持 PS/2 與 USB 的鍵盤過濾驅動(可卸載)

 支持 PS/2 與 USB 的鍵盤過濾驅動(可卸載)

Author:  sinister
Email:   [email protected]
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/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章