使用该种方式实现就是要了解Android class的加载机制。然后利用反射进行hook。
通过类关系可以看出主要有,PathClassLoader,DexClassLoader两个loader。
BaseDexClassLoader中查找class方法
private final DexPathList pathList;
...
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
// 该方法又调用了DexPathList中的方法
Class c = pathList.findClass(name, suppressedExceptions);
if (c == null) {
ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
DexPathList中查找class的代码
private Element[] dexElements;
...
public Class findClass(String name, List<Throwable> suppressed) {
for (Element element : dexElements) {
DexFile dex = element.dexFile;
if (dex != null) {
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
if (clazz != null) {
// 如果找到目标class就不会执行后面的把我们要修改的class放到前面即可完成替换
return clazz;
}
}
}
if (dexElementsSuppressedExceptions != null) {
suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
}
return null;
}
Element则主要保存dex有关的信息,可以看看系统给我们的对象里面都有哪些信息,对照着创建一个就好了。
try {
ClassLoader classLoader = getClassLoader();
L.i(classLoader.getClass().getSimpleName());
Object pathList = ReflexUtils.getProperty(classLoader, "pathList");
L.i(pathList.toString());
} catch (Exception e) {
e.printStackTrace();
}
04-09 18:34:13.900 I/[TS-FH]: [ onCreate ]PathClassLoader(MainActivity.java:18)
04-09 18:34:13.901 I/[TS-FH]: [ onCreate ]DexPathList[[zip file "/data/app/com.wxfjava.struggle-1/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]](MainActivity.java:22)
# --output=f.dex输出文件名称 源class,就目录下所有class文件打包
dx --dex --output=f.dex **/*.class
应用安装:将apk复制到/data/app/下,dex文件放到/data/dalvik-cache/目录,并在数据目录创建对应的数据信息。
try {
// 将修复dex复制到工作目录下
File srcFile = new File(FIX_DEX_FILE);
File dexFile = new File(dexPath.getAbsolutePath(), srcFile.getName());
FileUtils.copyFile(srcFile, dexFile);
ClassLoader pathClassLoader = mContext.getClassLoader();
Object pathList = ReflexUtils.getProperty(pathClassLoader, "pathList");
Object dexElements = ReflexUtils.getProperty(pathList, "dexElements");
int length = Array.getLength(dexElements);
DexClassLoader dexClassLoader = new DexClassLoader(dexFile.getAbsolutePath(), foptPath, null, pathClassLoader);
Object fixPathList = ReflexUtils.getProperty(dexClassLoader, "pathList");
Object fixDexElements = ReflexUtils.getProperty(fixPathList, "dexElements");
int fixLength = Array.getLength(fixDexElements);
Object newDexElements = Array.newInstance(Array.get(dexElements, 0).getClass(), length + fixLength);
System.arraycopy(fixDexElements, 0, newDexElements, 0, fixLength);
L.i("l:" + length + ",fixl:" + fixLength);
System.arraycopy(dexElements, 0, newDexElements, fixLength, length);
boolean result = ReflexUtils.setProperty(pathList, "dexElements", newDexElements);
L.i(result + "-" + pathList.toString());
} catch (Exception e) {
L.e(e);
}
这种方式只能在有问题的class加载之前进行替换修复才会有效。如果已经加载了有问题的class,进行替换是无效的,所以修复的代码要放到Application中进行执行。
要实现实时修复,就要用到Andfix的实现方式了。直接修改问题方法的内存地址指向完成实时修复。