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

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

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

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

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

Windows 10驱动开发入门(四):USB下的过滤器驱动_第1张图片
右键,更新驱动。

Windows 10驱动开发入门(四):USB下的过滤器驱动_第2张图片

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

Windows 10驱动开发入门(四):USB下的过滤器驱动_第3张图片

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

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

Windows 10驱动开发入门(四):USB下的过滤器驱动_第4张图片
我们的驱动安装成功,重新启动虚拟机。

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

Windows 10驱动开发入门(四):USB下的过滤器驱动_第5张图片
这时候可以打开DebugView,看看log输出了哪些内容。

Windows 10驱动开发入门(四):USB下的过滤器驱动_第6张图片
提示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;
}

驱动的入口,注意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;
}

驱动添加函数,注意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;

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;
}

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

你可能感兴趣的:(Windows,10驱动开发入门,驱动开发)