【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexPathList 构造函数分析 | makeDexElements 函数分析 )

2023-03-30 09:22:09 浏览数 (1)

文章目录

  • 前言
  • 一、DexPathList 构造函数分析
  • 二、DexPathList.makeDexElements 函数分析
  • 三、Element 类分析

前言


上一篇博客 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | 类加载器构造函数分析 | DexPathList 引入 ) 中 , 分析了 DexClassLoader 构造函数的调用流程 , 在构造函数中执行的核心操作就是 在 BaseDexClassLoader 的构造函数中 初始化了 DexPathList 实例对象 ;

本篇博客中重点分析 DexPathList ;

一、DexPathList 构造函数分析


在 DexPathList 构造函数中 , 主要是调用了 makeDexElements() 方法 , 该方法返回 Element[] 数组元素 , 赋值给 private final Element[] dexElements 成员 ;

代码语言:javascript复制
/*package*/ final class DexPathList {
    /**
     * List of dex/resource (class path) elements.
     * Should be called pathElements, but the Facebook app uses reflection
     * to modify 'dexElements' (http://b/7726934).
     */
    private final Element[] dexElements;
    
    /**
     * Constructs an instance.
     *
     * @param definingContext the context in which any as-yet unresolved
     * classes should be defined
     * @param dexPath list of dex/resource path elements, separated by
     * {@code File.pathSeparator}
     * @param libraryPath list of native library directory path elements,
     * separated by {@code File.pathSeparator}
     * @param optimizedDirectory directory where optimized {@code .dex} files
     * should be found and written to, or {@code null} to use the default
     * system directory for same
     */
    public DexPathList(ClassLoader definingContext, String dexPath,
            String libraryPath, File optimizedDirectory) {
        // 下面的代码 主要是对 参数合法性判断 
        if (definingContext == null) {
            throw new NullPointerException("definingContext == null");
        }

        if (dexPath == null) {
            throw new NullPointerException("dexPath == null");
        }

        if (optimizedDirectory != null) {
            if (!optimizedDirectory.exists())  {
                throw new IllegalArgumentException(
                        "optimizedDirectory doesn't exist: "
                          optimizedDirectory);
            }

            if (!(optimizedDirectory.canRead()
                            && optimizedDirectory.canWrite())) {
                throw new IllegalArgumentException(
                        "optimizedDirectory not readable/writable: "
                          optimizedDirectory);
            }
        }
        // 上述代码是对参数合法性判断 

        this.definingContext = definingContext;
        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();

		// 核心逻辑 
		// 调用 makeDexElements 方法 , 传入 
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                           suppressedExceptions);
        if (suppressedExceptions.size() > 0) {
            this.dexElementsSuppressedExceptions =
                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
        } else {
            dexElementsSuppressedExceptions = null;
        }
        this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
    }
}

源码路径 : /libcore/dalvik/src/main/java/dalvik/system/DexPathList.java

二、DexPathList.makeDexElements 函数分析


DexPathList.makeDexElements 函数中 , 主要返回了一个 Element[] 数组 ; ElementDexPathList 的内部类 ;

代码语言:javascript复制
/*package*/ final class DexPathList {
    /**
     * Makes an array of dex/resource path elements, one per element of
     * the given array.
     */
    private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
                                             ArrayList<IOException> suppressedExceptions) {
        // 创建要返回的 Element 数组对应的集合
        ArrayList<Element> elements = new ArrayList<Element>();
        /*
         * 打开并加载 dex 文件 
         * up front.
         */
        for (File file : files) {
            File zip = null;
            DexFile dex = null;
            String name = file.getName();

            if (name.endsWith(DEX_SUFFIX)) { // .dex 后缀
                // dex 后缀是 .dex , 进入该分支 
                // Raw dex file (not inside a zip/jar).
                try {
                    // 从文件中加载 dex 
                    dex = loadDexFile(file, optimizedDirectory);
                } catch (IOException ex) {
                    System.logE("Unable to load dex file: "   file, ex);
                }
            } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
                    || name.endsWith(ZIP_SUFFIX)) {
                // .apk , .jar , .zip 后缀 , 命中该分支  
                zip = file;

                try {
                    dex = loadDexFile(file, optimizedDirectory);
                } catch (IOException suppressed) {
                    /*
                     * IOException might get thrown "legitimately" by the DexFile constructor if the
                     * zip file turns out to be resource-only (that is, no classes.dex file in it).
                     * Let dex == null and hang on to the exception to add to the tea-leaves for
                     * when findClass returns null.
                     */
                    suppressedExceptions.add(suppressed);
                }
            } else if (file.isDirectory()) {
                // We support directories for looking up resources.
                // This is only useful for running libcore tests.
                elements.add(new Element(file, true, null, null));
            } else {
                System.logW("Unknown file type for: "   file);
            }

            if ((zip != null) || (dex != null)) {
                // 调用完毕后 , 如果获取的 DexFile 不为空 , 创建 Element 对象 , 并加入到 ArrayList 集合中
                elements.add(new Element(file, false, zip, dex));
            }
        }

        return elements.toArray(new Element[elements.size()]);
    }
}

源码路径 : /libcore/dalvik/src/main/java/dalvik/system/DexPathList.java

三、Element 类分析


Element 类是 DexPathList 的内部类 , 其第一个成员变量就是 private final File file , 这个就是 dex 文件类 ;

代码语言:javascript复制
/*package*/ final class DexPathList {
    /**
     * Element of the dex/resource file path
     */
    /*package*/ static class Element {
        private final File file;
        private final boolean isDirectory;
        private final File zip;
        private final DexFile dexFile;

        private ZipFile zipFile;
        private boolean initialized;

        public Element(File file, boolean isDirectory, File zip, DexFile dexFile) {
            this.file = file;
            this.isDirectory = isDirectory;
            this.zip = zip;
            this.dexFile = dexFile;
        }
    }
}

0 人点赞