这个已经做完好久了,现在贴出来作为历史备份吧。

智能狂拼 3 是一个很不错的输入法,虽然是一种整句输入法,单词输入能力一样很棒,响应速度比 MSPY2003 更是好了无数倍。官方出了一个 30M 的版本可以免费使用,实在非常厚道。

但是他有一个很恶心的 bug, 就是在某些程序里面经常崩溃,fterm 之类挂接了默认 SEH 的还好,MSN 之类就是用着用着就没掉了,实在不能忍,等了好久也不见补丁出来,只好自己动手搞之

用 ollydbg 启动 fterm , 在 fterm 里面用狂拼输入几下,就崩溃了,ollydbg 报告写入到一个地址不行

07CEB8AF C603 00 MOV BYTE PTR DS:[EBX],0

在 07CEB8AF 地方说不能写入到 EBX 指向的地方, ollydbg 里面显示这个 EBX 指向的位置居然是Unicode的 Windows\WinSxs 字串。这个是怎么弄出来的呢?先看看怎么才能跑到这里吧。

用 IDA 反编译 CSMPYEng.DLL (崩溃时候ollydbg停在这个 dll 里面),找到对应代码, IDA 标明代码引用的功能实在很棒,一下就看出来代码可能的流程了,如下,最后一行就是崩溃代码:

.text:1000B881 loc_1000B881: ; CODE XREF: sub_1000B7E0+7Dj
.text:1000B881 lea eax, [esp+2Ch+var_10+1]
.text:1000B885 lea ebx, [esp+2Ch+var_10+1]
.text:1000B889 push eax
.text:1000B88A call sub_100177B7
.text:1000B88F add esp, 4
.text:1000B892 test eax, eax
.text:1000B894 jnz short loc_1000B8A4
.text:1000B896
.text:1000B896 loc_1000B896: ; CODE XREF: sub_1000B7E0+C2j
.text:1000B896 inc ebx
.text:1000B897 push ebx
.text:1000B898 call sub_100177B7
.text:1000B89D add esp, 4
.text:1000B8A0 test eax, eax
.text:1000B8A2 jz short loc_1000B896
.text:1000B8A4
.text:1000B8A4 loc_1000B8A4: ; CODE XREF: sub_1000B7E0+B4j
.text:1000B8A4 mov cl, [ebx]
.text:1000B8A6 lea edx, [esp+2Ch+var_10]
.text:1000B8AA push edx
.text:1000B8AB mov byte ptr [esp+30h+arg_8], cl
.text:1000B8AF mov byte ptr [ebx], 0

这段代码并不难读,很清楚的看到这是一个循环,ebx 是某 char 指针 p,循环里面调用 call sub_100177B7 ,如果返回值是 0 , 则 p++ , 否则出来对 *p 进行一些操作。那么循环进入时候 p 是什么值呢?用 ollydbg 在 100b896 断点,随便按一个键让代码执行到断点,发现指向的就是我刚输入的字母。

现在问题就比较清楚了,多半是这个 sub_100177b7 在某些情况下面处理不正确,返回了0, 让 p++ 没完没了的跑下去,直到什么时候再次返回 1 , 这时候 p 都不知道跑到什么地方去了,这不崩就怪了。

之后的跟踪工作就不提了,总的来说很简单,发现问题在单独输入 i,u,v 的时候, 对于i/u/v字母之后的 null byte,  该函数并没有判断,也返回 0, 导致指针越界很远。修正工作也比较简单,将 sub_100177b7 的入口 patch 跳转到别处,判断一下输入,如果是 null byte, 直接返回 1, 否则转原来入口。这个 dll 的函数是按照 16 字节对齐的,因此里面有很多空间可以塞二进制代码。。

这样智能狂拼3就可以正常使用了。

补记:我在水木上面发表这个补丁之后两天,官方就推出了修正补丁,真是 !@#$%^ 。不过也好,这里就不放修改之后的补丁文件了,需要的可以直接到官方网站上面去下载。

适中版本 http://www.tiansuo.com.cn/upload/kuangpin/CSMPYEng.zip

专业版本 http://www.tiansuo.com.cn/upload/kuangpin/CSPPYEng.zip