所谓的驱动过滤器
,如果之前有做过windows
的API
相关的hook
,那就比较好理解了。道理跟API
的hook
差不多,虽然在驱动程序里,程序的解释不见得这么简单,但是对于外部的表象来看,基本上就是hook
的用法。
在微软的demo
中,就有这样的一个例子。kbfiltr
先看看kbfiltr
到底做了什么事情,能做什么事情。用源码编译好相关的驱动。
打开设备管理器,选择键盘的那一项。
把驱动拷贝到某个目录下,安装驱动。
出现驱动安装的提示,始终安装驱动。
安装完成以后,键盘的驱动变为如下:
当我们进入计算机以后,发现计算机的crtl + c
键无效了,不能进行复制。
这时候可以打开DebugView
,看看log
输出了哪些内容。
过滤驱动
,拦截了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
键,被替换了。