標 題: [轉帖]支持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
[轉帖]支持PS2和USB的鍵盤過濾驅動(xfocus)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.