钉钉防消息撤回插件的开发

起因

学习一门技术,最好的方法是实际做一个项目。而做任何一个项目,最好的动力就是实际的需求。

我的一个需求就是把在钉钉(一个阿里的通讯软件,相当于企业版的微信)上对方撤回的消息显示出来。目的也就是想看看对方发了什么让自己觉得囧的消息想撤回,哈哈哈。

经过

工具

工欲善其事,必先利其器

xposed开发中80%的时间是用来对apk包反编译后代码的解读和思考(如果你在修改的开源代码,请跳过这一段。。。),因此想要开发xposed插件,必须学会apk反编译工具的使用。

列举一下我使用到的工具:

  • apktool
    • 用来将apk包解析后得到资源文件和smali文件(类似于汇编代码)
    • 使用java -jar apktool.jar d YOUR_APK_FILE.apk -o TARGET_FOLDER命令
    • 建议学习一下smali的语法
  • jad-x,用来将dex文件(类似于class文件)转为可读的java文件
    • 将apk文件后缀改为zip后解压,用jadx-gui打开每个dex文件,并save all
    • 将保存下来的目录进行合并,可以使用*unix的cp -r命令处理
    • 打开一个IDE(如果你喜欢用vim也行),开始找你要的代码吧

建议最开始主要使用jad-x生成的java代码进行程序的理解,当涉及到一些资源的使用,或者jad-x解析不了的代码时,使用apktool解析出来内容进行挖掘。

另外,还可能涉及到抓包这个步骤,不过我目前还没有用到。

思考

Xposed开发其实本身并不会写很多代码,而是在于找到切入点,对源程序进行行为的改变。

比如要防止消息撤回,最先想到的就是让消息的状态永远是“非撤回”状态,然后考虑到数据库的变化,想到的是拦截数据库的修改消息状态(撤回)的动作。

寻找

寻找不仅仅意味着寻找代码,还可以是利用搜索引擎进行查找~

我找到了veryyoung写的一个module,可以用但还是存在着一些问题。这也为我提供了不少的思路。

可以看到recallStatus是一个关键字,我用它来检索了全部代码,找到了MessageImpl这个类。可以看到veryyoung是对MessageImpl这个类的recallStatus这个方法进行了hook,让它永远返回01表示已撤回)来达到阻止撤回的目的。

但依然存在两个问题:

  1. 应用重启后,被撤回的消息无法复原,即还是被撤回了
  2. 过去被成功撤回的消息,在对话框中显示为正常对话,而内容是”Msg has been recalled.”

"Msg has been recalled."

尝试

首先我想到的是这个”Msg has been recalled.”是不是一个资源string,找一下看哪里显示了这个字符串,结果没找到。。。

后来我意识到了:

  1. 这个字符串不是显示的时候加载的,而是被写入了数据库
  2. 显示”Msg has benn recalled”的时候并没有和那些正常“已撤回”的消息使用同样的格式,而是和普通的消息使用一样的格式,也是因为这条数据就只是一条普通的消息
  3. 数据的来源可能是远端的请求,所以本地资源里找不到这个字符串

于是我的方向转为了寻找数据库的写入或更新,只要这条“新“的消息不覆盖原始数据,那消息就能够被正常显示了,即使本地应用重启也没问题。

同时结合关键词recallStatus,我找到了MessageDB这个类(代码混淆后类名变成了cuw),在对几个可能更新消息内容的函数进行hook打印日志后,最终确定了某个函数。

这个函数在对方点击撤回时会在我这里被调用,且写入的消息内容正是”Msg has been recalled”。

Binggo!

总结

这个插件的源码地址是https://github.com/AaronGeist/DingDingHacker,欢迎提交issue。

我这篇文章也只是记录了一下开发的过程和思路,推荐大家读一下veryyoung的这篇文章,能够对安卓的逆向工程基础有所了解
http://veryyoung.me/blog/2016/09/29/android-reverse-basic.html