一 .输入法原理:
输入法的ime文件实质是个dll文件,导出windows的imm输入法管理器调用的一些回调函数。
初始化:
DllMain里注册窗口类
1.
ImeInquire里告诉imm输入法的me消息窗口的类名
2.
imm根据这个类名创建ime消息窗口
3.
消息窗口的回调函数被调用, 消息是wm_create
至此, 用户就可以开始使用输入法了,之后顺序是:
1.
用户按下一个键
2.
ImeProcessKey返回true, 表示输入法想处理这个键
3.
ImeToAsciiEx被调用
4.
此函数创建一些ime消息, 如开始/结束输入法编辑, 设置comp串, cand串, 显示comp窗口, 显示状态栏窗口, 提交等
5.
相应的窗口被创建, 显示,
6.
如果当前的应用程序是懂输入法的, 它自己也会显示comp串, 前提是你的ImeToAsciiEx需要告诉它comp串是什么
二.输入法注入原理总结:
输入法ime文件实现功能 :
1.导出imm输入法管理器要求的几个必须的函数
2.导出一个初始化和一个释放空间的函数.初始化函数用于设置目标dll的路径及回调函数的参数信息。释放空间函数用于重置回调函数参数及恢复dll加载的参数。
3.在dllmain里通过LoadLibrary加载注入的目标dll
3.(可选)可以通过GetProcAddress回调目标dll实现的某个回调函数,函数声明格式固定。
安装卸载输入法ime的主程序实现功能:
1.通过ImmInstallIME安装已经实现的输入法文件。
2.如果安装失败,通过GetKeyboardLayoutList得到输入法列表,判断是否已经安装,给错误提示.
3.安装成功的话,调用ime文件的导出的初始化函数,实现对目标dll的初始化.
4.可以通过FindWindowsEx遍历窗口或者只给顶层窗口,实现对所有窗口或者顶层窗口发送
WM_INPUTLANGCHANGEREQUEST消息,激活输入法。
目标dll:
1.导出回调函数供输入法ime文件调用
2.自己实现任意功能。
3火星文输入法外挂中是RegisterWindowMessage注册消息钩子与主程序通信。然后inline hook前五个字节,hook imm32.lib的ImmGetCompositionStringA和ImmGetCompositionStringW函数,实现对输入文字的修改.
三. 目前考虑的防护方式:
关于主防拦截:
1.输入法安装主要通过ImmInstallIME()函数,在驱动中过滤这个函数,实现对输入法安装拦截.
2.输入法安装后需要通过发送WM_INPUTLANGCHANGEREQUEST消息在指定窗口中激活输入法。在驱动中通过过滤NtUserGetMessage,拦截消息处理。
3.输入法安装需修改两处注册表值,在驱动中检测对这两处注册表值得更改。
关于加载输入法后的检测自保:
1.通过过滤NtImeSetValue函数得到输入的按键信息,检测已经安装的输入法。
2.通过过滤WM_IME_COMPOSITION消息得到CompositionString,在消息的INPUTCONTEXT 中得到按键的输入的字符。
3.输入法加载后一般会在输入法中加载目标dll注入到目标进程中,可以过滤loadlibrary等函数做一般的dll注入的检测。
4.过滤imexxx函数即输入法调用实现的相关内核中的函数检测输入法的功能调用
四. 第三代输入法注入样本:
以前的输入法注入是通过在伪造的输入法中加载恶意的dll,需要伪造输入法文件,恶意dll和安装输入法的控制程序。
在 http://blogs.avg.com/news-threats/ime-injection-evolution/ ;
这个avg的样本中所述的第三代输入法注入样本是hook 了ZwQueryValueKey函数。
原理如下:
1.用户在每次激活输入法时,系统会调用ImmLoadLayout激活载入键盘布局文件,即输入法文件,参数为文件的路径。
系统在ImmLoadLayout通过ZwQueryValueKey读注册表查询ime file name及路径,由于恶意软件hook了ZwQueryValueKey函数,返回任意文件名,
这个任意文件就是恶意代码文件。
2.恶意软件向explorer 进程发送 WM_INPUTLANGCHANGEREQUEST 消息激活输入法。Explorer进程会调用 ImmLoadIME,在这个函数中,会调用loadlibrary装载
ImmGetImeInfoEx返回的一个dll文件,这个dll文件就是第1步中的任意文件。
通过这两步,这个恶意dll文件即被explore进程装载
这种不需要安装输入法文件的输入法注入方式不用调用ImmInstallIME()安装输入法,故隐蔽性更好。
故对输入法注入的防护需要对ImmLoadLayout()函数的过滤监控.
五.参考资料:
1.泄露win2k源码(win2k\private\ntos\w32\ntuser\imm),reactos的win32k.sys代码(ReactOS-0.3.15-REL-src\ntoskrnl\ex\win32k.c)
过程如下:
安装时ImmInstallIME(ime路径,输入法名字)
该部分代码在imm32.dll中,我逆向xp的代码和泄露源码基本一样。
LoadVersionInof(判断imm版本号)-->GetImeLayout(获取当前键盘布局,如果有重名的,做冲突或升级处理)?AssignNewLayout(根据最大的ime handl计算一个新的id号给新输入法)
?WriteImeLayout(在注册表中新建一个键项,设置imefile,layout file,layout text值)->LoadKeyboardLayout(调用win32k.sys的 NtUserLoadKeyboardLayoutEx)
然后,在win32k.sys中,经过逆向,处理过程如下
遍历列表在链表中增加新输入法元素->_xxxInternalActivateKeyboardLayout(向本线程发送WM_INPUTLANGCHANGEREQUEST消息激活输入法)-> LoadKeyboardLayoutFile()->ReadLayoutFile(通过ZwCreateSection,
ZwMapViewOfSection内存添加目标的输入法dll文件)
typedef struct { //属性信息,没句柄,没路径
DWORD dwPrivateDataSize;
DWORD fdwProperty;
DWORD fdwConverstionCaps;
DWORD fdwSentenceCaps;
DWORD fdwUICaps;
DWORD fdwSCSCaps;
DWORD fdwSelectCaps;
} IMEINFO;
typedef struct tagIMEINFOEX { //有句柄,有路径,只有它包含路径,区分标识一个路径,全局的存在驱动里
HKL hkl; //0
IMEINFO ImeInfo; //4
WCHAR wszUIClass[IM_UI_CLASS_SIZE]; //size=16 32
DWORD fdwInitConvMode; // Check this later //64
BOOL fInitOpen; // Check this later //68
BOOL fLoadFlag; //72
DWORD dwProdVersion; //76
DWORD dwImeWinVersion; //80
WCHAR wszImeDescription[IM_DESC_SIZE]; //size=50 84
WCHAR wszImeFile[IM_FILE_SIZE]; //size=80 184 只有文件名,路径默认system32
} IMEINFOEX, *PIMEINFOEX; //184+80*2=0x158
typedef struct tagIMEDPI { //immuser.h //有句柄,有函数,没路径,区分标识dll文件,全局链表的存在imm32.dll
struct tagIMEDPI *pNext;
HANDLE hInst;
HKL hKL;
IMEINFO ImeInfo;
DWORD dwCodePage;
WCHAR wszUIClass[IM_UI_CLASS_SIZE];
DWORD cLock;
DWORD dwFlag;
struct _tagImeFunctions {
union {PFNINQUIREA a; PFNINQUIREW w; PVOID t;} ImeInquire;
union {PFNCONVLISTA a; PFNCONVLISTW w; PVOID t;} ImeConversionList;
union {PFNREGWORDA a; PFNREGWORDW w; PVOID t;} ImeRegisterWord;
union {PFNUNREGWORDA a; PFNUNREGWORDW w; PVOID t;} ImeUnregisterWord;
union {PFNGETREGWORDSTYA a; PFNGETREGWORDSTYW w; PVOID t;} ImeGetRegisterWordStyle;
union {PFNENUMREGWORDA a; PFNENUMREGWORDW w; PVOID t;} ImeEnumRegisterWord;
PFNCONFIGURE ImeConfigure;
PFNDESTROY ImeDestroy;
PFNESCAPE ImeEscape;
PFNPROCESSKEY ImeProcessKey;
PFNSELECT ImeSelect;
PFNSETACTIVEC ImeSetActiveContext;
PFNTOASCEX ImeToAsciiEx;
PFNNOTIFY NotifyIME;
PFNSETCOMPSTR ImeSetCompositionString;
PFNGETIMEMENUITEMS ImeGetImeMenuItems;
} pfn;
} IMEDPI, *PIMEDPI;
HKEY_CURRENT_USER\keyboard layout\preload 当前
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts]
typedef struct tagIMELAYOUT { //暂时不用看
HKL hImeKL;
WCHAR szKeyName[HEX_ASCII_SIZE];
WCHAR szImeName[IM_FILE_SIZE];
} IMELAYOUT, *PIMELAYOUT;
kd> dd f88e94a0
f88e94a0 e0010804 00000008 8e050338 0011da50
f88e94b0 0007f670 746af7ca 0011da50 0007f930
f88e94c0 746b0577 000a71d0 000000f5 0011da50
f88e94d0 00000000 0011e798 746b059b 7c939d8a
f88e94e0 00000009 000a7624 00000042 7d5c44a9
f88e94f0 0007f9bc 00010094 00000001 0007f694
f88e9500 77d18830 76313c0c 76316020 76313c1a
f88e9510 ffffffff 7d5c4513 77d18734 00010094
kd> dd
f88e9520 00000014 01010056 00000000 746b6adb
f88e9530 00000000 0007f7bc 7c8328f4 0007f7e4
f88e9540 001d8a20 00000010 0007f7e4 e0010804
f88e9550 7c811726 0007f65c 77d1882a 00000000
f88e9560 000cc0d8 e0010804 001200a5 0011da10
f88e9570 7c9378b0 746b6adb 00000000 00161468
f88e9580 0007fd94 00161480 0007f7a0 7c93795c
f88e9590 00161480 00000000 000e7b20 00000008
kd> dd f88e94a0+158-a0
f88e9558 77d1882a 00000000 000cc0d8 e0010804
f88e9568 001200a5 0011da10 7c9378b0 746b6adb
f88e9578 00000000 00161468 0007fd94 00161480
f88e9588 0007f7a0 7c93795c 00161480 00000000
f88e9598 000e7b20 00000008 7c937790 746b6adb
f88e95a8 00000000 00000020 0007f7d4 000c87a0
f88e95b8 7ffdd000 0007f79c 00090000 7c930202
f88e95c8 0000000c 000908c8 00090000 00129308
kd> p
win32k!NtUserGetImeInfoEx+0x7a:
bf914b49 898588feffff mov dword ptr [ebp-178h],eax
kd> dd f88e94a0
f88e94a0 e0010804 0006e0e8 7c92cfd0 00000000
f88e94b0 0006e214 7c92e900 7c930208 00000000
f88e94c0 01109b28 0006e030 00070000 7c930202
f88e94d0 00000016 00070aa8 00070000 010f9a98
f88e94e0 0006e008 0000001e 00000000 00050001
f88e94f0 00040000 62fc5168 51658f93 00206cd5
f88e9500 002e0035 00200030 00007248 00000000
f88e9510 00000000 7c92cfd0 7c9350f2 00000024
kd> dd f88e94a0+158-a0
f88e9558 00690077 0070006e 002e0079 006d0069
f88e9568 00000065 000706e8 0006e0dc 00070000
f88e9578 7c930202 00000005 00070778 00070000
f88e9588 01109848 0006e0b4 7c930202 0006e2f8
f88e9598 7c92e900 7c930208 ffffffff 7c930202
f88e95a8 7c93017b 7c9301bb 80000005 7c92d950
f88e95b8 7f6f31f8 7c80f8d8 0010000e 7c80f3ac
f88e95c8 7c92fe2a 00000008 0007ec18 00000000
kd> du f88e94a0+158-a0
f88e9558 "winpy.ime"
bf914b3c 6a00 push 0 下断
bf914b3e e8f9eceeff call win32k!_GetProcessWindowStation (bf80383c)
bf914b43 50 push eax
bf914b44 e8ba2c0100 call win32k!GetImeInfoEx (bf927803)
bf914b49 898588feffff mov dword ptr [ebp-178h],eax ss:0010:f88e949 下断
08040804
|