文章目录
前言
一、DexPathList.java#findClass 类加载函数源码分析
二、DexFile.java#loadClassBinaryName 函数源码分析
前言
上一篇博客 【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | ClassLoader#loadClass 函数分析 | BaseDexClassLoader#findClass 分析 ) 分析到 , 类加载流程中 , 在 BaseDexClassLoader 中的 findClass 方法中 , 主要调用 DexPathList pathList 成员的 findClass 函数查找类 ;
一、DexPathList.java#findClass 类加载函数源码分析
在 DexPathList.java#findClass 方法中 ,
首先 , 遍历 Element[] dexElements 成员 , 每个 Element 元素都封装了一个 DexFile , 即 dex 文件 ;
DexFile dex = element.dexFile;
然后尝试从 dex 文件中加载 Java 类 ;
Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
DexPathList.java#findClass 类加载函数源码 :
/** * 一对条目列表,与{@code ClassLoader}关联。 * 其中一个列表是索引/资源路径&mdash;通常提及 * 作为“类路径”&mdash;列表和其他名称目录 * 包含本机代码库。类路径条目可以是以下任一项: * 一个{@code.jar}或{@code.zip}文件,其中包含一个可选的 * 顶级{@code classes.dex}文件以及任意资源, * 或者是一个普通的{@code.dex}文件(不可能与 * 资源)。 * * <p>此类还包含使用这些列表进行查找的方法 * 课程和资源</p> */ /*package*/ final class DexPathList { /** * 索引/资源(类路径)元素的列表。 * 应该称为pathElements,但Facebook应用程序使用反射 * 要修改“dexElements”(http://b/7726934). */ private final Element[] dexElements; /** * 在所指向的某个dex文件中查找命名类 * 这个例子。这将在最早的列表中找到一个 * 路径元素。如果已找到类但尚未找到 * 已定义,则此方法将在定义中定义它 * 构造此实例时使用的上下文。 * * @param要查找的类的名称 * @param查找类时遇到抑制异常 * @返回已命名的类或{@code null}(如果该类不是空的) * 在任何dex文件中找到 */ public Class findClass(String name, List<Throwable> suppressed) { // 对 DexPathList 中的 Element 数组进行遍历 for (Element element : dexElements) { // 每个 Element 元素都封装了一个 DexFile , 即 dex 文件 DexFile dex = element.dexFile; if (dex != null) { // 尝试从 dex 文件中加载 Java 类 // ★ 核心跳转 Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); if (clazz != null) { return clazz; } } } if (dexElementsSuppressedExceptions != null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } return null; } }
源码路径 : /libcore/dalvik/src/main/java/dalvik/system/DexPathList.java#findClass
二、DexFile.java#loadClassBinaryName 函数源码分析
在 DexFile.java#loadClassBinaryName 函数中 , 调用了 defineClass 方法 , 在 defineClass 方法中 , 调用了 native 函数 defineClassNative ;
在 DexFile.java 的 loadClass 函数中 , 也会调用 DexFile.java#loadClassBinaryName 函数 , 进行类的加载 ;
DexFile.java#loadClassBinaryName 函数源码 :
/** * 操纵DEX文件。这门课在原则上与我们的课相似 * {@link java.util.zip.ZipFile}。它主要由类装入器使用。 * <p> * 注意,我们不直接打开并读取这里的DEX文件。它们是内存映射的 * 由VM只读。 */ public final class DexFile { /** * 加载一个类。成功返回类或{@code null}引用 * 一旦失败。 * <p> * 如果不是从类加载器调用此函数,则很可能不是 * 去做你想做的事。改用{@link Class#forName(String)}。 * <p> * 如果类 * 找不到,因为在每个 * 在我们查看的第一个DEX文件中找不到类的时间。 * * @param name * 类名,看起来应该像“java/lang/String” * * @param装载机 * 尝试加载类的类加载器(在大多数情况下 * 方法的调用方 * * @返回表示类的{@link Class}对象,或{@code null} * 如果无法加载该类 */ public Class loadClass(String name, ClassLoader loader) { String slashName = name.replace('.', '/'); return loadClassBinaryName(slashName, loader, null); } /** * 请参阅{@link#loadClass(String,ClassLoader)}。 * * 这需要一个“二进制”类名来更好地匹配类加载器语义。 * * @隐藏 */ public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) { return defineClass(name, loader, mCookie, suppressed); } private static Class defineClass(String name, ClassLoader loader, int cookie, List<Throwable> suppressed) { Class result = null; try { result = defineClassNative(name, loader, cookie); } catch (NoClassDefFoundError e) { if (suppressed != null) { suppressed.add(e); } } catch (ClassNotFoundException e) { if (suppressed != null) { suppressed.add(e); } } return result; } // ★ 核心跳转 private static native Class defineClassNative(String name, ClassLoader loader, int cookie) throws ClassNotFoundException, NoClassDefFoundError; }
源码路径 : /libcore/dalvik/src/main/java/dalvik/system/DexFile.java#loadClassBinaryName