通过Patch DLL修正QQ的状态推送

升级了Win10 TH2,感觉良好,赶紧备份一波。开始菜单的各种BUG得到了很好的修复,不过DPI貌似又变得与之前不一样了。

 

趁备份和清理文件的时候写写这个问题。

在QQ7.5版及之前,我们通过调用QQCPHelper这个COM库的PutRSInfo函数,可以向QQ推送状态,比如QZone的浏览器插件使用这个东西来推送你正在QZone中播放的歌曲。不过目前的QZone已经不使用这个了,因为Chrome等浏览器已经不支持NPAPI这种插件了。现在的QZone和QQ音乐都是直接向服务器发消息,服务器再向QQ客户端通知改状态。

这个东西当然可以用在其他地方,比如osuPlayer。比如我的小工具Sync2(把网易云音乐和千千静听正在播放的歌曲同步到QQ状态,当然是他人可见的)。

不知道是否是因为这个API已经不再使用了,在QQ7.6之后的版本,这个API函数的实现貌似出现了微妙的变化。(我还是觉得比起BUG,这更像是TX故意为之)

以前的PutRSInfo是可以正常接收Unicode字符串的。事实上我们调用COM库,传入的是BSTR(.NET有一层自动包装,传入.NET类型String即可),理论上都是Unicode编码。一直以来都没问题。但是更新之后,它反而会认为你传入的编码是ANSI(GBK),并尝试转为UTF8。这就导致你推送的消息中的中文全部乱码,当然英文是没事的。

关于这个问题的讨论

一个首先会想到的方案是将UTF8字符串强行编码成GBK字符串再传过去(于是实际上传的是内容是UTF8、却由GBK编码的乱码字符串)。但这样会由于UTF8用3个字节表示汉字、GBK用2个字节,导致奇数个汉字的最后一个字不能正确显示。关于这个问题的探讨

于是我们还是要寻找其他办法。虽然目前还没有搞出完美的解决方案,不过还是将这个可行方案的研究过程记录一下。

打开QQ目录下的CPHelper.dll,找到PutRSInfo这个函数的具体实现(这是一个内部函数,并非导出函数,下图中的名字是我自己加的)。

我们可以看到它构造字符串的时候调用的函数是CTXStringA::CTXStringA(tagGBK, wchar_t const *, int)。tagGBK已经明显透露出它是把输入的字符串看做GBK了。

再去看CTXStringA的实现库Common.dll,可以看到它的构造函数有三种,分别是针对纯英文(ASCII?)、GBK和UTF8的。于是想到把这个函数的调用换成UTF8版的构造函数是否能解决问题。

但是,查看CPHelper.dll的导入表(Import Table),发现它根本没导入UTF8版本的函数,只导入了一个GBK版。那我们把导入表里GBK版的函数名改成UTF8版就好(可以使用LordPE或者CFF Explorer)。如果真的能这样改的话,那还算是相当方便了。但是!UTF8版的导出函数名比GBK版多了1个字!!!而这个导入表改名的操作显然是不能变长的!(要是这帮人起名的时候,一个叫tagGBK,一个叫tagUTF,那估计就没这么多问题了)没办法,我们必须要在CPHelper.dll的导入表中添加这个UTF8版函数。

这个过程同样可以使用LordPE或是CFF Explorer。添加完后,查看函数的地址,然后再找到PutRSInfo函数的call调用指令处,将原本GBK版的地址换成UTF8版的地址(16进制直接改就好,当然你要写汇编指令应该也可以)。保存,收工。

测试一下,确实是可以了。

但是一旦QQ版本更新,这个就失效了,还得重新Patch。所以可能还是需要一个更稳定的方案才行。详情请看下篇。

附录

部分RSID:(从上古时期流传而来,可用性未知)

逆战 65588
御龙在天 65584
宝石总动员 65583
Q宠大乱斗 65582
摩登都市 65579
QQ仙侠 65578
QQ宝贝 65576
生化战场 65575
洛克王国 65574
3366小游戏 65572
烽火战国 65571
QQ西游 65570
七雄争霸 65568
QQ幻想世界 65567
大明龙权 65566
丝路英雄 65563
英雄岛 65562
战地之王 65561
QQ三国 65549
QQ音乐 65542
QQ幻想 65541

添加评论

Loading