一个依赖库更新引发的血案:QQ 号被冻结技术分析

应用软件 · 2020-07-16 · 1971 人浏览

昨天(2020年7月15日)中午十一点左右,大量网友反应自己的 QQ 号被冻结,这个问题一度冲上知乎和微博热搜第一,很多人表示自己的生活受到了影响。具体的事态发展各位吃瓜群众可以自行了解,这里打算从技术角度分析一下这次故障的原因。

先给结论:这应该是一次第三方依赖库升级引发的。

这个第三方依赖库是恰好是我本人在 github 上开源的,一个名为 `epic` 的项目(地址在这里:https://github.com/tiann/epic);`epic` 是一个在虚拟机层面、以 Java Method 为粒度的 运行时 AOP Hook 框架。它可以拦截本进程内部几乎任意的 Java 方法调用,可用于实现 AOP 编程、运行时插桩、性能分析、安全审计等。

我开发这个库的初衷是做性能分析,更具体点,是为了监控 App 内部线程的调度情况。当时我正在支付宝做 Android 端 App 的性能优化,因为研发团队很大,内部又没有一个完全统一的基础设施,代码里面很多四处散落的线程(池)、广播,Service等;可能一个线程或者广播并不消耗什么性能,但是量变产生质变,支付宝启动之后峰值会有三百多个子线程在运行,同时有一百多个广播在主线程跑,即使每个只运行几毫秒加起来也有若干秒了。为了知道是哪里创建了这些资源,我非常需要一个运行时插桩工具来监控 App 的运行情况;于是我就花了点时间撸了一个库并且开源在 github 上。

说到这里也许有童鞋会问:这 `epic`听起来不是挺好吗,为啥会导致冻结?

技术是一把双刃剑。由于 `epic` 可以拦截本进程内几乎任意的方法调用,一旦以合适的方式加以利用,就可以创建上帝视角,控制任意 App 的运行。这个功能与 Android 上大名鼎鼎的 Xposed 框架本质上是一样的。

Xposed 框架可以在不修改应用安装包的情况下修改程序的运行(修改系统),基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作。

比如说抢红包这功能,大部分都是借助 Xposed 框架通过模块实现的;这些功能看起来对普通人创造了便利,但是实际上滋生了很多阴暗的角落;就拿抢红包来说,有一些团体通过发红包来赌博,而如果有人通过 Xposed 框架干涉这个过程就相当于“出千”。若干年过去,大量灰黑产团体通过 Xposed 框架来干扰 App 的正常运行。因此 App 官方是非常讨厌 Xposed 框架存在的,它们通常会在用户手机上检测到 Xposed 框架存在之后,对用户进行账号冻结操作。

不过通常来说,使用 Xposed 框架的用户相对比较少,因此由于使用 Xposed 被封号的用户也是极少数。而这一次 QQ 更新之后,由于引入了错误的代码依赖,导致它在任何Android 手机上都认为检测到了 Xposed 框架,从而触发账户冻结。

QQ 在很早之前就引入了 `epic`,当时应该是为了做线上性能检测。那个时候的 `epic` 只是为了做性能监控,因此 API 与 Xposed 并不兼容,里面的类基本上与Xposed 没有太大关联;QQ 集成这个库之后,其实就是引入了一个非常普通的依赖;我们看一下上个版本 QQ 的代码:

一个依赖库更新引发的血案:QQ 号被冻结技术分析

可以看到,QQ 对 `epic`做了少许修改,部分类挪到了 `com.qq.android.dexposed`下面;因此这个实际上没有什么影响。然后我们看一下新版QQ的变化:

一个依赖库更新引发的血案:QQ 号被冻结技术分析

可以看到,这个类被挪到了 `de.robv.android.xposed` 这个包下面;而这个包,就是 Xposed 框架所在地方。一般来说,App 进程如果发现有这个包存在,那么就可以认为是手机上装有 Xposed 框架,从而进行一系列接下来的动作。而 QQ 的这个行为,直接就是把 Xposed 所有的类放在了自己名下;因此它如果检测 `Xposed` 相关类,会 100% 判定为手机上装有 Xposed 框架:

一个依赖库更新引发的血案:QQ 号被冻结技术分析

由于 `epic` 可以实现 Xposed 的功能,因此再后来我通过它实现了一些类 Xposed 框架,为了兼容原来的框架,我对 `epic`项目做出了一些调整:让它直接依赖了 Xposed 框架的相关类(具体commit见这里:https://github.com/tiann/epic/commit/dd5a10ddf2daa51a6724a943b73e87ae141fec82#diff-0d1d9100a9b6813b8cb5c470bf313d00)。在这一次调整之后,只要引入了 `epic`,就会默认引入 Xposed 相关的类。因此,QQ 在更新`epic`项目的版本之后,把 Xposed 相关的类也引入了 QQ。

经过对比分析两个版本 QQ 中 `epic` 的变化,可以进一步发现,是因为QQ更新了 `epic`的版本,导致引入了 `Xposed`相关的依赖,从而把所有 Xposed 相关的类集成到了QQ之中:

一个依赖库更新引发的血案:QQ 号被冻结技术分析

如上图,新版本的`epic`默认引入了两个额外依赖:`free_reflection` 和`Xposed`,进一步我们看到,新版本的QQ的确引入了我的另外一个库`free_reflection`:

一个依赖库更新引发的血案:QQ 号被冻结技术分析

至此,我们的技术分析就结束了。本次 QQ 被冻结的悲剧,实际上是一次工程失误,开发团队在对第三方依赖进行升级的时候,引入了错误的、不合适的依赖库,从而导致了悲剧的发生。

这也告诫我们,在升级第三方依赖的时候一定要慎之又慎,千万不要盲目追新;很多童鞋有升级强迫症,看到有更新就想试,实际上这是非常危险的。在现实世界中,项目的稳定性一定是第一位,在升级依赖之前,一定要做好充分的评估。这次 QQ 升级引发的事故,必须引以为鉴。

文章转自:https://mp.weixin.qq.com/s/ZaPQx8aWxUWqDMsjAEDgGA

qq 腾讯
Theme Jasmine by Kent Liao