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

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 逆向整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexPathList 构造函数分析 | makeDexElements 函数分析 )相关的知识,希望对你有一定的参考价值。

前言


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

本篇博客中重点分析 DexPathList ;





一、DexPathList 构造函数分析



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

/*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 的内部类 ;

/*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 文件类 ;

/*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;
        
    

以上是关于Android 逆向整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexPathList 构造函数分析 | makeDexElements 函数分析 )的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函数分析 )

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | dvmDexFileOpenPartial | dexFileParse | 脱壳点 | 获取 dex 文件在内存中的首地址 )(代码片

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

Android 逆向整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexFile loadDexFile 函数 | 构造函数 | openDexFile 函数 )(代码片

Android 逆向整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexPathList 中根据 File 加载 DexFile | loadDexFile 分析 )(代

Android 逆向整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | RawDexFile.cpp 分析 | dvmRawDexFileOpen函数读取 DEX 文件 )(代