返回

**Android进阶宝典 -- AndFix热修复原理**

Android

前言

众所周知,Android应用在发布后,难免会出现bug,传统的解决方式需要发 版新的apk,用户需要从应用商店下载新的apk,这是一个非常耗时的过程。AndFix出现后,通过在 dex文件中加载修复包,在无需发 版新的apk的情况下,对修复这些bugs,使用AndFix,可以实现修复bug无缝、安全,无需等待发 版。

AndFix的原理是基于字节码修改,它需要将修复好的代码patch进dex文件,然后加载到内存中,然后反射的方式, 将修改好的代码替换当前的代码,以修复相关的bug。

了解AndFix的原理,首要的问题是了解Android平台下,Java虚拟机是如何处理方法的。Android平台下,手机厂商 为了满足Android应用的跨平台特性,需要采用兼容Java平台的虚拟机,常见的虚拟机有Java虚拟机和ART虚拟机。

而为了理解Java虚拟机和ART虚拟机的原理,需要了解,Java虚拟机是如何加载一个class文件,然后执行一个 method。

方法执行流程

1. Method 的查找过程

从指定的类路径上的各个类加载器,加载 指定的class文件,调用java.lang.Class 中的 forName,创建一个 java.lang.Class 的实例。

Class Class.forName(String className) throws ClassNotFoundException
{
...
}

然后通过 class 的 method 字段,获取所有的 method。

method是method修饰符,参数列表,返回值类型,异常表。

如果出现重复的,则选择符合下 述规则的那个method。

(1)与主参数表形 型完全相同。
(2)返回类型兼容。

得到最精确的一个 method。

2.Method 的调用过程

最精确的method 对应的methodHandle。

该methodHandle是一种类型,有的是个普通 指针,有的是个代码。

如果是指针,则直接执行对应method。

如果是代码,是类型methodHandle的call方法,

然后执行对应的method。

ARM指令集

Android ART 指令集

ARM指令集分为两种,ARM,THUMB。

其中THUMB 是 ARM 指令集的精简版,为了节约空间而创 造的,只有16位,而ARM指令集有32位。

LDM 高低 / LDMIA 高低 / LDMH 高低:

Load Multiple
多个寄存器的一个一个加载到内存

STM 高低 / STMD 高低 / STMM 高低:

Store Multiple
多个寄存器的一个一个存储到内存

LDDP 高低 / LDDP 高低:

Load Doubleword to register
从内存中加载双字到寄存器

SDDP 高低 / SDDP 高低:

Store Doubleword to register
把寄存器中的双字存储到内存

MRCC 高低 / MRC 高低:

Move Register to Register Conditional
将某一寄存器中的值放入到另一个寄存器

MRCCC 高低 / MRCCC 高低 / MBNTC 高低 / MNTEC 高低 /

Move Register to Register Conditional Carry
带进位条件的操作指令

BX 高低 / BLX 高低:

branch exchange
条件跳转和链式跳转

TBNZ 高低 / TBLZ 高低:

Test and Branch
Test
指令后面的条件可以选择通过什么标志 跳转。

CPSR 高低 / CPSRd 高低:

Change Processor Status Register
伪指令,执行指令后,将设置的CPSR状态,写入到CPSR寄存器中,低字节等同于 CPSR,高字节为0。

ArtMethod

ArtMethod 在 ARM 中,采用的是指针寄存器:r0,返回参数和 返回值,采用R2,R3,...,R10,传递参数。

ArtMethod 是个指针,这是一个函数指针,ArtMethod 也包含了 method 的相关信息,然后调用method时, 就是通过 ArtMethod 指针,读取 method 中的 methodPointer 字段,然后调用methodHandle,然后执行method。

当然ArtMethod 不止methodPointer,还有 MethodSignature、MemberType、ReturnType、 局部变量表、异常表、callsite等数据

实现 AndFix 热修复的基本原理

从Dex文件中加载修复包,加载到内存,然后反射,最后替换修复好的method。

通过So进行热修复,需要通过JNI,我们需要监听onClassLoading这个方法,然后从Java进行反射

1. 加载修复包到 JVM

以dex进行传输,不能是apk,APP端的手机是 ART,通过LoadLibrary,加载预先准备好的libAndFix.so

将其定位成可执行,我们使用openDEX,这 是 ART 提供的 api,调用这个 api,然后进行 读取,通过dlopen进行装载。

最后调用自己的 loadLibrary() 函数,将我们准备的修复包,放入 ArtMethod

2. 准备数据

需要检测出,修改的方法的 methodSignature,然后将修复的方法中的 methodSignature 和 当前的方法 methodSignature 进行比较,用来检测到修复的方法。

Java 中通过反射,拿到 method,再拿到 methodSignature,将 methodSignature传 输到C,然后通过函数转化,拿到 ArtMethod 指针,用来检测出修复的方法。

3. 检查需要修复的方法

从 java 从 Java8 开始,可以对函数进行修改,现在需要把 java7 中的数据,迁移到 java8,这就需要读取到ArtMethod

然后把 ArtMethod传到java,然后调用 callSite,然后进行method 的替换。

4. method 的替换

所有的 method 在 java 中,全是方法指针,一个 method 可以有多个指针,指针会被被 So 通过 dlopen装载,因为 ArtMethod 有一个 methodPointer

从 ArtMethod 中得到 methodPointer,它是个函数指针,它会 指向某个 函数,指向到 Arm 指令,这是一种汇编,直接改 Arm 指令中的函数指针,直接指向我们 修改好的 ArtMethod 中的函数指针,然后修改好后,最后记得发送消息,这样检测到,修复的方法已经完成。

后记

理解 AndFix 热修复原理,首要的问题就是了解,Java虚拟机是如何加载一个 class文件,然后执行一个 method

推荐书籍

《Android 进阶宝典》,《深入理解Java虚拟机》。