也不知道他们是在搞什么,继上一次 Windows 10 企业版 21H2输入法卡顿后,这回又出现卡顿了,输入一个字母就能卡死。

继续按照上次的思路,卸载最近的补丁,然后卸载KB5022282后,没有恢复。看了这个补丁的更新日志,好像和也输入法没啥关系。

在网上搜索的时候,发现了一位仁兄写的文章,看了下,发现确实在%AppData%\Microsoft\InputMethod\Chs 产生了很多UDP开头的临时文件,而且来源可能是GetTempFileName,删除之后,输入法就恢复正常了。但是每次切换输入法都会产生一个临时文件,用一个批处理每次开机就删除下文件感觉还是不爽,特别是我基本能不关机就不关机,都是休眠。

有两种方式去解决这个问题:

一、退回旧输入法

这种办法最简单,在任务栏的输入法图标处,点击“语言首选项”,然后依次点击“中文(简体,中国)”-》“选项”-》“微软拼音”-》“选项”-》“常规”,然后往下拉,有个兼容性,把“使用以前版本的微软拼音输入法”开启即可。与新版差异不是很大,基本上也能接受。

二、修改输入法文件

这种办法有点复杂,需要自己有一定的修改文件能力。知道了临时文件的目录以及产生的来源是GetTempFileName就好办了,输入法的关键也就那么几个进程,看看谁在生成这个临时文件即可,最后发现是ctfmon.exe,代码所在文件为ChsAdvancedDS.dll。

对于拼音输入法而言,临时文件产生的IDA伪代码如下:

QWORD *__fastcall CHX_MTF_DS::CHSPinyinUdpEngine::GetTempUdpFilePath(_QWORD *a1, wchar_t *Source)
{
HRESULT v4; // eax
const char *v5; // r9
__int64 v6; // rcx
int v8; // [rsp+20h] [rbp-448h]
__int16 TempFileName[264]; // [rsp+30h] [rbp-438h] BYREF
wchar_t Destination[264]; // [rsp+240h] [rbp-228h] BYREF
wil::details::in1diag3 *retaddr; // [rsp+468h] [rbp+0h]

v8 = (int)a1;
memset_0(Destination, 0, 0x208ui64);
if ( *((_QWORD *)Source + 3) >= 8ui64 )
Source = *(wchar_t **)Source;
wcscpy_s(Destination, 0x104ui64, Source);
v4 = PathCchRemoveFileSpec(Destination, 0x104ui64);
if ( v4 < 0 )
{
wil::details::in1diag3::_Throw_Hr(
retaddr,
(void *)0xFB,
(unsigned int)”mincore\\TextInput\\Dev\\mtf\\DataSources\\CHX\\AdvancedDS\\lib\\CHSPinyinEngine.h”,
(const char *)(unsigned int)v4,
v8);
JUMPOUT(0x15028F736i64);
}
memset_0(TempFileName, 0, 0x208ui64);
if ( !GetTempFileNameW(Destination, L”UDP”, 0, (LPWSTR)TempFileName) )// 生成UDP开头的临时文件
{
wil::details::in1diag3::_Throw_GetLastError(
retaddr,
(void *)0xFE,
(unsigned int)”mincore\\TextInput\\Dev\\mtf\\DataSources\\CHX\\AdvancedDS\\lib\\CHSPinyinEngine.h”,
v5);
__debugbreak();
}
a1[3] = 7i64;
a1[2] = 0i64;
*(_WORD *)a1 = 0;
if ( TempFileName[0] )
{
v6 = -1i64;
do
++v6;
while ( TempFileName[v6] );
}
std::wstring::assign(a1, TempFileName);
return a1;
}

找到调用上诉代码的地方,在不远处发现了DeleteFileW的代码,开启调试,发现在生成UDP***临时文件后,对临时文件进行了一些操作,类似是写入数据,但是不知道为何,没有写入,导致临时文件是0大小,最终导致CopyFileW没有执行成功,并抛出了一个异常,后面代码就没有继续执行DeleteFileW。可以看下下面的伪代码:

_BOOL8 __fastcall CHX_MTF_DS::CHSWubiUdpEngine::ReloadUdpFiles(CHX_MTF_DS::CHSWubiUdpEngine *this)
{
int IsDesktop; // eax
const WCHAR *v3; // rsi
CHX_MTF_DS::CHSWubiUdpEngine *TempUdpFilePath; // r14
void **v5; // rbx
const WCHAR *v6; // rdx
BOOL v7; // eax
const char *v8; // r9
wil::details::in1diag3 *v9; // rcx
int v11; // [rsp+20h] [rbp-58h] BYREF
__int64 v12; // [rsp+28h] [rbp-50h]
int v13[2]; // [rsp+30h] [rbp-48h] BYREF
unsigned __int64 v14; // [rsp+48h] [rbp-30h]
wil::details::in1diag3 *retaddr; // [rsp+78h] [rbp+0h]

v12 = -2i64;
*((_BYTE *)this + 52) = 1;
LOBYTE(v11) = 0;
IsDesktop = ChsIME::ConfigHelper::IsDesktop((bool *)&v11);
if ( IsDesktop < 0 )
{
wil::details::in1diag3::_Throw_Hr(
retaddr,
(void *)0x3A,
(unsigned int)”mincore\\TextInput\\Dev\\mtf\\DataSources\\CHX\\AdvancedDS\\lib\\CHSWubiEngine.h”,
(const char *)(unsigned int)IsDesktop,
v11);
goto LABEL_24;
}
if ( !(_BYTE)v11 )
return 0i64;
v3 = (const WCHAR *)((char *)this + 56);
TempUdpFilePath = (CHX_MTF_DS::CHSWubiUdpEngine *)CHX_MTF_DS::CHSWubiUdpEngine::GetTempUdpFilePath(
v13,
(wchar_t *)this + 28);
v5 = (void **)((char *)this + 88);
if ( (CHX_MTF_DS::CHSWubiUdpEngine *)((char *)this + 88) != TempUdpFilePath )
{
if ( *((_QWORD *)this + 14) >= 8ui64 )
operator delete(*v5);
*((_QWORD *)this + 14) = 7i64;
*((_QWORD *)this + 13) = 0i64;
*(_WORD *)v5 = 0;
std::wstring::_Assign_rv((char *)this + 88, TempUdpFilePath);
}
if ( v14 >= 8 )
operator delete(*(void **)v13);
if ( *((_QWORD *)this + 14) < 8ui64 )
v6 = (const WCHAR *)((char *)this + 88);
else
v6 = (const WCHAR *)*v5;
if ( *((_QWORD *)this + 10) >= 8ui64 )
v3 = *(const WCHAR **)v3;
v7 = CopyFileW(v3, v6, 0);       //这里拷贝失败了
v9 = retaddr;
if ( !v7 )                                         // v7 返回了0,可以把这部分代码pass掉,直接跳过
{
LABEL_24:
wil::details::in1diag3::_Throw_GetLastError(
v9,
(void *)(unsigned int)(v7 + 64),
(unsigned int)”mincore\\TextInput\\Dev\\mtf\\DataSources\\CHX\\AdvancedDS\\lib\\CHSWubiEngine.h”,
v8);
JUMPOUT(0x15028FB52i64); // 这里跳出了,导致后面的deletefile 无法执行,也可以在这里把这部分代码nop掉
}
if ( !(unsigned __int8)CHX_MTF_DS::UDPEngine::LoadUDPLexicon(this, (char *)this + 88, 3i64) )
{
if ( *((_QWORD *)this + 14) >= 8ui64 )
v5 = (void **)*v5;
DeleteFileW((LPCWSTR)v5);
}
return *((_QWORD *)this + 1) == *((_QWORD *)this + 2);
}

将上面的“JUMPOUT(0x15028FB52i64); ”代码nop掉,然后替换掉 C:\Windows\System32\InputMethod\CHS 中的ChsAdvancedDS.dll即可,修改时候按下面步骤执行即可:修改ChsAdvancedDS.dll文件的权限,重命名该文件(因为此刻该文件还在被使用中),放入修改后的ChsAdvancedDS.dll文件,重启ctfmon.exe进程即可。

我修改的时候ChsAdvancedDS.dll文件版本是10.0.19041.746,文件日期是2021年1月6日,应该是不经常更新的,等更新了再继续修改吧。

另外五笔输入法也是有这个问题的,代码在“_QWORD *__fastcall CHX_MTF_DS::CHSWubiUdpEngine::GetTempUdpFilePath(_QWORD *a1, wchar_t *Source)”。

2 个评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注