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

http://www.cnblogs.com/evlon/articles/825597.html

(轉)支持 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/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章