吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7362|回复: 29
上一主题 下一主题
收起左侧

[C&C++ 原创] windows输入法注入原理入门

  [复制链接]
跳转到指定楼层
楼主
淡淡哇 发表于 2020-8-20 17:58 回帖奖励
本帖最后由 淡淡哇 于 2020-8-20 23:38 编辑

  • 前言
  • 输入法简介
  • 输入法安装
  • 输入法设置
  • 输入法注入
  • 输入法卸载
  • 参考资料



前言
一直我都没有认真学过输入法注入,直到最近有时间想学习一下,才发现相关资料其实还是有一定的缺乏的,我参考了部分资料,以个人的理解写了这篇文章,由于本人编程基础较弱,可能与实际存在一定的出入
在这里感谢李恒道的帮助

输入法简介
windows下输入法一般分为两种,一种是外挂式,一种是ime式
外挂式一般是通过键盘钩子对输入进行拦截,然后发送给对应的窗口
ime式则是通过系统提供的ime框架的基础上实现输入法,虽然叫ime,但是ime实际上是dll

ime调用流程:
键盘事件                应用程序
  ↓                   ↓
     Windows的USER.EXE
         ↓ ↑
       IME管理(应该是imm32.dll)
         ↓ ↑
        输入法
既然是在系统提供的ime框架上进行开发,框架就有一定的标准接口,标准接口的函数如下
[Asm] 纯文本查看 复制代码
    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      //选择汉字窗口注册函数

虽然看着很多,但是我们并不需要了解所有,只需要明白一些关键的函数即可明白输入法注入

输入法安装
我们想要使用输入法,第一步是将输入法安装到系统里,把ime文件写入目录C:\WINDOWS\system32后调用api ImmInstallIME
[Asm] 纯文本查看 复制代码
ImmInstallIME
函数原型:
HKL ImmInstallIME(LPCTSTR lpszIMEFileName, LPCTSTR lpszLayoutText);
函数的两个参数分别为输入法IME文件的文件名和在控制面板的是输入法选项中显示的输入法名称。函数调用后将返回一个被安装输入法的输入法标识符(或称做输入法句柄)。
示例代码:
HKL hKL = ImmInstallIME("c:\\winwb86.ime", "王码五笔型输入法86版");

这样就会成功安装输入法了

输入法设置
接下来我们使用IMESetPubString对输入法设置注入的dll路径(这里的api是imedllhost09.ime文件内的)
输入法的源码如下
[C] 纯文本查看 复制代码
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输入法模块的。
接下来我们捋一捋输入法内部的流程
[C] 纯文本查看 复制代码
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();
[C] 纯文本查看 复制代码
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函数
[C] 纯文本查看 复制代码
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是经过了一些简单的赋值,到这里我们的输入法设置已经结束

输入法注入
注入到目标程序使用的是SendMessageA (WinHwnd, 80, 1, ImeHwnd)
WinHwnd是目标程序窗口句柄
ImeHwnd是输入法句柄
这里的80,1我没有查阅到具体的资料,认为是通过spy++抓取的
目标程序加载了imedllhost09.ime后同样会执行dllmain函数
与之前不同的是这次g_IMEDLLString是已经被赋值的了

为什么在自己程序里设置g_IMEDLLString,而在其他程序也可以生效呢?

[Asm] 纯文本查看 复制代码
#pragma data_seg("mysechx")
DWORD CallBackData1=0;
DWORD CallBackData2=0;
DWORD CallBackData3=0;
DWORD OnloadDllWhenExit=0;    // 当输入法退出时是否卸载客户DLL  0-是,1-否
DWORD LoadNextWhenActive=0;    // 当本输入法激活时,是否自动打开下一个输入法 0-否,1-是
char g_IMEDLLString[802]="";
#pragma data_seg()

#pragma data_seg()用于dll中,在dll中定义一个共享的,有名字的数据段
这个数据段的变量可以被多个进程共享,否则多个进程之间无法共享dll的变量
注意:这里的共享数据必须进行初始化
这样我们就实现了跨进程的数据通信,接下来我们继续看MyLoadCilentDLLFun函数
[C] 纯文本查看 复制代码
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三个数据
至此我们的输入法注入彻底结束了

输入法卸载
停止注入调用的函数是IMEClearPubString,让我们看一下输入法的IMEClearPubString函数
[C] 纯文本查看 复制代码
int WINAPI IMEClearPubString()
{
        CallBackData1=0;
        CallBackData2=0;
        CallBackData3=0;
        OnloadDllWhenExit=0;
        LoadNextWhenActive=0;

        memset(g_IMEDLLString,0,802);
        return 1;
}

依然是对变量进行简单的赋值

接下来卸载输入法
删除以下注册表内的输入法标号(此处来自精易模块)
[C] 纯文本查看 复制代码
[b]输入法标号的获取[/b]
首先调用GetKeyboardLayoutList来获取输入法的数量
然后调用LoadKeyboardLayoutA获取每个输入法的句柄
通过判断输入法的句柄是否相等来获得输入法标号

接下来删除对应的注册表就可以了
[Asm] 纯文本查看 复制代码
“Keyboard Layout\Preload\”
“SYSTEM\CurrentControlSet\Control\Keyboard Layouts\”
“S-1-5-21-1060284298-606747145-682003330-500\Keyboard Layout\Preload”

然后调用UnloadKeyboardLayout卸载输入法
并且删除imedllhost09.ime以及对应的缓存文件

[Asm] 纯文本查看 复制代码
缓存文件的路径以及名称在注册表的“SYSTEM\CurrentControlSet\Control\Keyboard Layouts\”下
输入法的表示路径在注册表的 “Keyboard Layout\Preload\”下



参考资料
参考源码  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/





免费评分

参与人数 9威望 +1 吾爱币 +27 热心值 +7 收起 理由
WX4885 + 1 + 1 我很赞同!
苏紫方璇 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wuliwuli + 1 热心回复!
lidongyang611 + 1 我很赞同!
sunshy` + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
aflyhawk + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
邱淑贞 + 1 + 1 热心回复!
笙若 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
hgfty1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
IBinary 发表于 2020-8-21 16:21
顶一下.2楼不要说什么检测啥的. 毕竟你学习的是注入.是技术.不是让你来 做外挂的. 任何技术一旦分享出来都会面临着对抗技术的升级. 输入法当时很多主流的游戏都检测不到.或者没办法检测. 但是公开了也就这样了. 学一下技术最好.了解下原理.
3#
 楼主| 淡淡哇 发表于 2020-8-20 23:46 |楼主
4#
xuxunyue 发表于 2020-8-21 16:11
5#
endriver 发表于 2020-8-21 16:35
关键是要了解各种系统函数的用法
6#
无秽之鸦 发表于 2020-8-21 18:48
这注入不知道咋样
7#
netspirit 发表于 2020-8-21 20:59
IBinary 发表于 2020-8-21 16:21
顶一下.2楼不要说什么检测啥的. 毕竟你学习的是注入.是技术.不是让你来 做外挂的. 任何技术一旦分享出来都 ...

现在就别说注入了 就是真的输入法游戏也用不了啊
8#
REK_滑稽 发表于 2020-8-21 21:03
支持一下
9#
iwannaufly 发表于 2020-8-22 04:07
厉害,我一直直接用的类似精益模块的文本_投递
10#
花好s月圆 发表于 2020-8-22 06:38
xuxunyue 发表于 2020-8-21 16:11
输入法注入容易被检测到

直接注入到游戏,更容易被检测。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-11 20:04

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表