rocoofix原理分析
来源:互联网 发布:迅雷7mac官方下载 编辑:程序博客网 时间:2024/05/18 01:35
rocoofix原理分析
假设如下dex
1:下面分析android是如何加载class的,比如b.class
核心代码
https://android.googlesource.com/platform/libcore-snapshot/+/ics-mr1/dalvik/src/main/java/dalvik/system/DexPathList.java
public Class findClass(String name) { //dexElements [class.dex,class1.dex.....] for (Element element : dexElements) { DexFile dex = element.dexFile; if (dex != null) { Class clazz = dex.loadClassBinaryName(name, definingContext); if (clazz != null) { return clazz; } } } return null; } //可以看到是按照dexElements数组里面的dex文件顺序循环,第一个成功找到并返回
那么我们hack上面这个查找过程,新增补丁dex
.... //dexElements [补丁.dex,class.dex,class1.dex.....] //加入补丁.dex,并放到数组最前面,那么就会首先查找补丁.dex的类 for (Element element : dexElements) { ....
2:成功插入了补丁的class,但是下面的情况会出问题
报错:java.lang.IllegalAccessError: Class ref in pre-verified class场景:c.class调用b.class现在是 c.class[class.dex]---->b.class[补丁.dex] 不同dex中,报错原先是c.class[class.dex]---->b.class[class.dex] 同一个dex中,不报错
错误原因:被打上CLASS_ISPREVERIFIED标签的类会进行校验。
报错源代码:
https://android.googlesource.com/platform/dalvik.git/+/51801371a9b0f829303d326a2300518177dde3e8/vm/oo/Resolve.cpp
if (!fromUnverifiedConstant && IS_CLASS_FLAG_SET(referrer, CLASS_ISPREVERIFIED)) { ClassObject* resClassCheck = resClass; if (dvmIsArrayClass(resClassCheck)) resClassCheck = resClassCheck->elementClass; if (referrer->pDvmDex != resClassCheck->pDvmDex && resClassCheck->classLoader != NULL) { ALOGW("Class resolved by unexpected DEX:" " %s(%p):%p ref [%s] %s(%p):%p", referrer->descriptor, referrer->classLoader, referrer->pDvmDex, resClass->descriptor, resClassCheck->descriptor, resClassCheck->classLoader, resClassCheck->pDvmDex); ALOGW("(%s had used a different %s during pre-verification)", referrer->descriptor, resClass->descriptor); dvmThrowIllegalAccessError( "Class ref in pre-verified class resolved to unexpected " "implementation"); return NULL; } }
解决办法:防止类被打上CLASS_ISPREVERIFIED标签
1:那么类是什么时候被打上CLASS_ISPREVERIFIED标签的
https://android.googlesource.com/platform/dalvik/+/froyo-release/vm/analysis/DexVerify.c
if (dvmVerifyClass(clazz, VERIFY_DEFAULT)) { //校验通过则写入CLASS_ISPREVERIFIED标签 assert((clazz->accessFlags & JAVA_FLAGS_MASK) == pClassDef->accessFlags); ((DexClassDef*)pClassDef)->accessFlags |= CLASS_ISPREVERIFIED;//加入标签 }
2:如何防止被打上标签
原理:让校验不通过
如果a.class 引用了不在同一个dex文件中的class,那么a.class就不会有CLASS_ISPREVERIFIED
上面c.class开始和b.class在同一个dex,所以c.class被打上标签
后面补丁的b.class和c.class不在同一个dex,所以报错,为了解决这个问题
我们要事先就要防止a.class被打上标签
3 方法:
使用gralde插件,编译阶段在a.class的构造函数中插入(通过字节码修改库asm)
Hack.class的引用,hack.class在独立的dex文件中,那么a.class的标签就不会打上
https://github.com/dodola/RocooFix 实现了上面方案的开源库
rocoo 解决类加载问题核心代码
Field pathListField = RocooUtils.findField(loader, "pathList"); Object dexPathList = pathListField.get(loader); Field dexElement = RocooUtils.findField(dexPathList, "dexElements"); Class<?> elementType = dexElement.getType().getComponentType(); Method loadDex = RocooUtils.findMethod(dexPathList, "loadDexFile", File.class, File.class, ClassLoader.class, dexElement.getType()); loadDex.setAccessible(true); Object dex = loadDex.invoke(null, additionalClassPathEntries.get(0), optimizedDirectory, loader, dexElement.get(dexPathList)); Constructor<?> constructor = elementType.getConstructor(File.class, boolean.class, File.class, DexFile.class); constructor.setAccessible(true); Object element = constructor.newInstance(new File(""), false, additionalClassPathEntries.get(0), dex); Object[] newEles = new Object[1]; newEles[0] = element; RocooUtils.expandFieldArray(dexPathList, "dexElements", newEles);
rocoofix 插件,解决CLASS_ISPREVERIFIED 问题 核心代码
v.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); v.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false); v.visitJumpInsn(Opcodes.IFEQ, l1); v.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); v.visitLdcInsn(Type.getType("Lcom/dodola/rocoo/Hack;")); v.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V", false); v.visitLabel(l1);
参考
空间的文章 感谢qqzone的无私奉献
RocooFix 感谢dodola的无私奉献
- rocoofix原理分析
- Android RocooFix 使用注意事项
- RocooFix源码解析
- 热修复 RocooFix
- Android RocooFix 热修复框架
- Android RocooFix 热修复框架
- Android RocooFix 热修复框架
- Android RocooFix 热修复框架
- Android RocooFix 热修复框架
- Android RocooFix 热修复框架
- Android 热修复框架RocooFix
- Android RocooFix 热修复框架[2]
- Android RocooFix 热修复框架[1]
- Android RocooFix热修复动态加载框架介绍
- EJB调用原理分析
- BT原理分析
- EJB调用原理分析
- 珊瑚虫外挂原理分析
- 潜龙勿用(1)
- POJ 1990 MooFest
- Sublime Text 2 安装主题的方法
- php数组
- 十年沉浮,也落魄过,当时是个程序员
- rocoofix原理分析
- C++ sgi STL学习笔记之non-mutating algorithm
- 什么是C++虚函数、虚函数的作用和使用方法
- [SPOJ BALNUM - Balanced Numbers]数位DP
- 【NOI OJ】18 打印月历
- tcp数据重传时间细节探秘及数据中心优化
- 如何指定第一个dex中的类
- Linux系统CPU核数等信息查看
- C++ STL--stack/queue 的使用方法