驅動開發之 鍵盤過濾驅動--傳統型鍵盤過濾

來源:互聯網
上載者:User

標籤:驅動開發   鍵盤過濾驅動   

近來在看 《寒江獨釣 windows核心編程》,看到鍵盤過濾部分,記下筆記,僅供參考,有理解不對之處,還望大家指正。

現在來說一下傳統型鍵盤過濾,就是把自己的裝置對象綁定在KbdClass裝置對象之上。那麼發送到KbdClass的IRP都會先經過自己的裝置對象,我們可以在讀派遣函數中設定完成常式,當IRP完成後在完成曆程中得到按鍵資訊。

KbdClass被稱為鍵盤類驅動,在windows中,類驅動通常是指統管一類裝置的驅動程式。不管是USB鍵盤,還是PS/2鍵盤均經過它,所以在這層做攔截,能獲得很好的通用性。

用工具Devicetree查看


安裝 《寒江獨釣 windows核心編程》中執行個體 ctl2cap,再次查看


代碼如下:

#include <wdm.h>// Kbdclass驅動的名字#define KBD_DRIVER_NAME  L"\\Driver\\Kbdclass"typedef struct _C2P_DEV_EXT {     // 這個結構的大小    ULONG NodeSize;     // 過濾裝置對象    PDEVICE_OBJECT pFilterDeviceObject;    // 同時調用時的保護鎖    KSPIN_LOCK IoRequestsSpinLock;    // 進程間同步處理      KEVENT IoInProgressEvent;     // 連結的裝置對象    PDEVICE_OBJECT TargetDeviceObject;     // 綁定前底層裝置對象    PDEVICE_OBJECT LowerDeviceObject; } C2P_DEV_EXT, *PC2P_DEV_EXT;NTSTATUS c2pDevExtInit(     IN PC2P_DEV_EXT devExt,     IN PDEVICE_OBJECT pFilterDeviceObject,     IN PDEVICE_OBJECT pTargetDeviceObject,     IN PDEVICE_OBJECT pLowerDeviceObject ) {     memset(devExt, 0, sizeof(C2P_DEV_EXT));     devExt->NodeSize = sizeof(C2P_DEV_EXT);     devExt->pFilterDeviceObject = pFilterDeviceObject;     KeInitializeSpinLock(&(devExt->IoRequestsSpinLock));     KeInitializeEvent(&(devExt->IoInProgressEvent), NotificationEvent, FALSE);     devExt->TargetDeviceObject = pTargetDeviceObject;     devExt->LowerDeviceObject = pLowerDeviceObject;     return( STATUS_SUCCESS ); }// 這個函數是事實存在的,只是文檔中沒有公開。聲明一下// 就可以直接使用了。NTSTATUSObReferenceObjectByName(                        PUNICODE_STRING ObjectName,                        ULONG Attributes,                        PACCESS_STATE AccessState,                        ACCESS_MASK DesiredAccess,                        POBJECT_TYPE ObjectType,                        KPROCESSOR_MODE AccessMode,                        PVOID ParseContext,                        PVOID *Object                        );extern POBJECT_TYPE IoDriverObjectType;ULONG gC2pKeyCount = 0;PDRIVER_OBJECT gDriverObject = NULL;// 這個函數經過改造。能開啟驅動對象Kbdclass,然後綁定// 它下面的所有的裝置:NTSTATUS c2pAttachDevices(                   IN PDRIVER_OBJECT DriverObject,                   IN PUNICODE_STRING RegistryPath                   ) {     NTSTATUS status = 0;     UNICODE_STRING uniNtNameString;     PC2P_DEV_EXT devExt;     PDEVICE_OBJECT pFilterDeviceObject = NULL;     PDEVICE_OBJECT pTargetDeviceObject = NULL;     PDEVICE_OBJECT pLowerDeviceObject = NULL;     PDRIVER_OBJECT KbdDriverObject = NULL;     KdPrint(("MyAttach\n"));     // 初始化一個字串,就是Kdbclass驅動的名字。    RtlInitUnicodeString(&uniNtNameString, KBD_DRIVER_NAME);     // 請參照前面開啟裝置對象的例子。只是這裡開啟的是驅動對象。    status = ObReferenceObjectByName (         &uniNtNameString,         OBJ_CASE_INSENSITIVE,         NULL,         0,         IoDriverObjectType,         KernelMode,         NULL,         &KbdDriverObject         );     // 如果失敗了就直接返回    if(!NT_SUCCESS(status))     {         KdPrint(("MyAttach: Couldn't get the MyTest Device Object\n"));         return( status );     }    else    {        // 這個開啟需要解應用。早點解除了免得之後忘記。        ObDereferenceObject(DriverObject);    }    // 這是裝置鏈中的第一個裝置    pTargetDeviceObject = KbdDriverObject->DeviceObject;    // 現在開始遍曆這個裝置鏈    while (pTargetDeviceObject)     {        // 產生一個過濾裝置,這是前面讀者學習過的。這裡的IN宏和OUT宏都是        // 空宏,只有標誌性意義,表明這個參數是一個輸入或者輸出參數。        status = IoCreateDevice(             IN DriverObject,             IN sizeof(C2P_DEV_EXT),             IN NULL,             IN pTargetDeviceObject->DeviceType,             IN pTargetDeviceObject->Characteristics,             IN FALSE,             OUT &pFilterDeviceObject             );         // 如果失敗了就直接退出。        if (!NT_SUCCESS(status))         {             KdPrint(("MyAttach: Couldn't create the MyFilter Filter Device Object\n"));             return (status);         }         // 綁定。pLowerDeviceObject是綁定之後得到的下一個裝置。也就是        // 前面常常說的所謂真實裝置。        pLowerDeviceObject =             IoAttachDeviceToDeviceStack(pFilterDeviceObject, pTargetDeviceObject);         // 如果綁定失敗了,放棄之前的操作,退出。        if(!pLowerDeviceObject)         {             KdPrint(("MyAttach: Couldn't attach to MyTest Device Object\n"));             IoDeleteDevice(pFilterDeviceObject);             pFilterDeviceObject = NULL;             return( status );         }         // 裝置擴充!下面要詳細講述裝置擴充的應用。        devExt = (PC2P_DEV_EXT)(pFilterDeviceObject->DeviceExtension);         c2pDevExtInit(             devExt,             pFilterDeviceObject,             pTargetDeviceObject,             pLowerDeviceObject );         // 下面的操作和前面過濾串口的操作基本一致。這裡不再解釋了。        pFilterDeviceObject->DeviceType=pLowerDeviceObject->DeviceType;         pFilterDeviceObject->Characteristics=pLowerDeviceObject->Characteristics;         pFilterDeviceObject->StackSize=pLowerDeviceObject->StackSize+1;         pFilterDeviceObject->Flags |= pLowerDeviceObject->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE) ;         //next device         pTargetDeviceObject = pTargetDeviceObject->NextDevice;    }    return status; } VOID c2pDetach(IN PDEVICE_OBJECT pDeviceObject) { PC2P_DEV_EXT devExt; BOOLEAN NoRequestsOutstanding = FALSE; devExt = (PC2P_DEV_EXT)pDeviceObject->DeviceExtension; __try { __try { IoDetachDevice(devExt->TargetDeviceObject);devExt->TargetDeviceObject = NULL; IoDeleteDevice(pDeviceObject); devExt->pFilterDeviceObject = NULL; DbgPrint(("Detach Finished\n")); } __except (EXCEPTION_EXECUTE_HANDLER){} } __finally{} return; }#define  DELAY_ONE_MICROSECOND  (-10)#define  DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)#define  DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)VOID c2pUnload(IN PDRIVER_OBJECT DriverObject) {     PDEVICE_OBJECT DeviceObject;     PDEVICE_OBJECT OldDeviceObject;     PC2P_DEV_EXT devExt;     LARGE_INTEGERlDelay;    PRKTHREAD CurrentThread;    //delay some time     lDelay = RtlConvertLongToLargeInteger(100 * DELAY_ONE_MILLISECOND);    CurrentThread = KeGetCurrentThread();    // 把當前線程設定為低即時模式,以便讓它的運行盡量少影響其他程式。    KeSetPriorityThread(CurrentThread, LOW_REALTIME_PRIORITY);    UNREFERENCED_PARAMETER(DriverObject);     KdPrint(("DriverEntry unLoading...\n"));     // 遍曆所有裝置並一律解除綁定    DeviceObject = DriverObject->DeviceObject;    while (DeviceObject)    {        // 解除綁定並刪除所有的裝置        c2pDetach(DeviceObject);        DeviceObject = DeviceObject->NextDevice;    }     ASSERT(NULL == DriverObject->DeviceObject);    while (gC2pKeyCount)    {        KeDelayExecutionThread(KernelMode, FALSE, &lDelay);    }    KdPrint(("DriverEntry unLoad OK!\n"));     return; } NTSTATUS c2pDispatchGeneral(                                  IN PDEVICE_OBJECT DeviceObject,                                  IN PIRP Irp                                  ) {     // 其他的分發函數,直接skip然後用IoCallDriver把IRP發送到真實裝置    // 的裝置對象。     KdPrint(("Other Diapatch!"));     IoSkipCurrentIrpStackLocation(Irp);     return IoCallDriver(((PC2P_DEV_EXT)        DeviceObject->DeviceExtension)->LowerDeviceObject, Irp); } NTSTATUS c2pPower(                        IN PDEVICE_OBJECT DeviceObject,                        IN PIRP Irp                        ) {     PC2P_DEV_EXT devExt;    devExt =        (PC2P_DEV_EXT)DeviceObject->DeviceExtension;     PoStartNextPowerIrp( Irp );     IoSkipCurrentIrpStackLocation( Irp );     return PoCallDriver(devExt->LowerDeviceObject, Irp ); } NTSTATUS c2pPnP(                      IN PDEVICE_OBJECT DeviceObject,                      IN PIRP Irp                      ) {     PC2P_DEV_EXT devExt;     PIO_STACK_LOCATION irpStack;     NTSTATUS status = STATUS_SUCCESS;     KIRQL oldIrql;     KEVENT event;     // 獲得真實裝置。    devExt = (PC2P_DEV_EXT)(DeviceObject->DeviceExtension);     irpStack = IoGetCurrentIrpStackLocation(Irp);     switch (irpStack->MinorFunction)     {     case IRP_MN_REMOVE_DEVICE:         KdPrint(("IRP_MN_REMOVE_DEVICE\n"));         // 首先把請求發下去        IoSkipCurrentIrpStackLocation(Irp);         IoCallDriver(devExt->LowerDeviceObject, Irp);         // 然後解除綁定。        IoDetachDevice(devExt->LowerDeviceObject);         // 刪除我們自己產生的虛擬設備。        IoDeleteDevice(DeviceObject);         status = STATUS_SUCCESS;         break;     default:         // 對於其他類型的IRP,全部都直接下發即可。         IoSkipCurrentIrpStackLocation(Irp);         status = IoCallDriver(devExt->LowerDeviceObject, Irp);     }     return status; }// 這是一個IRP完成回呼函數的原型NTSTATUS c2pReadComplete(                               IN PDEVICE_OBJECT DeviceObject,                               IN PIRP Irp,                               IN PVOID Context                               ) {     PIO_STACK_LOCATION IrpSp;     ULONG buf_len = 0;     PUCHAR buf = NULL;     size_t i;     IrpSp = IoGetCurrentIrpStackLocation( Irp );     //  如果這個請求是成功的。很顯然,如果請求失敗了,這麼擷取     //   進一步的資訊是沒意義的。     if( NT_SUCCESS( Irp->IoStatus.Status ) )      {        // 獲得讀請求完成後輸出的緩衝區        buf = Irp->AssociatedIrp.SystemBuffer;        // 獲得這個緩衝區的長度。一般的說傳回值有多長都儲存在        // Information中。        buf_len = Irp->IoStatus.Information;        //… 這裡可以做進一步的處理。我這裡很簡單的列印出所有的掃        // 描碼。        for(i=0;i<buf_len;++i)        {            DbgPrint("ctrl2cap: %2x\r\n", buf[i]);        }    }    gC2pKeyCount--;if( Irp->PendingReturned ){ IoMarkIrpPending( Irp ); }     return Irp->IoStatus.Status;}NTSTATUS c2pDispatchRead(                               IN PDEVICE_OBJECT DeviceObject,                               IN PIRP Irp ) {     NTSTATUS status = STATUS_SUCCESS;     PC2P_DEV_EXT devExt;     PIO_STACK_LOCATION currentIrpStack;     KEVENT waitEvent;    KeInitializeEvent( &waitEvent, NotificationEvent, FALSE );if (Irp->CurrentLocation == 1) { ULONG ReturnedInformation = 0; KdPrint(("Dispatch encountered bogus current location\n")); status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Status = status; Irp->IoStatus.Information = ReturnedInformation; IoCompleteRequest(Irp, IO_NO_INCREMENT); return(status); }     // 全域變數鍵計數器加1    gC2pKeyCount++;    // 得到裝置擴充。目的是之後為了獲得下一個裝置的指標。    devExt =        (PC2P_DEV_EXT)DeviceObject->DeviceExtension;    // 設定回呼函數並把IRP傳遞下去。 之後讀的處理也就結束了。    // 剩下的任務是要等待讀請求完成。    currentIrpStack = IoGetCurrentIrpStackLocation(Irp);     IoCopyCurrentIrpStackLocationToNext(Irp);    IoSetCompletionRoutine( Irp, c2pReadComplete,         DeviceObject, TRUE, TRUE, TRUE );     return  IoCallDriver( devExt->LowerDeviceObject, Irp ); }NTSTATUS DriverEntry(                      IN PDRIVER_OBJECT DriverObject,                      IN PUNICODE_STRING RegistryPath                      ) {     ULONG i;     NTSTATUS status;     KdPrint (("c2p.SYS: entering DriverEntry\n"));     // 填寫所有的分發函數的指標    for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)     {         DriverObject->MajorFunction[i] = c2pDispatchGeneral;     }     // 單獨的填寫一個Read分發函數。因為要的過濾就是讀取來的按鍵資訊    // 其他的都不重要。這個分發函數單獨寫。    DriverObject->MajorFunction[IRP_MJ_READ] = c2pDispatchRead;     // 單獨的填寫一個IRP_MJ_POWER函數。這是因為這類請求中間要調用    // 一個PoCallDriver和一個PoStartNextPowerIrp,比較特殊。    DriverObject->MajorFunction [IRP_MJ_POWER] = c2pPower;     // 我們想知道什麼時候一個我們綁定過的裝置被卸載了(比如從機器上    // 被拔掉了?)所以專門寫一個PNP(隨插即用)分發函數    DriverObject->MajorFunction [IRP_MJ_PNP] = c2pPnP;     // 卸載函數。    DriverObject->DriverUnload = c2pUnload;     gDriverObject = DriverObject;    // 綁定所有鍵盤裝置    status =c2pAttachDevices(DriverObject, RegistryPath);    return status; }






聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.