Hook分发函数
前一篇文章讲述了进行键盘过滤,截取用户输入的方法。本篇文章开始更加深入地讨论键盘的过滤与反过滤对抗。无论是过滤还是饭过滤,原理都是过滤,取胜的关键在于谁第一个得到信息。
一种方发是Hook分发函数,即将键盘驱动的分发函数替换成自己的函数用来达到过滤的目的。
1.获得类驱动对象
首先要获得键盘类驱动对象,才能去替换下面的分发函数。这个操作较为简单,因为这个驱动的名字是“\\Device\\Kbdclass”,所以可以直接用函数ObReferenceObjectByName来获取。
代码如下:
//驱动的名字
#define KBD_DRIVER_NAME L"\\Driver\\Kdbclass"
//当我们求得驱动对象指针时,将其放到这里
PDRIVER_OBJECT KdbDriverObject;
UNICODE_STRING uniNtNameString;
//初始化驱动的名字字符串
RtlInitUnicodeString(&uniNtNameString,KBD_DRIVER_NAME);
//根据名字字符串来获得驱动对象
status = ObReferenceObjectByName(
&uniNtNameString,
OBJ_CASE_INSENSITIVE,
NULL,
0,
IoDriverObjectType,
KernelMode,
&KdbDriverObject,
);
if (!NT_SUCCESS(status))
{
//如果失败
DbgPrint("MyAttach:Couldn't get the kbd driver Object\n");
return STATUS_SUCCESS;
}
else
{
//凡是调用了Reference系列的函数都要通过调用ObDereferenceObject来解除引用
ObDereferenceObject(KdbDriverObject);
}
这样就获得了驱动对象,然后只要替换其分发函数就行了。
2.修改类驱动的分发函数指针
虽然驱动对象不同,但是替换的方法还是一样的。值得注意的,必须保存原有的驱动对象的分发函数;否则,第一,替换之后将无法恢复;第二,完成我们自己的处理后无法继续调用原有的分发函数。
这里用到一个原子操作:InterlockedExchangePointer.这个操作的好处是,用户设置新的函数指针是原子的,不会被打断。插入其他可能要执行到调用这些分发函数的其他代码
//这个数组用来保存所有旧的指针
ULONG i;
PDRIVER_DISPATCH OldDispatchFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
.....
//把所有的分发函数指针替换成我们自己编写的同一个分发函数
for (i = 0 ; i <= IRP_MJ_MAXIMUM_FUNCTION ; ++i)
{
//假设MyFilterDispatch是笔者已经写好的一个分发函数
OldDispatchFunction[i] = KdbDriverObject->MajorFunction[i];
//进行原子交换操作
InterlockedExchangePointer(
&KbdDriverObject->MajorFunction[i],
MyFilterDispatch
);
}
3.类驱动之下的端口驱动
前面的过滤方式是替换分发函数指针。但是这是依然比较明显,因为分发函数的指针本来是已知的,如果安全监控软件有针对性地对这个指针进行检查和保护,就容易发现这个指针已经被替换掉的情况。
KbdClass被称为键盘类驱动,在Windows中,类驱动通常是指统管一类设备的驱动程序。不管是USB键盘,还是PS/2键盘均进过它,所以在这一层做拦截,能获得很好的通用性,类驱动之下和实际硬件交互的驱动被称为“端口驱动”。具体到键盘,i8042prt是PS/2键盘的端