01.参考文章:https://blog.csdn.net/qq_39799899/article/details/102478355
02.Java层修复(dex文件覆盖):本文中使用
03.Native层修复:实例:https://blog.csdn.net/u013132758/article/details/96644721
1、创建工具类FixDexUtil:
注意:设置补丁文件存放位置REPAIR_FILE_PATH
public class FixDexUtil { //这下面两个属性可自己修改 private final static String REPAIR_FILE_NAME = "BugClass"; //修复文件名 private final static String REPAIR_FILE_PATH = "fix"; //修复文件路径(默认初始路径为根目录) private static final String DEX_SUFFIX = ".dex"; private static final String APK_SUFFIX = ".apk"; private static final String JAR_SUFFIX = ".jar"; private static final String ZIP_SUFFIX = ".zip"; private static final String DEX_DIR = "odex"; private static final String OPTIMIZE_DEX_DIR = "optimize_dex"; private static HashSetloadedDex = new HashSet<>(); static { loadedDex.clear(); } public static void startRepair(final Context context) { File externalStorageDirectory = context.getExternalCacheDir(); // 遍历所有的修复dex , 因为可能是多个dex修复包 File fileDir = externalStorageDirectory != null ? new File(externalStorageDirectory, FixDexUtil.REPAIR_FILE_PATH) : new File(context.getFilesDir(), FixDexUtil.DEX_DIR);// data/user/0/包名/files/odex(这个可以任意位置) if (!fileDir.exists()) {//如果目录不存在就创建所有目录,这里需要添加权限 fileDir.mkdirs(); } if (FixDexUtil.isGoingToFix(context)) { FixDexUtil.loadFixedDex(context, context.getExternalCacheDir()); Log.i("GT_", "正在修复"); } } public static void loadFixedDex(Context context) { loadFixedDex(context, null); } public static void loadFixedDex(Context context, File patchFilesDir) { // dex合并之前的dex doDexInject(context, loadedDex); } public static boolean isGoingToFix(@NonNull Context context) { boolean canFix = false; File externalStorageDirectory = context.getExternalCacheDir(); // 遍历所有的修复dex , 因为可能是多个dex修复包 File fileDir = externalStorageDirectory != null ? new File(externalStorageDirectory, REPAIR_FILE_PATH) : new File(context.getFilesDir(), DEX_DIR);// data/data/包名/files/odex(这个可以任意位置) File[] listFiles = fileDir.listFiles(); if (listFiles != null) { for (File file : listFiles) { if (file.getName().startsWith(REPAIR_FILE_NAME) && (file.getName().endsWith(DEX_SUFFIX) || file.getName().endsWith(APK_SUFFIX) || file.getName().endsWith(JAR_SUFFIX) || file.getName().endsWith(ZIP_SUFFIX))) { loadedDex.add(file);// 存入集合 //有目标dex文件, 需要修复 canFix = true; } } } return canFix; } private static void doDexInject(Context appContext, HashSet loadedDex) { String optimizeDir = appContext.getFilesDir().getAbsolutePath() + File.separator + OPTIMIZE_DEX_DIR; // data/data/包名/files/optimize_dex(这个必须是自己程序下的目录) File fopt = new File(optimizeDir); if (!fopt.exists()) { fopt.mkdirs(); } try { // 1.加载应用程序dex的Loader PathClassLoader pathLoader = (PathClassLoader) appContext.getClassLoader(); for (File dex : loadedDex) { // 2.加载指定的修复的dex文件的Loader DexClassLoader dexLoader = new DexClassLoader( dex.getAbsolutePath(),// 修复好的dex(补丁)所在目录 fopt.getAbsolutePath(),// 存放dex的解压目录(用于jar、zip、apk格式的补丁) null,// 加载dex时需要的库 pathLoader// 父类加载器 ); // 3.开始合并 // 合并的目标是Element[],重新赋值它的值即可 //3.1 准备好pathList的引用 Object dexPathList = getPathList(dexLoader); Object pathPathList = getPathList(pathLoader); //3.2 从pathList中反射出element集合 Object leftDexElements = getDexElements(dexPathList); Object rightDexElements = getDexElements(pathPathList); //3.3 合并两个dex数组 Object dexElements = combineArray(leftDexElements, rightDexElements); // 重写给PathList里面的Element[] dexElements;赋值 Object pathList = getPathList(pathLoader);// 一定要重新获取,不要用pathPathList,会报错 setField(pathList, pathList.getClass(), "dexElements", dexElements); } Toast.makeText(appContext, "修复完成", Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); } } private static void setField(Object obj, Class> cl, String field, Object value) throws NoSuchFieldException, IllegalAccessException { Field declaredField = cl.getDeclaredField(field); declaredField.setAccessible(true); declaredField.set(obj, value); } private static Object getField(Object obj, Class> cl, String field) throws NoSuchFieldException, IllegalAccessException { Field localField = cl.getDeclaredField(field); localField.setAccessible(true); return localField.get(obj); } private static Object getPathList(Object baseDexClassLoader) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { return getField(baseDexClassLoader, Class.forName("dalvik.system.baseDexClassLoader"), "pathList"); } private static Object getDexElements(Object pathList) throws NoSuchFieldException, IllegalAccessException { return getField(pathList, pathList.getClass(), "dexElements"); } private static Object combineArray(Object arrayLhs, Object arrayRhs) { Class> clazz = arrayLhs.getClass().getComponentType(); int i = Array.getLength(arrayLhs);// 得到左数组长度(补丁数组) int j = Array.getLength(arrayRhs);// 得到原dex数组长度 int k = i + j;// 得到总数组长度(补丁数组+原dex数组) Object result = Array.newInstance(clazz, k);// 创建一个类型为clazz,长度为k的新数组 System.arraycopy(arrayLhs, 0, result, 0, i); System.arraycopy(arrayRhs, 0, result, i, j); return result; } }
2、获取原始Apk和获取补丁Apk:
01.获取原始Apk
02.获取补丁Class文件
03.Class文件转换为dex文件
.dx --dex --no-strict --output=C:Users...DesktopdexBugClass.dex C:Users...DesktopdexBugClass.class
04.adb命令push生成的dex文件到手机指定位置
adb push C:Users...DesktoptestBugClass.dex /sdcard/Android/data/com.gsls.thermalremediation/cache/fix
3、项目下载地址: https://download.csdn.net/download/huye930610/37076176
4、存在的问题:
- 01.无法成功修复Release版本的Apk(加上了混淆)
混淆会使修复失败(Apk签名没有影响)
尝试:使用重复混淆的方式------Project的build.gradle中添加依赖
使用重复混淆的方式------proguard文件中设置复用混淆规则
结果:没什么卵用!!!!!
- 02.项目中使用了Dagger2注解,无法获取到Class文件
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)