Android App 线上热修复方案

来源:互联网 发布:北大发展研究院知乎 编辑:程序博客网 时间:2024/05/28 16:24

Android App 线上热修复方案

热修复一词恐怕最早应用在微软。为了巩固其windows系统和office的市场占有率,微软开发并维护了一套线上修复方案,用于修复漏洞及特定问题(LDR),避免延续到发版解决(GDR),详见HotFix维基词条。

天猫android面临同样的问题,尤其对于双十一来讲。提早发出去的包,如果出现客户端的问题,实在是干着急,覆水难收。因此线上修复方案迫在眉睫。那么跟随这篇文章,我们来梳理一下热修复方案的技术细节以及阿里沉淀的相关技术。

一句话概述:基于Xposed中的思想,通过修改c层的Method实例描述,来实现更改与之对应的java方法的行为,从而达到修复的目的。

Xposed

先来了解一下Xposed:诞生于XDA论坛,类似一个应用平台,不同的是其提供诸多系统级的应用。可实现许多神奇的功能。Xposed需要以越狱为前提,像是android中的cydia。

Xposed做了什么

Xposed可以修改任何程序的任何java方法(需root),github上提供了XposedInstaller,是一个android app。提供很多framework层,应用层级的程序。开发者可以为其开发一些系统或应用方面的插件,自定义android系统,它甚至可以做动态权限管理(XposedMods)。

启动和运行

Xposed是在什么时机启动,需要什么条件,来做了如此敏感的动作?

android系统启动与应用启动

zygote进程是android手机系统启动后,常驻的一个名为‘受精卵’的进程,

  • zygote的启动实现脚本在/init.rc文件中

  • 启动过程中执行的二进制文件在/system/bin/app_process

任何应用程序启动时,会从zygote进程fork出一个新的进程。并装载一些必要的class,invoke一些初始化方法。这其中包括像:

  • ActivityThread

  • ServiceThread

  • ApplicationPackageManager

等应用启动中必要的类,触发必要的方法,比如:handleBindApplication,将此进程与对应的应用绑定的初始化方法;同时,会将zygote进程中的dalvik虚拟机实例复制一份,因此每个应用程序进程都有自己的dalvik虚拟机实例;会将已有Java运行时加载到进程中;会注册一些android核心类的jni方法到虚拟机中,支撑从c到java的启动过程。

Xposed做了手脚

Xposed在这个过程改写了app_process(源码在Xposed : a modified app_process binary),替换/system/bin/app_process这个二进制文件。然后做了两个事:

  1. 通过Xposed的hook技术,在上述过程中,对上面提到的那些加载的类的方法hook。

  2. 加载XposedBridge.jar

这时hook必要的方法是为了方便开发者为它开发插件,加载XposedBridge.jar是为动态hook提供了基础。在这个时候加载它意味着,所有的程序在启动时,都可以加载这个jar(因为上面提到的fork过程)。结合hook技术,从而达到了控制所有程序的所有方法。

为获得/system/bin/目录的读写权限,因而需要以root为前提。

Xposed的hook思想

那么Xposed是怎么hook java方法的呢?要从XposedBridge看起,重点在
XposedBridge.hookmethod(原方法的Member对象,含有新方法的XC_MethodHook对象);,这里会调到

private native synchronized static void hookMethodNative(Member method, Class<?> declaringClass, int slot, Object additionalInfo);

这个native的方法,通过这个方法,可以让所hook的方法,转向native层的一个c方法。如何做到?

When a transmit from java to native occurs, dvm sets up a native stack.
In dvmCallJNIMethod(), dvmPlatformInvoke is used to call the native method(signature in Method.insns).

在jni这个中间世界里,类型数据由jni表来沟通java和c的世界;方法由c++指针结合DVM*系(如dvmSlotToMethod,dvmDecodeIndirectRef等方法)的api方法,操作虚拟机,从而实现java方法与c方法的世界。

那么hook的过程是这样:首先通过dexclassload来load所要hook的方法,分析类后,进c层,见代码XposedBridge_hookMethodNative方法,拿到要hook的Method类,然后通过dvmslotTomethod方法获取Method*指针,

Method* method = dvmSlotToMethod(declaredClass, slot);

declaredClass就是所hook方法所在的类,对应的jobject。slot是Method类中,描述此java对象在vm中的索引;那么通过这个方法,我们就获取了c层的Method指针,通过

SET_METHOD_FLAG(method, ACC_NATIVE);

将该方法标记为一个native方法,然后通过

method->nativeFunc = &hookedMethodCallback;

Point “method->nativeFunc” at the JNI bridge, and overload “method->insns” to point at the actual function.

引用自Jni源码注释。定向c层方法到hookedMethodCallback,这样当被hook的java方法执行时,就会调到c层的hookedMethodCallback方法。

After name resolved via dvmResolveNativeMethod, meth->nativeFunc is assigned with new MethodCallBridge dvmCallJNIMethod.
So, only resolve once for one native method.

通过meth->nativeFunc重定向MethodCallBridge到hookedMethodCallback这个方法上,控制这个c++指针是无视java的private的。

另外,在method结构体中有

method->insns = (const u2*) hookInfo;

用insns指向替换成为的方法,以便hookedMethodCallback可以获取真正期望执行的java方法。

现在所有被hook的方法,都指向了hookedMethodCallbackc方法中,然后在此方法中实现调用替换成为的java方法。

从Xposed提炼精髓

回顾Xposed,以root为必要条件,在app_process加载XposedBidge.jar,从而实现有hook所有应用的所有方法的能力;而后续动态hook应用内的方法,其实只是load了从zypote进程复制出来的运行时的这个XposedBidge.jar,然后hook而已。因此,若在一个应用范围内的hook,root不是必须的,只是单纯的加载hook的实现方法,即可修改本应用的方法。

基于其思路,阿里沉淀出两套热修复解决方案,并已开源

  1. dexposed

  2. AndFix

两技术方案均已开源,实现细节可自行前往,学习了解。这里概述一下两者与Xposed的关系,以及两者的区别。

dexposed

dexposed紧继承自Xposed,拥有强大的:「原方法前hook」「方法替换」「原方法后hook」三种方式。相互组合,可依据你的hook思想,解决和规避几乎所有的意外情形。它更像是一个hook工具,效果跟使用者思路关系紧密,是地地道道的hook方案。在阿里有着良好的实践成果。

AndFix

AndFix提炼精华,轻便、简洁(详见源码)的完成了热修复方案。完美的支持了Android 2.3到6.0系统,以及x86框架,机型覆盖率广。提供根据两个apk生成patch的工具,因而使用者只需正向编程,通过工具生成patch文件,下发给客户端即可,编程效率高。是完善的、高可用的热修复方案。

正向编程:意思是说像你发新版那样去解决问题,而不是用hook思想去解决问题

关于AndFix的apkpatch工具

它根据两个apk差别来生成apatch文件,不要将其完全理解成是一个差异文件。它是对比两个apk中的smali文件,找到不同的方法,增加方法annotation(供客户端修复逻辑识别并修复),保留此方法所在类的smali描述,修改类名、将其再打成dex,并与META-INF下的签名、证书、包含有patch信息的PATCH.MF一并打成一个压缩文件,文件格式命为apatch。

对smali类方法修改的内容如下:

.class public Lorg/renlonglee.andfixsample.classWillBeingFixed_CF;.super Landroid/app/Activity;.source "classWillBeingFixed.java".method private methodWillBeingReplaced()V    .locals 0    .annotation runtime Lcom/alipay/euler/andfix/annotation/MethodReplace;        clazz = "org.renlonglee.andfixsample.classWillBeingFixed"        method = "methodWillBeingReplaced"    .end annotation    .prologue    .line 13    return-void.end method

请留意这些修改:

  1. 类名由classWillBeingFixed被修改成为classWillBeingFixed_CF

  2. 被替换的方法methodWillBeingReplaced,被添加了一个MethodReplace注解。注解描述中说明了原类及原方法。

由此,你应该了解到,对于客户端,这些信息已经足够。那么客户端的处理为:解析apatch->解析dex->加载类->识别含有MethodReplace注解的方法->根据原方法签名已经新方法smali描述进行hook并替换。

实现细节

AndFix在实现上相对简洁,例如在c层核心hook代码中:

Method* meth = (Method*) env->FromReflectedMethod(src);Method* target = (Method*) env->FromReflectedMethod(dest);

来获取Method实例指针,免去获取slot,再从classLoader根据偏移量获取的方式。

我们还做了什么

以上为开源内容,当你想应用到自己的项目中,还需要做更多:

  • patch包加密、校验

  • app的安全校验

  • 版本控制

  • 加载的安全性

业界其他方案

本篇介绍的方案是从「修改对应java类的c层对象」来实现的热修复,业界内也不乏通过「修改BaseDexClassLoader中的pathList,来动态加载dex」方式实现热修复。后者纯java实现,但需要hack类的优化流程,将打CLASS_ISPREVERIFIED标签的类,去除此标签,以解决类与类引用不在一个dex中的异常问题。这会放弃dex optimize对启动运行速度的优化。原则上,这对于方法数没有大到需要multidex的应用,损失更明显。而前者不触犯原有的优化流程,只点杀需要hook的方法,更为纯粹、有效。

MORE to explore

另外,dexposed和AndFix各自对方案做了诸多不同思路的实现,感兴趣的可对比Xposed继续探索。再贴一下各自c层实现方法替换的传送门:

  • Xposed

  • dexposed

  • AndFix

写在最后

在业界中,热修复方案,棘手且重要。有时它可以避免发新版,避免无谓时间的浪费,稳住迭代节奏;有时它可以救项目于水深火热之中;有时它也可以带给用户展现上的惊喜。其技术较深且要抵抗更深技术的侵入。阿里将其开源,旨在更多人加入到这个「路漫漫其修远兮」的终端热修复解决方案中,欢迎大家参与开源,提宝贵issue。从目前的android端技术发展来看,原则上这个hook角度是可以永存的。当然还需要更多人的参与与维护,冲破各种未来的限制。另外,热修复只是此技术的一种表现形式,它的更多作用与潜藏的力量,我们共同挖掘。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 手胳膊麻疼怎么办 小手臂扭到筋了怎么办 右胳膊拧着筋了怎么办 踢球膝关节伤筋怎么办 腿膝盖内侧扭伤怎么办 腱鞘炎做小针刀手指伸不直怎么办 胳膊无故筋疼怎么办 胳膊肘聚筋了怎么办 手臂筋扭伤肿痛怎么办 胳膊经常扭到怎么办 小孩胳膊扭到了怎么办 打篮球胳膊扭了怎么办 大腿根有淋巴结怎么办 脸上长了淋巴结怎么办 拔牙后淋巴肿痛怎么办 脖子上生淋巴结怎么办 脖子两侧淋巴结肿大怎么办 脖子左侧锁骨痛怎么办 整牙后中线歪了怎么办 右侧卵巢旁囊肿怎么办 颈下淋巴结肿大怎么办 耳朵后面淋巴结肿大怎么办 颈侧淋巴结肿大怎么办 锁骨窝淋巴肿大怎么办 晚上颈肌痉挛怎么办 劲部淋巴结肿大怎么办 胸锁乳头肌增厚怎么办 幼儿脖子两边歪怎么办 小孩脖子偏一边怎么办 小孩头有点歪怎么办 宝宝脖子有点歪怎么办 心情郁闷胸口疼怎么办 宝宝咳嗽胸口疼怎么办 小孩脖子歪了怎么办 脖子有点向右偏怎么办 感觉乳房有硬块怎么办 宝宝有点歪头怎么办 一岁半宝宝头歪怎么办 宝宝头偏向左边怎么办 长大了头睡偏了怎么办 一岁半宝宝头偏怎么办