键盘过滤驱动(驱动笔记15 – 键盘过滤驱动学习笔记)

发布时间:2025-12-10 19:37:21 浏览次数:33

驱动笔记15 – 键盘过滤驱动学习笔记-键盘过滤驱动怎么安装

驱动笔记15 – 键盘过滤驱动学习笔记键盘过滤驱动对于分层驱动的学习是一个很好的例子,它相对文件过滤驱动来说较为简单,也更容易理解。并不是所有的驱动都需要直接访问硬件的,事实上几乎所有的硬件设备都存在着驱动程序链,最底层的驱动程序可以直接访问硬件,并对上层提供透明服务,最上层的驱动程序只要对接收到的数据进行过滤、格式化等处理即可,这样大大减少了开发的难度。我这次学习的对象是KLOG,但我将它的代码进行了精简,这样使得它的_pkeyboard_input_data

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

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

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

首先看DriverEntry例程:

NTSTATUS

DriverEntry(




INPDRIVER_OBJECT


pDriverObject,




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





returnSTATUS_SUCCESS;

}

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

NTSTATUS

HookKeyboard(




INPDRIVER_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))




{









returnstatus;




}




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




RtlAnsiStringToUnicodeSt
ring(
&uKeyboardDeviceName,
&ntNameString,TRUE);




IoAttachDevice(pKeyboardDeviceObject,
&uKeyboardDeviceName,
&pKeyboardDeviceExtension

->pKeyboardDevice);




RtlFreeUnicodeString(
&uKeyboardDeviceName);





returnSTATUS_SUCCESS;

}

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

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

NTSTATUS

DispatchRead(




INPDEVICE_OBJECT


pDeviceObject,




INPIRP






pIrp

)

{




PIO_STACK_LOCATION


currentIrpStack;




PIO_STACK_LOCATION


nextIrpStack;




currentIrpStack
=IoGetCurrentIrpStackLoca
tion(pIrp);




nextIrpStack
=IoGetNextIrpStackLocatio
n(pIrp);





*nextIrpStack
=
*currentIrpStack;





// 设置完成例程




IoSetCompletionRoutine(pIrp, OnReadCompletion, pDeviceObject, TRUE,TRUE, TRUE);










returnIoCallDriver(((PDEVICE_EXTENSION)pDeviceObject
->DeviceExtension)
->pKeyboardDevice,pIrp);

}

在这个完成例程OnReadCompletion中,我们就可以获取按键的信息了,代码如下所示: NTSTATUS

OnReadCompletion(




INPDEVICE_OBJECT


pDeviceObject,




INPIRP














pIrp,




INPVOID










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
\n
“,keys[i].MakeCode);


















//if(keys[i].Flags == KEY_BREAK)





//DbgPrint(“%s\n”, “KeyUp”);





//if(keys[i].Flags == KEY_MAKE)





//DbgPrint(“%s\n”, “Key Down”);








}




}










if(pIrp
->PendingReturned)




{








IoMarkIrpPending(pIrp);




}





return pIrp
->IoStatus.Status;

}

不过这里我们获取到是只是按键的扫描码,如果想知道直观的按键信息,还需要写个函数进行处理,这里我就给省略了。虽然这个程序比起rootkit.com上面那个KLOG简单的多,使得它的流程更加清楚,也更容易理解,但它不实用。

为什么呢?因为实用的键盘记录器应该能够将按键信息保存起来,这里我们只能通过DbgView来看到按键信息。但由于这个完成例程的IRQL为DISPATCH,不能进行文件操作,因此在KLOG的代码中是首先创建一个线程,然后在线程中进行保存操作,因为线程函数工作在PASSIVE,允许处理文件。

一旦涉及到线程和文件操作就变得麻烦多了,关于这点可以通过KLOG的代码看出来,其实也不难,就是需要考虑的东西较多而已。

最后说一下,这个程序还有一个BUG,就是在卸载的时候再有按键就会BSOD,因为在我们卸载驱动的时候,还有很多IRP处于pending状态,当我们被卸载后,再有按键操作,IRP返回后却找不到对应的驱动,就会造成蓝屏的后果。

关于这个问题,网上有很多文章都介绍了介绍的办法,我也没有实验过,就不废话了。

需要做网站?需要网络推广?欢迎咨询客户经理 13272073477