热修复一词恐怕最早应用在微软。为了巩固其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进程fork出一个新的进程。并装载一些必要的class,invoke一些初始化方法。这其中包括像:
等应用启动中必要的类,触发必要的方法,比如: Xposed做了手脚Xposed在这个过程改写了app_process(源码在Xposed : a modified app_process binary),替换/system/bin/app_process这个二进制文件。然后做了两个事:
这时hook必要的方法是为了方便开发者为它开发插件,加载XposedBridge.jar是为动态hook提供了基础。在这个时候加载它意味着,所有的程序在启动时,都可以加载这个jar(因为上面提到的fork过程)。结合hook技术,从而达到了控制所有程序的所有方法。 为获得/system/bin/目录的读写权限,因而需要以root为前提。 Xposed的hook思想
那么Xposed是怎么hook java方法的呢?要从XposedBridge看起,重点在
这个native的方法,通过这个方法,可以让所hook的方法,转向native层的一个c方法。如何做到?
在jni这个中间世界里,类型数据由jni表来沟通java和c的世界;方法由c++指针结合DVM*系(如dvmSlotToMethod,dvmDecodeIndirectRef等方法)的api方法,操作虚拟机,从而实现java方法与c方法的世界。
那么hook的过程是这样:首先通过dexclassload来load所要hook的方法,分析类后,进c层,见代码XposedBridge_hookMethodNative方法,拿到要hook的Method类,然后通过
declaredClass就是所hook方法所在的类,对应的jobject。slot是Method类中,描述此java对象在vm中的索引;那么通过这个方法,我们就获取了c层的Method指针,通过
将该方法标记为一个native方法,然后通过
引用自Jni源码注释。定向c层方法到
通过 另外,在method结构体中有
用insns指向替换成为的方法,以便
现在所有被hook的方法,都指向了 从Xposed提炼精髓回顾Xposed,以root为必要条件,在app_process加载XposedBidge.jar,从而实现有hook所有应用的所有方法的能力;而后续动态hook应用内的方法,其实只是load了从zypote进程复制出来的运行时的这个XposedBidge.jar,然后hook而已。因此,若在一个应用范围内的hook,root不是必须的,只是单纯的加载hook的实现方法,即可修改本应用的方法。 基于其思路,阿里沉淀出两套热修复解决方案,并已开源: 两技术方案均已开源,实现细节可自行前往,学习了解。这里概述一下两者与Xposed的关系,以及两者的区别。 dexposeddexposed紧继承自Xposed,拥有强大的:「原方法前hook」「方法替换」「原方法后hook」三种方式。相互组合,可依据你的hook思想,解决和规避几乎所有的意外情形。它更像是一个hook工具,效果跟使用者思路关系紧密,是地地道道的hook方案。在阿里有着良好的实践成果。 AndFixAndFix提炼精华,轻便、简洁(详见源码)的完成了热修复方案。完美的支持了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类方法修改的内容如下:
请留意这些修改:
由此,你应该了解到,对于客户端,这些信息已经足够。那么客户端的处理为:解析apatch->解析dex->加载类->识别含有MethodReplace注解的方法->根据原方法签名已经新方法smali描述进行hook并替换。 实现细节AndFix在实现上相对简洁,例如在c层核心hook代码中:
来获取Method实例指针,免去获取slot,再从classLoader根据偏移量获取的方式。 我们还做了什么以上为开源内容,当你想应用到自己的项目中,还需要做更多:
业界其他方案本篇介绍的方案是从「修改对应java类的c层对象」来实现的热修复,业界内也不乏通过「修改BaseDexClassLoader中的pathList,来动态加载dex」方式实现热修复。后者纯java实现,但需要hack类的优化流程,将打CLASS_ISPREVERIFIED标签的类,去除此标签,以解决类与类引用不在一个dex中的异常问题。这会放弃dex optimize对启动运行速度的优化。原则上,这对于方法数没有大到需要multidex的应用,损失更明显。而前者不触犯原有的优化流程,只点杀需要hook的方法,更为纯粹、有效。 MORE to explore另外,dexposed和AndFix各自对方案做了诸多不同思路的实现,感兴趣的可对比Xposed继续探索。再贴一下各自c层实现方法替换的传送门: 写在最后在业界中,热修复方案,棘手且重要。有时它可以避免发新版,避免无谓时间的浪费,稳住迭代节奏;有时它可以救项目于水深火热之中;有时它也可以带给用户展现上的惊喜。其技术较深且要抵抗更深技术的侵入。阿里将其开源,旨在更多人加入到这个「路漫漫其修远兮」的终端热修复解决方案中,欢迎大家参与开源,提宝贵issue。从目前的android端技术发展来看,原则上这个hook角度是可以永存的。当然还需要更多人的参与与维护,冲破各种未来的限制。另外,热修复只是此技术的一种表现形式,它的更多作用与潜藏的力量,我们共同挖掘。 PS: pc上查看请到这里. |
|