《深入探索安卓热修复技术原理》1-32

Security Classification: 【C-1】 | Publish Time:2024-10-09 | Category:Reading Notes | Edit

Expiry Notice: The article was published three months ago. Please independently assess the validity of the technical methods and code mentioned within. :)

AI Summary: 本文介绍了热修复技术在安卓应用中的实现及其原理,主要包括以下内容: 1. **传统流程技术的弊端**:重新发布版本成本高、用户下载安装成本高、Bug修复影响用户体验。 2. **hybrid方案**:通过H5展示常用业务逻辑,但需要开发者掌握前端技术。 3. **热修复技术**:通过云端推送补丁,实现自动修复,减少更新成本。 4. **底层修复方案**:通过替换已加载类中的方法,但不能增加方法或字段,存在兼容性问题。 5. **类加载方案**:重启后使用ClassLoader加载新类,解决已加载类无法卸载的问题。 6. **资源修复**:利用新AssetManager替代旧资源,快速替换资源而无须完整包更新。 7. **so层修复**:通过调整native库加载路径,实现native方法的修复。 8. **Andfix底层替换方案**:直接替换方法入口,依赖于Android虚拟机的ArtMethod结构。 9. **兼容性问题**:不同手机厂商可能对ArtMethod结构进行了修改,导致热修复方案的适配性问题。 10. **即时生效限制**:无法添加或减少类的字段和方法,只能替换内容,且反射调用的非静态方法会引发问题。 总结而言,热修复技术旨在解决应用更新中的效率和体验问题,但在兼容性和结构变更方面仍面临挑战。 --- (From Model:gpt-4o-mini-2024-07-18)

技术介绍

传统流程技术弊端:

1、重新发布版本代价太大
2、用户下载安装成本太高
3、Bug不修复用户体验差

hybird方案,将常用业务逻辑以h5的方式展示出来,但需要开发者额外掌握前端语言等技术栈。对于无法转换成h5的业务逻辑也无法做到修改。

热修复技术应运而生,通过云端推送补丁,进行自动拉取与补丁修复。

底层修复方案

在已经加载的类中替换原有的方法,但是无法实现方法的增加与字段的增加,这样会导致无法索引到正确的类。并且实例化的对象会出现非预期的效果。

依赖修改虚拟机方法实现的具体字段,例如修改Dalvik方法的jni函数指针,改类与方法的访问权限等。安卓是开源的,各大手机厂商可以对源码进行改造,如果写死了ArtMethod方法的结构,但是适配到了魔改的手机,会出现非预期的后果。

类加载方案

在app重新启动后,让classloader加载新的类。安卓无法对已加载的类进行卸载,不重启,原先的类还在虚拟机中,也无法加载新的类。只有在重启后,在加载旧类之前抢先加载,这样后续访问这个旧类的时候才会resolve成新的类。

合并方案

根据情况选择修复方法,补丁生成时判断使用底层修复还是类加载方案修复。

资源修复

Install Run 实现原理

1、构造新的AssetManager,反射调用addAssertPath。获取一个包含新资源的AssetManager
2、通过反射把所有引入旧的AssetManager修改为新的AssetManager

代码处理逻辑基本集中于如何找到所有引用AssetManager的代码位置。

Sophix 实现

1、构造新的资源包,只包含新资源与修复的资源
2、在AssetManager对象做析构与重构,这样原来的所有引用是不会发生改变的。

这样无需下发完整包,无需合成完整包,替换更快更完全。

so层修复

so库修复本质是对native方法的修复和替换。

把补丁so库的路径插入到native-libraryDirectories数组的最前面。就能达到加载so库的时候是补丁so库,而不是加载原来的so库。在启动期间反射注入patch中的so库,对开发者是透明的。其他方案可能需要手动修改system.load函数以实现替换目的。

技术原理

Andfix底层替换方案原理

在native层中直接替换掉原有方法。在原有类基础上进行修改。具体实现:

参数一是需要修改的方法,参数二是需要替换的方法。新方法存在于补丁包中。

通过安卓虚拟机类型判断,切换不同分支。以art虚拟机为例,不同安卓版本的art,底层java对象的数据结构是不同的。所以会根据不同的安卓版本进入不同的分支,以安卓6.0为例:


通过env->FromReflectMethod,可以由method对象获取到这个方法对应的ArtMethod的真正起始地址,然后把它强转为指针,然后就可以对所有成员进行修改。

通过上述代码,实现对目标函数到补丁函数的替换。

为何可以实现热修复?

安卓6.0,虚拟机调用方法原理。

artmethod结构最重要的两个字段是entry_point_from_intercept和entry_point_from_quick_compiled_code,他们是方法的执行入口。java代码会在安卓虚拟机中编译成Dex code。art中采用解释模式或者AOT机器码模式执行。

解释模式是提取dex code,逐条解释执行即可。解释模式下会取得这个方法的entry_point_from_intercept,然后跳转过去执行。

aot模式会预编译dex code对应的机器码,然后运行期间一条条执行机器码即可。同样的,他会跳转到entry_point_from_quick_compiled_code地址。

所以会想到替换这两个入口地址即可实现方法的替换。但实际上,用到了结构中的其他成员字段。

上图中的代码,编译成aot机器码,如下:

在调用方法时,取得了结构中的dexcache_resolved_methods字段。这是一个存放了ArtMethod* 的指针数组。

通过它可以访问到这个method所在dex的所有method对应的artmethod。Activity.oncreate方法的索引是70,64位系统每个指针大小是8字节。artmethod元素是从这个数组的0x2个位置开始存放的,因此偏移是(70+2)*8。其他例子中还会用到其他成员属性,所以需要把所有的成员字段都做替换才能做到顺滑的热修复。

兼容缺陷

在上面的例子中。andfix把底层结构强转了art::method::ArtMethod,这是andfix自己定义的类,与aosp源码一致。但是并不代表是运行时机型的artmethod,如果运行机器在第一个成员属性前加了属性,那么强转就会出现错误。导致需要替换的成员没有做到应该的替换。

这也是andfix无法适配很多机型的原因,本质上就是手机厂商对rom的artmethod做了魔改。

突破底层结构差异

将成员属性替换编程artmethod整体替换。

变成

这样即便任意厂商把底层结构改的六亲不认,也可以做到把旧方法成员变成新方法成员。但是关键的地方在于,如何计算sizeof(ArtMethod)。size计算有偏差,或者替换区域超出边界,会出现非常严重的后果。

rom开发者来说非常简单,但是上层开发者是需要在运行时获取ArtMethod的大小的。所以需要从虚拟机的源码入手,从底层的数据结构以及排列特点探寻答案:

art中,初始化一个类会给类的所有方法分配空间。有direct方法和virtual方法,分别是类的static与不可被继承的对象方法。virtual中就是所有的可以被继承的方法了。 AllocArtMethodArray函数分配了他们的方法所在区域。

ptr是方法数组的指针,方法是一个接一个紧密的new出来的。这时只是分配空间,并没有填入各个ArtMethod的成员值。

可以发现ArtMethod是紧密排列的,所以一个ArtMethod的大小不就是相邻两个方法对应的ArtMethod的起始地址差值吗?

因此我们可以在jni层获取到他们的值。问题 就迎刃而解了。

访问权限

补丁中的类访问同包名下的类会出现访问权限异常。因为与原来的类不是一个classloader,因此两个类不会判定为同包名。

虚拟机的代码中也要求classloader必须一致。所以需要反射修改新类的classloader为原来的classloader。

反射调用非静态方法产生的问题

反射调用后,虽然got的类名一致。但是确是不同的类。前者是被热替换的类,后者是原有的类。两者是不同的。在底层会调用到invokemethod:

这里会做验证,o代表作用的对象。c代表ArtMethod所属的class。所以o必须是c的实例才能通过验证,所以必然不会通过验证。

静态方法是在类的级别直接调用的,不需要接受对象作为入参。所以无需检查。对于这种问题,后续会使用冷启动的方法来处理。

即时生效限制

无法添加减少存在的类的字段、方法,只能做到内容更换。两种情况不适用:

1、引起原有类结构发生变化的更改
2、修复了的非静态方法会被反射调用

对于其他情况,这种方式的热修复都可以被任意使用。


Comment List

© Copyright: This article is an original work and the copyright belongs to the  Depy's docs  unless marked as Reproduced

Please contact the blogger for authorization to reprint