微信 macOS 客户端拦截撤回功能实践

微信的小贱人特多,尤其是群里的,老喜欢撤回,还有一大堆的跟风党起哄党...当然少不了一些手抖发错的,嘿嘿嘿。好奇心如此强的我怎能错过这些消息!

一、原理

要拦截撤回的消息,首先就要知道当客户端接收到撤回消息的通知时候,执行一个方法来撤回消息:

服务端 ---发送撤回通知---> 客户端接收 ---> 执行撤回消息的方法(删除本地消息,刷新UI,提示消息被撤回)

所以我们需要做修改的这个入口就是执行撤回消息的这个方法,让其不删除本地的消息就可以了。


二、准备

在OS X环境下逆向工具有以下几个大杀器:

  1. Hopper Disassembler - 反编译工具,能够将二进制执行文件反编译出伪代码
  2. class-dump - 导出可执行文件的Class Header
  3. Hexfiend - 二进制文件编辑器
  4. gdb - 调试神器,不用多介绍啦,通过brew install gdb安装
  5. otx - 同上,貌似比较小众,通过brew install --HEAD homebrew/head-only/otx安装

当然还有App Store下载最新版的微信OS X客户端一份。


三、Dump出头文件

首先肯定要先知道WeChat这个App有哪些方法,并猜测出撤回消息的这个方法在那个Class中,因此利用Class-Dump是最好不过的了,先把/Applications/WeChat.app/Contents/MacOS/WeChat复制一份到Class-Dump的相同目录下,然后直接执行:class-dump -H WeChat,目录下就会dump出了大量的.h文件。

不难想到撤回这个英文应该是Recall,用英文版的微信就知道啦,可以直接通过Finder的搜索条搜索Recall,找出包含Recall的头文件:

有趣的是,包含Recall的方法比较少,而且按照方法名好像也不太相关,但其中一个方法成功吸引了我的注意:- (void)onRevokeMsg:(id)arg1,这是在MessageService.h里面的一个方法,值得一试!


四、反编译

打开Hopper Disassembler,并在菜单栏中选择File->Read Executable to Disassemble,接下来就得等一段时间了,毕竟WeChat的这个二进制可执行文件有30多M。

等待Hopper Disassembler Load完之后,大概是这样的:

是不是觉得很可怕

但是,我们应该要特么地冷静下来,因为我们已经掌握了疑似撤回消息的方法名,在右侧上方的搜索栏中搜索onRevokeMsg,就能快速定位到方法的位置了,想要看懂这对奇怪的东西,就要生成伪代码,通过伪代码来看逻辑,点击右上角的这个按钮:

这就是传说中的伪代码,通过观察,找到了以下的这段代码:

结果非常明显了,标注的两个方法分别是删除被撤回的消息添加一条撤回成功的消息,所以证明了以上定位的方法onRevokeMsg正是撤回消息的方法,我们只要从这里入手就可以实现拦截撤回的功能了。


五、修改onRevokeMsg方法

这一步我就直接采用简单暴力有效的方法了,直接干掉onRevokeMsg方法,不执行内部逻辑操作,选中-[MessageService onRevokeMsg:]:下方的一行:

快捷键:Alt + A 并输入ret,点击Assemble and Go Next

其中ret的意思就是Return,当执行到这个方法的时候直接就Return了,屏蔽了下面的所有操作。


六、重新生成可执行文件

在菜单栏中选择File->Produce New Executable,提示是否清除签名信息选择Yes就可以了,然后保存到任意位置,然后覆盖/Applications/WeChat.app/Contents/MacOS/下的WeChat,至此所有的操作都已经完成了。

打开OS X微信客户端登陆,并在手机随便找个基友给他发条消息然后撤回,在OS X微信客户端可以发现毫无动静,消息没有撤回,可见这次实践非常成功(=゚ω゚)ノ


最后

当然除了修改二进制文件能够暴力实现拦截撤回的功能,也可以通过dylib对微信客户端进行hook,替换乘自己的方法,这样能够更加灵活并可以有更多的玩法,例如实现撤回消息发出通知等效果。再研究研究吧~

已经以更优雅的动态库注入方式实现拦截撤回功能,具体实现参考:https://github.com/Sunnyyoung/WeChatHook-macOS