网易云音乐正在播放的歌曲同步到QQ状态:Sync2

效果如图所示。大概是一年前的老物。QQ接口变掉之后曾失效,有网友来问到这个问题,于是有了之前那篇文的探讨。

当然,这个同步的效果绝非自娱自乐,这个状态是好友都是可以看得到的。

修正了新版QQ的问题之后,顺便开源:

oschina地址   github地址

下载地址:github   oschina

————

具体的实现,可以看源码,以及之前那篇文的探讨。这里接着之前那篇写下后面的处理。

上篇讲到,我们通过Patch,把CPHelper.dll中PutRSInfo函数改成了可以正常接受UTF8字符串的。然后该怎么做呢?通常情况下,把改过后的DLL保存放回QQ路径替换原来的文件就可以了。但是这样有很明显的问题,那就是当QQ更新以后,自然又会把文件覆盖掉导致失效。若是在QQ更新后再用同样的DLL覆盖回来,又有潜在的版本不一致性的问题(比如新版CPHelper.dll加了个什么函数进去,而你旧版是没有的,这样QQ调用的时候就出问题了)。

在github的讨论中有大触提出使用Hook技术监控CTXStrings的GBK版构造函数,在PutRSInfo调用该函数的时候重定向到UTF8版,以动态地、不依赖DLL地解决该问题。(我不知道我理解的对不对)但是这样还是有难度,首先我不会这么高级的hook(哭)。另外,CPHelper.dll中的PutRSInfo是一个内部函数,是不导出的,其实没有名字(有些图里的名字是我写上去的)。对于一个内部函数来说,无法从外部调用,当DLL版本更新之后,它的地址怎么变也很难保证。对于人当然可以通过汇编代码的意义来确认就是这个函数,而对于程序怎么才能确认就是这个函数呢?想了想还是太复杂了于是放弃。

我使用的是我想到的另一个方案:把我们改好的DLL做成一个独立的、不干扰QQ的CPHelper.dll的DLL,我们的程序只调用它就能完成PutRSInfo操作。为此继续修改CPHelper.dll,上次我们改的是导入表,这次再来改导出表。刚才说到,PutRSInfo是一个内部函数,无法从外部调用。我们通常调用这个DLL,是因为它是一个COM库,我们通过COM调用间接地使用了该函数。但是COM库是要在系统注册的,最终调用COM库时,它一定会定位到QQ文件夹里那个CPHelper.dll。为此,必须导出PutRSInfo函数,使它成为一个导出函数,能被外部的程序直接调用。

从上次改好的DLL继续下手,同样可以使用LordPE或是CFF Explorer。首先看一下PutRSInfo的地址。515A7DC1这是VA,换算成RVA(相对VA)是7DC1,这个是我们需要的。另外在文件中的偏移量(File Offset)是71C1。(下图工具为LordPE中的文件位置计算器)

用工具找到输出表,随便找一个函数开刀(因为我们制作这个DLL的唯一目的就是导出这个函数,其它的导出函数都是用不到的所以没关系了。不过建议就改第一个好了),把最关键的RVA改成PutRSInfo的RVA。函数名嘛,可以改也可以不改,经我试验无论怎么改也是没办法通过这个名字在.NET中进行P/Invoke调用的(但是我们可以通过这个序号即1来调用)。另外最好把“名称字串”也改掉防止与真正的DLL在同一目录下时可能出现的冲突。

改好后保存。然后怎么使用呢?示例如下:

[DllImport("CPHelper.dll", EntryPoint = "#1", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int PutRSInfo(uint dwUin, uint dwRsId, string strRSPrompt, string strParam);

 

注意这个“#1”,这就是通过导出函数的序号来调用导出函数的方式。通常来说EntryPoint应该填写函数名字,但是实测这里填函数名是找不到的,急的我都快放弃了。

测试从程序中调用。结果应该没有问题……才怪。

为什么?因为它需要依赖项,比如CTXStringA这个类是从Common.dll中定义的,所以Common.dll肯定是需要的。

那我们把Common.dll复制到同一目录下,再测试,你就会发现……

Common.dll的依赖项更多了!

如果你要让这个函数能够调用,需要把Common.dll的依赖项一个个找出来复制到程序同一目录下,估计至少有十几个dll。

就没有别的办法吗?当然有的。我们可以设置程序搜索DLL的路径,让程序从QQ的目录下搜索DLL,这样就能集齐依赖项了。

实现这个的方式好像不止一种,我选用了最简单、但可能会造成麻烦的一种,即修改当前工作目录:Environment.CurrentDirectory。由于Sync2中并不会用到这个工作路径,所以大丈夫。

这下运行程序就可以成功推送状态了。

评论 (6) -

  • 我是不是可以推送一些诡异的东西在这上面...
    • 是的,理论上推送任何文字都是可以的,图标也可以在TX的任何支持的产品中选择。

      但是我比较反对这样做……

      之前我还写过direct!osu(源代码见gitosc,已不再维护),可以用来把osu!正在玩的歌曲的名字同步过去。
  • asd
    真的dalao...
    顺便表示本来看到是准备改去osu!用的,然而发现了direct!osu
    • direct!osu早已不再更新了,感兴趣的话,你可以仿照着搓自己的工具。
      要让Sync2支持osu!实现起来也是非常简单的。不过这个受众就太小了,一直很忙的我暂时没有这个计划,你也可以自行去实现哦。如果真的做了,还请分享一个github或是oschina地址。
      • asd
        然而我连Sync2都没用成功...
        不知道是不是因为QQ的版本特殊,没观察到状态。
        思考ing,顺便吐槽下这个验证码
        • 可能性最大的是权限问题,试着用管理员权限运行一下。
          其次可能是你没开QQ或者输错了QQ号……
          又或者是被某些机智的杀毒软件挡住了。
          至于验证码,其实人工审核评论根本不需要验证码,这个非常naive的验证码是系统自带功能,懒得去掉了。

添加评论

Loading