目录
1 前言
2 热修复技术
2.1 Dexposed
2.2 AndFix
2.3 QZone
2.4 Tinker
2.6 epic
2.7 YAHFA
2.8 FastHook
2.9 美团Robust
3 总结
1 前言
对热修复技术做一个总结,也是告一个段落(几年前的研究,有些可能过时了)
以AndFix入门,具体可以参考以前的使用步骤。
热修复系列——AndFix使用教程基础篇_疑是银河落九天的博客-CSDN博客_andfix使用
2 热修复技术 2.1 Dexposed详情参考
Android中免Root实现Hook的Dexposed框架实现原理解析以及如何实现应用的热修复_尼古拉斯.赵四的博客-CSDN博客_dexposed
工作原理
- 基于Xposed的AOP框架
- 免root实现方法级粒度hook
- 通过JNI注册,从Java转Native,然后在native中修改被hook方法的一些信息,最后通过反射调用handleHookedMethod回到Java
- 立即生效
限制
不支持ART(xposed重新编译了自己的libart.so,并阻止内联,需要对不同版本兼容)
2.2 AndFix详情参考
Android热修复框架AndFix原理解析及使用_尼古拉斯.赵四的博客-CSDN博客
工作原理
AndFixManager.java类的replaceMethod方法
private void replaceMethod(ClassLoader classLoader, String clz,
String meth, Method method) {
try {
String key = clz + "@" + classLoader.toString();
//key为新类名@默认类加载器名
Class> clazz = mFixedClass.get(key);
if (clazz == null) {// class not load
//使用默认类加载器加载修复类对应的原始类
Class> clzz = classLoader.loadClass(clz);
...
}
ArtMethod结构替换代码
void replace_7_0(JNIEnv* env, jobject src, jobject dest) {
art::mirror::ArtMethod* smeth =
(art::mirror::ArtMethod*) env->FromReflectedMethod(src);
art::mirror::ArtMethod* dmeth =
(art::mirror::ArtMethod*) env->FromReflectedMethod(dest);
// reinterpret_cast(smeth->declaring_class_)->class_loader_ =
// reinterpret_cast(dmeth->declaring_class_)->class_loader_; //for plugin classloader
reinterpret_cast(dmeth->declaring_class_)->clinit_thread_id_ =
reinterpret_cast(smeth->declaring_class_)->clinit_thread_id_;
reinterpret_cast(dmeth->declaring_class_)->status_ =
reinterpret_cast(smeth->declaring_class_)->status_ -1;
//for reflection invoke
reinterpret_cast(dmeth->declaring_class_)->super_class_ = 0;
smeth->declaring_class_ = dmeth->declaring_class_;
smeth->access_flags_ = dmeth->access_flags_ | 0x0001;
smeth->dex_code_item_offset_ = dmeth->dex_code_item_offset_;
smeth->dex_method_index_ = dmeth->dex_method_index_;
smeth->method_index_ = dmeth->method_index_;
smeth->hotness_count_ = dmeth->hotness_count_;
smeth->ptr_sized_fields_.dex_cache_resolved_methods_ =
dmeth->ptr_sized_fields_.dex_cache_resolved_methods_;
smeth->ptr_sized_fields_.dex_cache_resolved_types_ =
dmeth->ptr_sized_fields_.dex_cache_resolved_types_;
smeth->ptr_sized_fields_.entry_point_from_jni_ =
dmeth->ptr_sized_fields_.entry_point_from_jni_;
smeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_ =
dmeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_;
LOGD("replace_7_0: %d , %d",
smeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_,
dmeth->ptr_sized_fields_.entry_point_from_quick_compiled_code_);
}
限制
- 以Field为切入点,使用自定义类加载器生成新的ArtMethod,使用默认类加载器生成旧ArtMethod,再进行结构内字段的替换。
- AndFix采用的是替换结构体中的字段,但由于厂商可能会修改ArtMethod结构体,因此会有兼容问题。Sophix(是阿里推出的移动热修复方案,闭源,有本书《深入探索Android热修复原理》介绍了Sophix,支持)采用的是替换整个ArtMethod结构体,这样就不会存在兼容问题。
- 立即生效
- 目前支持2.3 - 7.0
- 《深入探索Android热修复原理》讲述sophix的对比图如下
详情参考
安卓App热补丁动态修复技术介绍
工作原理
- classloader可以包含多个dex文件,排成一个有序数组dexElements,当查找类时会按顺序遍历dex文件,如果不同的dex有相同的类存在,那么会优先选择排在前面的dex文件中的类
- 如果一个类与它直接引用的类在同一个dex文件中,那么该类会被打上preverify标签。因此如果需要修复类与引用类所在dex文件不同则会报错。解决方案是往所有类的构造函数插桩引用一个不存在的类,防止被打上标签
- 重启生效,因为已经加载过的类无法被卸载,想要重新加载新的类就需要重启APP。
限制
- 由于插桩导致所有类都非preverify,因此加载类时会触发verify与optimize *** 作,由于启动时会加载大量的类,因此效率影响较大
- 存在art下内存地址混乱问题,因为dex2oat将类能确定的各个地址写死,运行时补丁包的地址改变,原始类调用就会出错
详情参考
GitHub - WeMobileDev/article: articles by WeChat Mobile Development Team
工作原理
使用DexDiff算法,在编译时通过新旧Dex生成差异包,将差异包与旧Dex还原为新Dex,重启生效
限制
- 占用Rom体积,大约是修改Dex数量的1.5倍(dexopt与dex压缩成jar)的大小
- 需要额外进程合成新的Dex,合成时间的长短与内存消耗也会影响最终的成功率
详情参考
我为Dexposed续一秒——论ART上运行时 Method AOP实现 | Weishu's Notes
https://github.com/tiann/epic/blob/master/README_cn.md
工作原理
通过callee的ArtMethod对象得到方法的entrypoint,根据方法入口点找到对应地址,直接将内存的前8个字节修改为一段跳转指令,然后在跳转指令中执行二段跳板,进而跳转到新方法中执行
限制
- 受限于dynamic callee-side rewrite机制,如果被Hook函数的code段太短以至于一个简单的trampoline跳转都放不下,那么epic无效
- 如果ART中有深度内联,直接把本函数的代码内联到调用者,那么epic无效
- 仅支持Android 5-11
- 当前仅支持thumb2/arm64指令集,arm32/x86/x86_64/mips/mips64还没有适配
- 在支持硬浮点的cpu架构,比如(armeabi-v7a, arm64-v8a)上,带有double/float参数的函数Hook可能有问题,没有充分测试
- 还有一些其他机型上可能存在的闪退
- 目前已测机型:5.0, 5.1, 6.0, 7.0, 7.1个别机型,以及这些机型的thumb2指令集,和6.0/7.1 的arm64指令集(作者博客所述)
- 无法修改类初始化过程中执行的函数
详情参考
YAHFA--ART环境下的Hook框架 - 记事本
在Android N上对Java方法做hook遇到的坑 - 记事本
工作原理
修改目标方法的ArtMethod结构体,将hook方法的ArtMethod地址保存在原方法的entry_point_from_jni,并修改原方法的entry_point_from_quick_compiled_code,使其指向一段辅助代码,在这里完成eax的设置和跳转,即时生效。
从原理上看,还是走的虚拟机调用流程,与AndFix的不同之处在于,AndFix直接替换了Method,而YAHFA使用一种简单方式实现了AOP,能够实现方法的hook并回调原始方法
限制
- 无法解决方法直接跳转,也就是不走ArtMethod入口,目标方法地址直接写死(其实这种情况在高版本里比较少了,特别是第三方应用的方法更少)
- 由于class加载后才进行的结构替换,因此field在class中的相对地址已经确定,无法进行字段的新增与删除
- 因为直接替换方法,此时已经执行了loadClass初始化,因此在该过程中执行的方法如构造函数、静态成员都有问题
详情参考
[原创]FastHook——一种高效稳定、简洁易用的Android Hook框架-Android安全-看雪论坛-安全社区|安全招聘|bbs.pediy.com
[原创]FastHook——实现.dynsym段和.symtab段符号查询-Android安全-看雪论坛-安全社区|安全招聘|bbs.pediy.com
[原创]FastHook——远超YAHFA的优异稳定性-Android安全-看雪论坛-安全社区|安全招聘|bbs.pediy.com
[原创]FastHook——巧妙利用动态代理实现非侵入式AOP-Android安全-看雪论坛-安全社区|安全招聘|bbs.pediy.com
https://github.com/turing-technician/FastHook
工作原理
这个框架很优秀,其实还是native替换,fasthook支持内联替换和entrypoint两种方式,而且手动jit的过程中处理了很多细节,值得参考。
限制
还是native替换的通病,不能新增类/字段,以及类初始化过程中执行的函数
2.9 美团Robust详情参考
Android热更新方案Robust - 美团技术团队
工作原理
Instant Run类型方案,基于编译过程进行插桩,有代码侵入,对运行效率,包体积,方法数有一定影响。
限制
插桩原始代码,因此会有代码冗余
3 总结总结,具体对比我就略过了,技术参考链接已经很详细了。
总的来说,就是Java整体替换的方式可以新增类/字段,native内存替换只能修改方法体本身。
整体全面的热修复方案以Sophix为代表,不过它不开源。
native替换以FastHook为代表,不过有点黑科技,处理适配和兼容应该会花不少时间。
Robust插桩方案除开对代码有侵入性的话,从稳定性和兼容性来说,应该是最好的(但是这套方案没有详细了解)
有错误欢迎指正,也欢迎技术交流!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)