• Windows 10驱动开发入门(四):USB下的过滤器驱动


    所谓的驱动过滤器,如果之前有做过windowsAPI 相关的hook,那就比较好理解了。道理跟API hook差不多,虽然在驱动程序里,程序的解释不见得这么简单,但是对于外部的表象来看,基本上就是hook的用法。

    在微软的demo中,就有这样的一个例子。kbfiltr

    先看看kbfiltr到底做了什么事情,能做什么事情。用源码编译好相关的驱动。

    打开设备管理器,选择键盘的那一项。

    在这里插入图片描述
    右键,更新驱动。

    在这里插入图片描述

    把驱动拷贝到某个目录下,安装驱动。

    在这里插入图片描述

    出现驱动安装的提示,始终安装驱动。

    安装完成以后,键盘的驱动变为如下:

    在这里插入图片描述
    我们的驱动安装成功,重新启动虚拟机。

    当我们进入计算机以后,发现计算机的crtl + c 键无效了,不能进行复制。

    在这里插入图片描述
    这时候可以打开DebugView,看看log输出了哪些内容。

    在这里插入图片描述
    提示LCtrl Pressed,被按下。

    过滤驱动,拦截了Ctrl 键,也许还拦截了其他键,我们没有测试到,但总的来说,驱动可以拦截到键盘的任何一个键。

    看看代码是怎么做到的。

    NTSTATUS
    DriverEntry(
        IN PDRIVER_OBJECT  DriverObject,
        IN PUNICODE_STRING RegistryPath
        )
    /*++
    
    Routine Description:
    
        Installable driver initialization entry point.
        This entry point is called directly by the I/O system.
    
    Arguments:
    
        DriverObject - pointer to the driver object
    
        RegistryPath - pointer to a unicode string representing the path,
                       to driver-specific key in the registry.
    
    Return Value:
    
        STATUS_SUCCESS if successful,
        STATUS_UNSUCCESSFUL otherwise.
    
    --*/
    {
        WDF_DRIVER_CONFIG               config;
        NTSTATUS                        status;
    
        DebugPrint(("Keyboard Filter Driver Sample - Driver Framework Edition.\n"));
        DebugPrint(("Built %s %s\n", __DATE__, __TIME__));
    
        //
        // Initiialize driver config to control the attributes that
        // are global to the driver. Note that framework by default
        // provides a driver unload routine. If you create any resources
        // in the DriverEntry and want to be cleaned in driver unload,
        // you can override that by manually setting the EvtDriverUnload in the
        // config structure. In general xxx_CONFIG_INIT macros are provided to
        // initialize most commonly used members.
        //
    
        WDF_DRIVER_CONFIG_INIT(
            &config,
            KbFilter_EvtDeviceAdd
        );
    
        //
        // Create a framework driver object to represent our driver.
        //
        status = WdfDriverCreate(DriverObject,
                                RegistryPath,
                                WDF_NO_OBJECT_ATTRIBUTES,
                                &config,
                                WDF_NO_HANDLE); // hDriver optional
        if (!NT_SUCCESS(status)) {
            DebugPrint(("WdfDriverCreate failed with status 0x%x\n", status));
        }
    
        return status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    驱动的入口,注意KbFilter_EvtDeviceAdd函数。

    NTSTATUS
    KbFilter_EvtDeviceAdd(
        IN WDFDRIVER        Driver,
        IN PWDFDEVICE_INIT  DeviceInit
        )
    /*++
    Routine Description:
    
        EvtDeviceAdd is called by the framework in response to AddDevice
        call from the PnP manager. Here you can query the device properties
        using WdfFdoInitWdmGetPhysicalDevice/IoGetDeviceProperty and based
        on that, decide to create a filter device object and attach to the
        function stack.
    
        If you are not interested in filtering this particular instance of the
        device, you can just return STATUS_SUCCESS without creating a framework
        device.
    
    Arguments:
    
        Driver - Handle to a framework driver object created in DriverEntry
    
        DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
    
    Return Value:
    
        NTSTATUS
    
    --*/
    {
        WDF_OBJECT_ATTRIBUTES   deviceAttributes;
        NTSTATUS                status;
        WDFDEVICE               hDevice;
        WDFQUEUE                hQueue;
        PDEVICE_EXTENSION       filterExt;
        WDF_IO_QUEUE_CONFIG     ioQueueConfig;
    
        UNREFERENCED_PARAMETER(Driver);
    
        PAGED_CODE();
    
        DebugPrint(("Enter FilterEvtDeviceAdd \n"));
    
        //
        // Tell the framework that you are filter driver. Framework
        // takes care of inherting all the device flags & characterstics
        // from the lower device you are attaching to.
        //
        WdfFdoInitSetFilter(DeviceInit);
    
        WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_KEYBOARD);
    
        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_EXTENSION);
    
        //
        // Create a framework device object.  This call will in turn create
        // a WDM deviceobject, attach to the lower stack and set the
        // appropriate flags and attributes.
        //
        status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &hDevice);
        if (!NT_SUCCESS(status)) {
            DebugPrint(("WdfDeviceCreate failed with status code 0x%x\n", status));
            return status;
        }
    
        filterExt = FilterGetData(hDevice);
    
        //
        // Configure the default queue to be Parallel. Do not use sequential queue
        // if this driver is going to be filtering PS2 ports because it can lead to
        // deadlock. The PS2 port driver sends a request to the top of the stack when it
        // receives an ioctl request and waits for it to be completed. If you use a
        // a sequential queue, this request will be stuck in the queue because of the 
        // outstanding ioctl request sent earlier to the port driver.
        //
        WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig,
                                 WdfIoQueueDispatchParallel);
    
        //
        // Framework by default creates non-power managed queues for
        // filter drivers.
        //
        ioQueueConfig.EvtIoInternalDeviceControl = KbFilter_EvtIoInternalDeviceControl;
    
        status = WdfIoQueueCreate(hDevice,
                                &ioQueueConfig,
                                WDF_NO_OBJECT_ATTRIBUTES,
                                WDF_NO_HANDLE // pointer to default queue
                                );
        if (!NT_SUCCESS(status)) {
            DebugPrint( ("WdfIoQueueCreate failed 0x%x\n", status));
            return status;
        }
    
        //
        // Create a new queue to handle IOCTLs that will be forwarded to us from
        // the rawPDO. 
        //
        WDF_IO_QUEUE_CONFIG_INIT(&ioQueueConfig,
                                 WdfIoQueueDispatchParallel);
    
        //
        // Framework by default creates non-power managed queues for
        // filter drivers.
        //
        ioQueueConfig.EvtIoDeviceControl = KbFilter_EvtIoDeviceControlFromRawPdo;
    
        status = WdfIoQueueCreate(hDevice,
                                &ioQueueConfig,
                                WDF_NO_OBJECT_ATTRIBUTES,
                                &hQueue
                                );
        if (!NT_SUCCESS(status)) {
            DebugPrint( ("WdfIoQueueCreate failed 0x%x\n", status));
            return status;
        }
    
        filterExt->rawPdoQueue = hQueue;
    
        //
        // Create a RAW pdo so we can provide a sideband communication with
        // the application. Please note that not filter drivers desire to
        // produce such a communication and not all of them are contrained
        // by other filter above which prevent communication thru the device
        // interface exposed by the main stack. So use this only if absolutely
        // needed. Also look at the toaster filter driver sample for an alternate
        // approach to providing sideband communication.
        //
        status = KbFiltr_CreateRawPdo(hDevice, ++InstanceNo);
    
        return status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132

    驱动添加函数,注意KbFilter_EvtIoInternalDeviceControl

    case IOCTL_INTERNAL_KEYBOARD_CONNECT:
            //
            // Only allow one connection.
            //
            if (devExt->UpperConnectData.ClassService != NULL) {
                status = STATUS_SHARING_VIOLATION;
                break;
            }
    
            //
            // Get the input buffer from the request
            // (Parameters.DeviceIoControl.Type3InputBuffer).
            //
            status = WdfRequestRetrieveInputBuffer(Request,
                                        sizeof(CONNECT_DATA),
                                        &connectData,
                                        &length);
            if(!NT_SUCCESS(status)){
                DebugPrint(("WdfRequestRetrieveInputBuffer failed %x\n", status));
                break;
            }
    
            NT_ASSERT(length == InputBufferLength);
    
            devExt->UpperConnectData = *connectData;
    
            //
            // Hook into the report chain.  Everytime a keyboard packet is reported
            // to the system, KbFilter_ServiceCallback will be called
            //
    
            connectData->ClassDeviceObject = WdfDeviceWdmGetDeviceObject(hDevice);
    
    #pragma warning(disable:4152)  //nonstandard extension, function/data pointer conversion
    
            connectData->ClassService = KbFilter_ServiceCallback;
    
    #pragma warning(default:4152)
    
            break;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    Hook 掉report chain,每次键盘有动静的话,都会调用KbFilter_ServiceCallback,关键看KbFilter_ServiceCallback是怎么做处理的。

    VOID
    KbFilter_ServiceCallback(
        IN PDEVICE_OBJECT  DeviceObject,
        IN PKEYBOARD_INPUT_DATA InputDataStart,
        IN PKEYBOARD_INPUT_DATA InputDataEnd,
        IN OUT PULONG InputDataConsumed
        )
    /*++
    
    Routine Description:
    
        Called when there are keyboard packets to report to the Win32 subsystem.
        You can do anything you like to the packets.  For instance:
    
        o Drop a packet altogether
        o Mutate the contents of a packet
        o Insert packets into the stream
    
    Arguments:
    
        DeviceObject - Context passed during the connect IOCTL
    
        InputDataStart - First packet to be reported
    
        InputDataEnd - One past the last packet to be reported.  Total number of
                       packets is equal to InputDataEnd - InputDataStart
    
        InputDataConsumed - Set to the total number of packets consumed by the RIT
                            (via the function pointer we replaced in the connect
                            IOCTL)
    
    Return Value:
    
        Status is returned.
    
    --*/
    {
        PDEVICE_EXTENSION   devExt;
        WDFDEVICE   hDevice;
    
        hDevice = WdfWdmDeviceGetWdfDeviceHandle(DeviceObject);
    
        devExt = FilterGetData(hDevice);
    
    	size_t length = InputDataEnd - InputDataStart;
    
    	for (size_t i = 0; i < length; i++) {
    		KEYBOARD_INPUT_DATA data[2];
    		int endIndex = 1;
    
    		if (InputDataStart[i].MakeCode == 0x1d /*LCtrl*/ && 
    			(InputDataStart[i].Flags == KEY_MAKE || InputDataStart[i].Flags == KEY_BREAK)){
    			data[0] = InputDataStart[i];
    			data[1] = InputDataStart[i + 1];
    			data[0].MakeCode = 0x2a;	//LShift
    			KdPrint(("LCrtl Pressed"));
    		}
    		else if (InputDataStart[i].MakeCode == 0x1de0 /*RCtrl*/ &&
    				(InputDataStart[i].Flags == KEY_MAKE || InputDataStart[i].Flags == KEY_BREAK)) {
    			data[0] = InputDataStart[i];
    			data[1] = InputDataStart[i + 1];
    			data[0].MakeCode = 0x36;	//RShift
    			KdPrint(("RCrtl Pressed"));
    		}
    		else if (InputDataStart[i].MakeCode == 0x5be0 /*LWin*/ &&
    				(InputDataStart[i].Flags == KEY_MAKE || InputDataStart[i].Flags == KEY_BREAK)) {
    			data[0] = InputDataStart[i];
    			data[1] = InputDataStart[i + 1];
    			data[0].MakeCode = 0x2a;	//LShift
    			KdPrint(("LWin Pressed"));
    		}
    		else if (InputDataStart[i].MakeCode == 0xe05b /*maybe LWin too*/ &&
    			(InputDataStart[i].Flags == KEY_MAKE || InputDataStart[i].Flags == KEY_BREAK)) {
    			data[0] = InputDataStart[i];
    			data[1] = InputDataStart[i + 1];
    			data[0].MakeCode = 0x2a;	//LShift
    			KdPrint(("LWin Pressed"));
    		}
    		else if (InputDataStart[i].MakeCode == 0x5ce0 /*RWin*/ &&
    				(InputDataStart[i].Flags == KEY_MAKE || InputDataStart[i].Flags == KEY_BREAK)) {
    			data[0] = InputDataStart[i];
    			data[1] = InputDataStart[i + 1];
    			data[0].MakeCode = 0x36;	//RShift
    			KdPrint(("RWin Pressed"));
    		}
    		else if (InputDataStart[i].MakeCode == 0xe05c /*maybe RWin too*/ &&
    			(InputDataStart[i].Flags == KEY_MAKE || InputDataStart[i].Flags == KEY_BREAK)) {
    			data[0] = InputDataStart[i];
    			data[1] = InputDataStart[i + 1];
    			data[0].MakeCode = 0x36;	//RShift
    			KdPrint(("RWin Pressed"));
    		}
    		else if (InputDataStart[i].MakeCode == 0x38 /*Alt*/ &&
    				(InputDataStart[i].Flags == KEY_MAKE || InputDataStart[i].Flags == KEY_BREAK)) {
    			data[0] = InputDataStart[i];
    			data[1] = InputDataStart[i + 1];
    			data[0].MakeCode = 0x2a;	//LShift
    			KdPrint(("Alt Pressed"));
    		}
    		else if (InputDataStart[i].MakeCode == 0x38e0 /*AltGr*/ &&
    				(InputDataStart[i].Flags == KEY_MAKE || InputDataStart[i].Flags == KEY_BREAK)) {
    			data[0] = InputDataStart[i];
    			data[1] = InputDataStart[i + 1];
    			data[0].MakeCode = 0x2a;	//RShift
    			KdPrint(("AltGr Pressed"));
    		}
    		else if (InputDataStart[i].MakeCode == 0x0f /*Tab*/ &&
    				(InputDataStart[i].Flags == KEY_MAKE || InputDataStart[i].Flags == KEY_BREAK)) {
    			data[0] = InputDataStart[i];
    			data[1] = InputDataStart[i + 1];
    			data[0].MakeCode = 0x2a;	//LShift
    			KdPrint(("Tab Pressed"));
    		}
    		else {
    			//No Filtering
    			data[0] = InputDataStart[i];
    			data[1] = InputDataStart[i + 1];
    			endIndex = 1;
    		}
            KdPrint(("Tab Pressed data 0 0x%x \n", data[0]));
            KdPrint(("Tab Pressed data 1 0x%x \n", data[1]));
            KdPrint(("Tab Pressed data 0 MakeCode 0x%x \n", data[0].MakeCode));
            
    
            
    
    		(*(PSERVICE_CALLBACK_ROUTINE)(ULONG_PTR)devExt->UpperConnectData.ClassService)(
    			devExt->UpperConnectData.ClassDeviceObject,
    			&data[0],
    			&data[endIndex],
    			InputDataConsumed);
    	}
    
    	*InputDataConsumed = (ULONG)length;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135

    果不其然,我们的ctrl键,被替换了。

  • 相关阅读:
    【湖科大教书匠】计算机网络随堂笔记第1章(计算机网络概述)
    你不讲武德自己偷着乐学习!spring Security五套「源码级」这份笔记哪里来的?
    如何在一个传统的html中,引入vueJs并使用vue复制组件?
    某手创作服务 __NS_sig3 sig3 | js 逆向
    Maven路上的疑难杂症
    C51--PWN-舵机控制
    gitflow工作流程思路
    mysql-联合查询
    保卫你的应用:探索过滤器和拦截器的奥秘
    vCenter学习笔记
  • 原文地址:https://blog.csdn.net/weixin_40425640/article/details/125535197