标签:
最近开源界涌现了很多热补丁项目,但从方案上来说,主要包括Dexposed、AndFix、ClassLoader(来源是原QZone,现淘宝的工程师陈钟,在15年年初就已经开始实现)三种。前两个都是阿里巴巴内部的不同团队做的(淘宝和支付宝),后者则来自腾讯的QQ空间团队。
开源界往往一个方案会有好几种实现(比如ClassLoader方案已经有不下三种实现了),但这三种方案的原理却徊然不同,那么让我们来看看它们三者的原理和各自的优缺点吧。
基于Xposed的AOP框架,方法级粒度,可以进行AOP编程、插桩、热补丁、SDK hook等功能。
Xposed需要Root权限,是因为它要修改其他应用、系统的行为,而对单个应用来说,其实不需要root。 Xposed通过修改Android Dalvik运行时的Zygote进程,并使用Xposed Bridge来hook方法并注入自己的代码,实现非侵入式的runtime修改。比如蜻蜓fm和喜马拉雅做的事情,其实就很适合这种场景,别人反编译市场下载的代码是看不到patch的行为的。小米(onVmCreated里面还未小米做了资源的处理)也重用了dexposed,去做了很多自定义主题的功能,还有沉浸式状态栏等。
我们知道,应用启动的时候,都会fork zygote进程,装载class和invoke各种初始化方法,Xposed就是在这个过程中,替换了app_process,hook了各种入口级方法(比如handleBindApplication、ServerThread、ActivityThread、ApplicationPackageManager的getResourcesForApplication等),加载XposedBridge.jar提供动态hook基础。
具体到方法,可参见XposedBridge:
1
|
/**
|
其具体native实现则在Xposed的libxposed_common.cpp里面有注册,根据系统版本分发到libxposed_dalvik和libxposed_art里面,以dalvik为例大致来说就是记录下原来的方法信息,并把方法指针指向我们的hookedMethodCallback,从而实现拦截的目的。
方法级的替换是指,可以在方法前、方法后插入代码,或者直接替换方法。只能针对java方法做拦截,不支持C的方法。
来说说硬伤吧,不支持art,不支持art,不支持art。
重要的事情要说三遍。尽管在6月,项目网站的roadmap就写了7、8月会支持art,但事实是现在还无法解决art的兼容。
另外,如果线上release版本进行了混淆,那写patch也是一件很痛苦的事情,反射+内部类,可能还有包名和内部类的名字冲突,总而言之就是写得很痛苦。
同样是方法的hook,AndFix不像Dexposed从Method入手,而是以Field为切入点。
先看Java入口,AndFixManager.fix
:
1
|
/**
|
看来最终fix是在fixClass方法
:
1
|
private void fixClass(Class<?> clazz, ClassLoader classLoader) {
|
在dalvik和art上,系统的调用不同,但是原理类似,这里我们尝个鲜,以6.0为例art_method_replace_6_0
:
1
|
// 进行方法的替换
|
在dalvik上的实现略有不同,是通过jni bridge来指向补丁的方法。
使用上,直接写一个新的类,会由补丁工具会生成注解,描述其与要打补丁的类和方法的对应关系。
原腾讯空间Android工程师,也是我的启蒙老师的陈钟发明的热补丁方案,是他在看源码的时候偶然发现的切入点。
我们知道,multidex方案的实现,其实就是把多个dex放进app的classloader之中,从而使得所有dex的类都能被找到。而实际上findClass的过程中,如果出现了重复的类,参照下面的类加载的实现,是会使用第一个找到的类的。
1
|
public Class findClass(String name, List<Throwable> suppressed) {
|
该热补丁方案就是从这一点出发,只要把有问题的类修复后,放到一个单独的dex,通过反射插入到dexElements数组的最前面,不就可以让虚拟机加载到打完补丁的class了吗。
说到此处,似乎已经是一个完整的方案了,但在实践中,会发现运行加载类的时候报preverified错误,原来在DexPrepare.cpp
,将dex转化成odex的过程中,会在DexVerify.cpp
进行校验,验证如果直接引用到的类和clazz是否在同一个dex,如果是,则会打上CLASS_ISPREVERIFIED标志。通过在所有类(Application除外,当时还没加载自定义类的代码)的构造函数插入一个对在单独的dex的类的引用,就可以解决这个问题。空间使用了javaassist进行编译时字节码插入。
Dexposed不支持Art模式(5.0+),且写补丁有点困难,需要反射写混淆后的代码,粒度太细,要替换的方法多的话,工作量会比较大。
AndFix支持2.3-6.0,但是不清楚是否有一些机型的坑在里面,毕竟jni层不像java曾一样标准,从实现来说,方法类似Dexposed,都是通过jni来替换方法,但是实现上更简洁直接,应用patch不需要重启。但由于从实现上直接跳过了类初始化,设置为初始化完毕,所以像是静态函数、静态成员、构造函数都会出现问题,复杂点的类Class.forname很可能直接就会挂掉。
ClassLoader方案支持2.3-6.0,会对启动速度略微有影响,只能在下一次应用启动时生效,在空间中已经有了较长时间的线上应用,如果可以接受在下次启动才应用补丁,是很好的选择。
总的来说,在兼容性稳定性上,ClassLoader方案很可靠,如果需要应用不重启就能修复,而且方法足够简单,可以使用AndFix,而Dexposed由于还不能支持art,所以只能暂时放弃,希望开发者们可以改进使它能支持art模式,毕竟xposed的种种能力还是很吸引人的(比如hook别人app的方法拿到解密后的数据,嘿嘿),还有比如无痕埋点啊线上追踪问题之类的,随时可以下掉。
转载:http://blog.zhaiyifan.cn/2015/11/20/HotPatchCompare/
标签:
原文地址:http://www.cnblogs.com/hero4china/p/5425561.html