支持 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/