windows输入法注入原理入门
本帖最后由 淡淡哇 于 2020-8-20 23:38 编辑[*]前言
[*]输入法简介
[*]输入法安装
[*]输入法设置
[*]输入法注入
[*]输入法卸载
[*]参考资料
https://static.52pojie.cn/static/image/hrline/1.gif
前言
一直我都没有认真学过输入法注入,直到最近有时间想学习一下,才发现相关资料其实还是有一定的缺乏的,我参考了部分资料,以个人的理解写了这篇文章,由于本人编程基础较弱,可能与实际存在一定的出入
在这里感谢李恒道的帮助
https://static.52pojie.cn/static/image/hrline/1.gif
输入法简介
windows下输入法一般分为两种,一种是外挂式,一种是ime式
外挂式一般是通过键盘钩子对输入进行拦截,然后发送给对应的窗口
ime式则是通过系统提供的ime框架的基础上实现输入法,虽然叫ime,但是ime实际上是dll
ime调用流程:
键盘事件 应用程序
↓ ↓
Windows的USER.EXE
↓ ↑
IME管理(应该是imm32.dll)
↓ ↑
输入法
既然是在系统提供的ime框架上进行开发,框架就有一定的标准接口,标准接口的函数如下
ImeConversionList //将字符串或字符转换成目标字串
ImeConfigure //配置当前ime参数函数
ImeDestroy //退出当前使用的IME
ImeEscape //应用软件访问输入法的接口函数
ImeInquire //启动并初始化当前ime输入法
ImeProcessKey //ime输入键盘事件管理函数
ImeSelect //启动当前的ime输入法
ImeSetActiveContext //设置当前的输入处于活动状态
ImeSetCompositionString //由应用程序设置输入法编码
ImeToAsciiEx //将输入的键盘事件转换为汉字编码事件
NotifyIME //ime事件管理函数
ImeRegisterWord //向输入法字典注册字符串
ImeUnregisterWord //删除被注册的字符串
ImeGetRegisterWordStyle
ImeEnumRegisterWord
UIWndProc //用户界面接口函数
StatusWndProc //状态窗口注册函数
CompWndProc //输入编码窗口注册函数
CandWndProc //选择汉字窗口注册函数
虽然看着很多,但是我们并不需要了解所有,只需要明白一些关键的函数即可明白输入法注入
https://static.52pojie.cn/static/image/hrline/1.gif
输入法安装
我们想要使用输入法,第一步是将输入法安装到系统里,把ime文件写入目录C:\WINDOWS\system32后调用api ImmInstallIME
ImmInstallIME
函数原型:
HKL ImmInstallIME(LPCTSTR lpszIMEFileName, LPCTSTR lpszLayoutText);
函数的两个参数分别为输入法IME文件的文件名和在控制面板的是输入法选项中显示的输入法名称。函数调用后将返回一个被安装输入法的输入法标识符(或称做输入法句柄)。
示例代码:
HKL hKL = ImmInstallIME("c:\\winwb86.ime", "王码五笔型输入法86版");
这样就会成功安装输入法了
https://static.52pojie.cn/static/image/hrline/1.gif
输入法设置
接下来我们使用IMESetPubString对输入法设置注入的dll路径(这里的api是imedllhost09.ime文件内的)
输入法的源码如下
int WINAPI IMESetPubString(LPCTSTR tmpStr,DWORD UnloadDLL,DWORD loadNextIme,DWORD DllData1,DWORD DllData2,DWORD DllData3)
{
CallBackData1=DllData1;
CallBackData2=DllData2;
CallBackData3=DllData3;
OnloadDllWhenExit=UnloadDLL;
LoadNextWhenActive=loadNextIme;
memset(g_IMEDLLString,0,802);
if (lstrlen(tmpStr)>800)
{
lstrcpyn(g_IMEDLLString,tmpStr,800);
}
else
{
lstrcpy(g_IMEDLLString,tmpStr);
}
return 1;
}
tmpStr是dll的路径
UnloadDLL是输入法退出的时候是否卸载dll 0代表是 1代表否
loadNextIme切换目标输入法的时候是否直接切换到下一个输入法 0代表否 1代表是
DllData1 数据1
DllData2 数据2
DllData3 数据3
从这里开始我们来理解一下调用逻辑
因为程序调用了imedllhost09.ime的IMESetPubString
所以会使用kernel32.LoadLibraryA加载模块,然后调用kernel32.GetProcAddress获取IMESetPubString,也就是说我们一般是第一个加载了imedllhost09.ime输入法模块的。
接下来我们捋一捋输入法内部的流程
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
if(!ImeClass_Register(hinstDLL)) return FALSE; // DLL加载时注册必须的UI基本窗口类
MyLoadCilentDLLFun();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
ImeClass_Unregister(hinstDLL);// DLL退出时注销注册的窗口类
if (CilentDLL!=NULL && OnloadDllWhenExit==0)
{
FreeLibrary(CilentDLL); // 输入法退出时卸载客户DLL
}
break;
default:
break;
}
return true;
}
if(!ImeClass_Register(hinstDLL)) return FALSE;是注册了一个基本的窗口类
然后调用了函数MyLoadCilentDLLFun();
接下来我们看看MyLoadCilentDLLFun();
void MyLoadCilentDLLFun()
{
if (CilentDLL==NULL)
{
if (lstrlen(g_IMEDLLString)>0)
{
CilentDLL=LoadLibrary(g_IMEDLLString); // 在输入法加载时同时加载客户DLL
if (CilentDLL!=NULL)
{
// 如果存在,则调用客户DLL指定名称的回调函数
RunDllCallBackX=(RUNDLLHOSTCALLBACK)GetProcAddress(CilentDLL,"RunDllHostCallBack");
if (RunDllCallBackX!=NULL)
{
RunDllCallBackX(CallBackData1,CallBackData2,CallBackData3);
}
}
}
}
}
首先判断CilentDLL是否为空,因为我们刚初始化肯定是空的,然后调用获取g_IMEDLLString字符串长度,判断是否大于0,如果大于0再执行注入,这里我们刚初始化的,肯定为空,所以执行逻辑结束
执行完dllmain入口函数后,会紧接着执行ImeInquire(),ImeInquire是输入法初始化过程,这里我们不再叙述
输入法内部的流程我们大概梳理完了,接下来我们来看IMESetPubString函数
int WINAPI IMESetPubString(LPCTSTR tmpStr,DWORD UnloadDLL,DWORD loadNextIme,DWORD DllData1,DWORD DllData2,DWORD DllData3)
{
CallBackData1=DllData1;
CallBackData2=DllData2;
CallBackData3=DllData3;
OnloadDllWhenExit=UnloadDLL;
LoadNextWhenActive=loadNextIme;
memset(g_IMEDLLString,0,802);
if (lstrlen(tmpStr)>800)
{
lstrcpyn(g_IMEDLLString,tmpStr,800);
}
else
{
lstrcpy(g_IMEDLLString,tmpStr);
}
return 1;
}
这里我们可以看到IMESetPubString是经过了一些简单的赋值,到这里我们的输入法设置已经结束
https://static.52pojie.cn/static/image/hrline/1.gif
输入法注入
注入到目标程序使用的是SendMessageA (WinHwnd, 80, 1, ImeHwnd)
WinHwnd是目标程序窗口句柄
ImeHwnd是输入法句柄
这里的80,1我没有查阅到具体的资料,认为是通过spy++抓取的
目标程序加载了imedllhost09.ime后同样会执行dllmain函数
与之前不同的是这次g_IMEDLLString是已经被赋值的了
为什么在自己程序里设置g_IMEDLLString,而在其他程序也可以生效呢?
#pragma data_seg("mysechx")
DWORD CallBackData1=0;
DWORD CallBackData2=0;
DWORD CallBackData3=0;
DWORD OnloadDllWhenExit=0; // 当输入法退出时是否卸载客户DLL0-是,1-否
DWORD LoadNextWhenActive=0; // 当本输入法激活时,是否自动打开下一个输入法 0-否,1-是
char g_IMEDLLString="";
#pragma data_seg()
#pragma data_seg()用于dll中,在dll中定义一个共享的,有名字的数据段
这个数据段的变量可以被多个进程共享,否则多个进程之间无法共享dll的变量
注意:这里的共享数据必须进行初始化
这样我们就实现了跨进程的数据通信,接下来我们继续看MyLoadCilentDLLFun函数
void MyLoadCilentDLLFun()
{
if (CilentDLL==NULL)
{
if (lstrlen(g_IMEDLLString)>0)
{
CilentDLL=LoadLibrary(g_IMEDLLString); // 在输入法加载时同时加载客户DLL
if (CilentDLL!=NULL)
{
// 如果存在,则调用客户DLL指定名称的回调函数
RunDllCallBackX=(RUNDLLHOSTCALLBACK)GetProcAddress(CilentDLL,"RunDllHostCallBack");
if (RunDllCallBackX!=NULL)
{
RunDllCallBackX(CallBackData1,CallBackData2,CallBackData3);
}
}
}
}
}
此时CilentDLL依然为null,但g_IMEDLLString已经在IMESetPubString被赋值
接下来会调用LoadLibrary加载注入dll
然后调用GetProcAddress获取注入dll的RunDllHostCallBack回调函数
并且调用回调函数传入CallBackData1,CallBackData2,CallBackData3三个数据
至此我们的输入法注入彻底结束了
https://static.52pojie.cn/static/image/hrline/1.gif
输入法卸载
停止注入调用的函数是IMEClearPubString,让我们看一下输入法的IMEClearPubString函数
int WINAPI IMEClearPubString()
{
CallBackData1=0;
CallBackData2=0;
CallBackData3=0;
OnloadDllWhenExit=0;
LoadNextWhenActive=0;
memset(g_IMEDLLString,0,802);
return 1;
}
依然是对变量进行简单的赋值
接下来卸载输入法
删除以下注册表内的输入法标号(此处来自精易模块)
输入法标号的获取
首先调用GetKeyboardLayoutList来获取输入法的数量
然后调用LoadKeyboardLayoutA获取每个输入法的句柄
通过判断输入法的句柄是否相等来获得输入法标号
接下来删除对应的注册表就可以了
“Keyboard Layout\Preload\”
“SYSTEM\CurrentControlSet\Control\Keyboard Layouts\”
“S-1-5-21-1060284298-606747145-682003330-500\Keyboard Layout\Preload”
然后调用UnloadKeyboardLayout卸载输入法
并且删除imedllhost09.ime以及对应的缓存文件
缓存文件的路径以及名称在注册表的“SYSTEM\CurrentControlSet\Control\Keyboard Layouts\”下
输入法的表示路径在注册表的 “Keyboard Layout\Preload\”下
https://static.52pojie.cn/static/image/hrline/1.gif
参考资料
参考源码https://www.52pojie.cn/forum.php?mod=viewthread&tid=38537&highlight=%CA%E4%C8%EB%B7%A8%D7%A2%C8%EB
这里感谢lzq大神
IME输入法编程心得 https://www.cnblogs.com/freedomshe/archive/2012/11/30/ime_learning.htmlIME输入法编程溯源 https://www.cnblogs.com/freedomshe/archive/2012/11/13/ime-resources.html
加密与解密第四版https://detail.tmall.com/item.htm?spm=a230r.1.14.16.1e7d38f5rT1yMd&id=580607194609&ns=1&abbucket=10
易语言输入法注入http://www.511yj.com/eyuyan-zr-srf.html
dll技术之输入法注入 https://blog.csdn.net/qq446569365/article/details/71155557
精益模块注入法注入 http://ec.125.la/
https://static.52pojie.cn/static/image/hrline/1.gif
顶一下.2楼不要说什么检测啥的. 毕竟你学习的是注入.是技术.不是让你来 做外挂的. 任何技术一旦分享出来都会面临着对抗技术的升级. 输入法当时很多主流的游戏都检测不到.或者没办法检测. 但是公开了也就这样了. 学一下技术最好.了解下原理. 自己顶一下我自己! 输入法注入容易被检测到 关键是要了解各种系统函数的用法 这注入不知道咋样 IBinary 发表于 2020-8-21 16:21
顶一下.2楼不要说什么检测啥的. 毕竟你学习的是注入.是技术.不是让你来 做外挂的. 任何技术一旦分享出来都 ...
现在就别说注入了 就是真的输入法游戏也用不了啊 支持一下 厉害,我一直直接用的类似精益模块的文本_投递 xuxunyue 发表于 2020-8-21 16:11
输入法注入容易被检测到
直接注入到游戏,更容易被检测。