[轉帖]支持PS2和USB的鍵盤過濾驅動(xfocus)

標  題: [轉帖]支持PS2和USB的鍵盤過濾驅動(xfocus)
發信站: 北郵人論壇 (Sun Mar 18 10:48:44 2007), 站內 
   
  
/*******************************************************************
  
這個鍵盤過濾驅動是一個定時鎖定計算機程序的功能部分,以前 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

發佈了6 篇原創文章 · 獲贊 18 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章