首页 > 程序开发 > 综合编程 > 安全编程 >

驱动笔记15:键盘过滤驱动学习笔记

2009-03-20

http://nokyo.blogbus.com/logs/36697074.html       键盘过滤驱动对于分层驱动的学习是一个很好的例子,它相对文件过滤驱动来说较为简单,也更容易理解。     并不是所有的驱动都需要直接访问硬

http://nokyo.blogbus.com/logs/36697074.html

键盘过滤驱动对于分层驱动的学习是一个很好的例子,它相对文件过滤驱动来说较为简单,也更容易理解。

并不是所有的驱动都需要直接访问硬件的,事实上几乎所有的硬件设备都存在着驱动程序链,最底层的驱动程序可以直接访问硬件,并对上层提供透明服务,最上层的驱动程序只要对接收到的数据进行过滤、格式化等处理即可,这样大大减少了开发的难度。

我这次学习的对象是KLOG,但我将它的代码进行了精简,这样使得它的工作流程更容易被看清楚。

首先看DriverEntry例程:

NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING RegistryPath
)
{
int i = 0;
PDEVICE_EXTENSION pKeyboardDeviceExtension;

for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
pDriverObject->MajorFunction[i] = DispatchPassDown;
}

pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;
pDriverObject->DriverUnload = Unload;

// 开启记录
HookKeyboard(pDriverObject);

// 设置DEVICE_EXTERSION
pKeyboardDeviceExtension = (PDEVICE_EXTENSION)pDriverObject->DeviceObject->DeviceExtension;

return STATUS_SUCCESS;
}

很容易看懂,我就不多说了,其中DispatchPassDown仅仅将接收到的IRP转发给下一层设备,代码就不贴了。下面我们先来看其中的重头戏之一:HookKeyboard。

NTSTATUS
HookKeyboard(
IN PDRIVER_OBJECT pDriverObject
)
{
NTSTATUS status;
PDEVICE_OBJECT pKeyboardDeviceObject;
PDEVICE_EXTENSION pKeyboardDeviceExtension;
STRING ntNameString;
UNICODE_STRING uKeyboardDeviceName;
CCHAR ntNameBuffer[64] = "\Device\KeyboardClass0";

// 创建设备
status = IoCreateDevice(pDriverObject,
sizeof(DEVICE_EXTENSION),
NULL,
FILE_DEVICE_KEYBOARD,
0,
TRUE,
&pKeyboardDeviceObject);
if (!NT_SUCCESS(status))
{
return status;
}

pKeyboardDeviceObject->Flags |= (DO_BUFFERED_IO | DO_POWER_PAGABLE);
pKeyboardDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

// 设置DEVICE_EXTENSION
RtlZeroMemory(pKeyboardDeviceObject->DeviceExtension, sizeof(DEVICE_EXTENSION));
pKeyboardDeviceExtension = (PDEVICE_EXTENSION)pKeyboardDeviceObject->DeviceExtension;

// 绑定到设备
RtlInitAnsiString(&ntNameString, ntNameBuffer);
RtlAnsiStringToUnicodeString(&uKeyboardDeviceName, &ntNameString, TRUE);
IoAttachDevice(pKeyboardDeviceObject, &uKeyboardDeviceName, &pKeyboardDeviceExtension->pKeyboardDevice);
RtlFreeUnicodeString(&uKeyboardDeviceName);

return STATUS_SUCCESS;
}

首先IoCreateDevice不用多说了,在任何一个驱动程序中都会见到,关键是下面的Flags设置和IoAttachDevice。我们创建了设备之后,需要将其加入到键盘的驱动程序链中,这个具体转化为代码就是将我们的设备Attatch到"\Device\KeyboardClass0"中(当然不一定非得是这个设备,挂这个设备的主要目的是为了能够动态卸载我们的驱动,因此不能挂接更上层的设备)。

为了得到按键操作的信息,我们发送一个IRP_MJ_READ到驱动的设备栈,由于此时还不一定有按键产生,于是驱动程序把这个IRP标记为pending状态,一旦有按键产生,则马上把这个IRP完成,所以我们需要在IRP_MJ_READ的处理例程中设置一个完成例程。(因为我们工作的异步模式下)

NTSTATUS
DispatchRead(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
{
PIO_STACK_LOCATION currentIrpStack;
PIO_STACK_LOCATION nextIrpStack;


currentIrpStack = IoGetCurrentIrpStackLocation(pIrp);
nextIrpStack = IoGetNextIrpStackLocation(pIrp);
*nextIrpStack = *currentIrpStack;
// 设置完成例程
IoSetCompletionRoutine(pIrp, OnReadCompletion, pDeviceObject, TRUE, TRUE, TRUE);

return IoCallDriver(((PDEVICE_EXTENSION)pDeviceObject->DeviceExtension)->pKeyboardDevice, pIrp);
}


在这个完成例程OnReadCompletion中,我们就可以获取按键的信息了,代码如下所示:
NTSTATUS
OnReadCompletion(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp,
IN PVOID Context
)
{
PDEVICE_EXTENSION pKeyboardDeviceExtension;
PKEYBOARD_INPUT_DATA keys;
int numKeys;
int i = 0;

pKeyboardDeviceExtension = (PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;

if (pIrp->IoStatus.Status == STATUS_SUCCESS)
{
keys = (PKEYBOARD_INPUT_DATA)pIrp->AssociatedIrp.SystemBuffer;
numKeys = pIrp->IoStatus.Information / sizeof(KEYBOARD_INPUT_DATA);

for(i = 0; i < numKeys; i++)
{
DbgPrint("ScanCode: %x ", keys[i].MakeCode);

// if(keys[i].Flags == KEY_BREAK)
// DbgPrint("%s ", "Key Up");

相关文章
最新文章
热点推荐