首页
社区
课程
招聘
[原创] 安卓9.0dex文件加载分析
发表于: 2025-3-30 11:33 1301

[原创] 安卓9.0dex文件加载分析

2025-3-30 11:33
1301

在分析dex文件加载过程之前,先看看dex如何被系统识别与调用。
1;apk包在安装后dex会存放在根目录,data/data/目录下。
2;加载前需要获取dex文件路径,然后优化。
3;优化后dex文件字节码加载器会初始化生成对象。
4;生成对象后即可调用里面的方法了。
5;反射的原理无非就是获取dex文件里面的类,
然后用类初始化成对象,利用对象调用里面的方法。

以上用代码表示如下,

1
2
3
4
5
6
7
8
9
10
11
String dexPath = "/data/data/your.package.name/files/plugin.dex"// DEX文件路径
File optimizedDir = getDir("outdex", Context.MODE_PRIVATE); // 生成一个优化
DexClassLoader dexClassLoader;
String AbsolutePath=optimizedDir.getAbsolutePath();
dexClassLoader = new DexClassLoader(dexPath,AbsolutePath , null,getClassLoader());
 
// 使用反射机制获取类和方法
String className = "com.example.PluginClass"; // 目标类的全名
Class<?> loadedClass = dexClassLoader.loadClass(className);
Method method = loadedClass.getMethod("yourMethodName"); // 方法名
method.invoke(loadedClass.newInstance()); // 调用方法

这里我们关键分析DexClassLoader类,这个类加载了dex文件。
源代码来自;http://androidxref.com/

进入网站后搜索DexClassLoader,右侧select all全选,
搜索后点击点击DexClassLoader.java,这个文件是关键点
在这里插入图片描述
如下是关键代码
可以看到DexClassLoader 继承了BaseDexClassLoader ,
也就是说有BaseDexClassLoader 里面的所有代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
35public class DexClassLoader extends BaseDexClassLoader {
36    /**
37     * Creates a {@code DexClassLoader} that finds interpreted and native
38     * code.  Interpreted classes are found in a set of DEX files contained
39     * in Jar or APK files.
40     *
41     * <p>The path lists are separated using the character specified by the
42     * {@code path.separator} system property, which defaults to {@code :}.
43     *
44     * @param dexPath the list of jar/apk files containing classes and
45     *     resources, delimited by {@code File.pathSeparator}, which
46     *     defaults to {@code ":"} on Android
47     * @param optimizedDirectory this parameter is deprecated and has no effect since API level 26.
48     * @param librarySearchPath the list of directories containing native
49     *     libraries, delimited by {@code File.pathSeparator}; may be
50     *     {@code null}
51     * @param parent the parent class loader
52     */
53    public DexClassLoader(String dexPath, String optimizedDirectory,
54            String librarySearchPath, ClassLoader parent) {
55        super(dexPath, null, librarySearchPath, parent);
56    }
57}
58

这里看一下关键方法,第一个参数为dexPath 也就是dex文件路径,
这里只看dex文件路径就好了,这个是加载dex文件的关键,

1
2
3
4
53    public DexClassLoader(String dexPath, String optimizedDirectory,
54            String librarySearchPath, ClassLoader parent) {
55        super(dexPath, null, librarySearchPath, parent);
56    }

DexClassLoader方法调用了super方法,也就是调用了BaseDexClassLoader 里面的方法,

1
super(dexPath, null, librarySearchPath, parent);

这里继续跟踪,搜索DexClassLoader
在这里插入图片描述
进去后看见关键方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
63    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
64            String librarySearchPath, ClassLoader parent) {
65        this(dexPath, optimizedDirectory, librarySearchPath, parent, false);
66    }
67
68    /**
69     * @hide
70     */
71    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
72            String librarySearchPath, ClassLoader parent, boolean isTrusted) {
73        super(parent);
74        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
75
76        if (reporter != null) {
77            reportClassLoaderChain();
78        }
79    }

调用BaseDexClassLoader时由于传入了四个参数,

1
super(dexPath, null, librarySearchPath, parent);

我们只跟踪如下方法,
如下方法调用了this,多传入了一个参数false,
this的意思是调用相同的方法,但是参数不一样,

1
2
3
4
63    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
64            String librarySearchPath, ClassLoader parent) {
65        this(dexPath, optimizedDirectory, librarySearchPath, parent, false);
66    }

我们继续跟踪如下方法

1
2
3
4
5
6
7
8
9
71    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
72            String librarySearchPath, ClassLoader parent, boolean isTrusted) {
73        super(parent);
74        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
75
76        if (reporter != null) {
77            reportClassLoaderChain();
78        }
79    }

上面的方法初始化了DexPathList对象,
继续跟踪,进入DexPathList方法里面,这个方法传入了上面说的5个参数。
进入DexPathList.java文件后,跟踪如下方法,这个方法传入了五个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
135    DexPathList(ClassLoader definingContext, String dexPath,
136            String librarySearchPath, File optimizedDirectory, boolean isTrusted) {
137        if (definingContext == null) {
138            throw new NullPointerException("definingContext == null");
139        }
140
141        if (dexPath == null) {
142            throw new NullPointerException("dexPath == null");
143        }
144
145        if (optimizedDirectory != null) {
146            if (!optimizedDirectory.exists())  {
147                throw new IllegalArgumentException(
148                        "optimizedDirectory doesn't exist: "
149                        + optimizedDirectory);
150            }
151
152            if (!(optimizedDirectory.canRead()
153                            && optimizedDirectory.canWrite())) {
154                throw new IllegalArgumentException(
155                        "optimizedDirectory not readable/writable: "
156                        + optimizedDirectory);
157            }
158        }
159
160        this.definingContext = definingContext;
161
162        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
163        // save dexPath for BaseDexClassLoader
164        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
165                                           suppressedExceptions, definingContext, isTrusted);
166
167        // Native libraries may exist in both the system and
168        // application library paths, and we use this search order:
169        //
170        //   1. This class loader's library path for application libraries (librarySearchPath):
171        //   1.1. Native library directories
172        //   1.2. Path to libraries in apk-files
173        //   2. The VM's library path from the system property for system libraries
174        //      also known as java.library.path
175        //
176        // This order was reversed prior to Gingerbread; see http://b/2933456.
177        this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
178        this.systemNativeLibraryDirectories =
179                splitPaths(System.getProperty("java.library.path"), true);
180        List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
181        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
182
183        this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);
184
185        if (suppressedExceptions.size() > 0) {
186            this.dexElementsSuppressedExceptions =
187                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
188        } else {
189            dexElementsSuppressedExceptions = null;
190        }
191    }
192

以上方法具体是在检测dex文件的相关信息,例如是否存在,
这里继续跟踪dexPath参数,

如下代码传入了dexPath参数,

1
2
3
163        // save dexPath for BaseDexClassLoader
164        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
165                                           suppressedExceptions, definingContext, isTrusted);

继续跟踪splitDexPath方法
可以看到此方法调用了splitPaths方法,但传入了两个参数,
这里继续寻找两个参数的此方法,

1
2
3
4
5
6
7
8
256    /**
257     * Splits the given dex path string into elements using the path
258     * separator, pruning out any elements that do not refer to existing
259     * and readable files.
260     */
261    private static List<File> splitDexPath(String path) {
262        return splitPaths(path, false);
263    }

如下方法为两个参数的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
265    /**
266     * Splits the given path strings into file elements using the path
267     * separator, combining the results and filtering out elements
268     * that don't exist, aren't readable, or aren't either a regular
269     * file or a directory (as specified). Either string may be empty
270     * or {@code null}, in which case it is ignored. If both strings
271     * are empty or {@code null}, or all elements get pruned out, then
272     * this returns a zero-element list.
273     */
274    private static List<File> splitPaths(String searchPath, boolean directoriesOnly) {
275        List<File> result = new ArrayList<>();
276
277        if (searchPath != null) {
278            for (String path : searchPath.split(File.pathSeparator)) {
279                if (directoriesOnly) {
280                    try {
281                        StructStat sb = Libcore.os.stat(path);
282                        if (!S_ISDIR(sb.st_mode)) {
283                            continue;
284                        }
285                    } catch (ErrnoException ignored) {
286                        continue;
287                    }
288                }
289                result.add(new File(path));
290            }
291        }
292
293        return result;
294    }

此方法的返回值为List<File>,返回值里面为文件的集合,因此此方法
初始化了dex文件,把dex文件加载进了内存

由于返回值为dex文件的内存集合
这里只看如下代码

1
result.add(new File(path));

这里的add方法把dex的数据放进了result集合里面,

继续看如下方法,result来自以下代码,这里初始化了集合,
File的意思为文件的意思,这个集合用来存放文件集合,

1
List<File> result = new ArrayList<>();

到这里这个方法makeDexElements就结束了,如下代码返回了result,
因此我们需要继续跟踪result返回值,

1
293        return result;

回到前面的代码,这里我们看返回值dexElements

1
2
3
163        // save dexPath for BaseDexClassLoader
164        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
165                                           suppressedExceptions, definingContext, isTrusted);

这里我们继续跟踪makeDexElements方法,这个方法传入了result返回值。
这个方法是五个参数的,我们找五个参数的这个方法。

在这里插入图片描述
相关代码如下,这里我们跟踪files的参数,这个参数为dex的内存文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
325    private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
326            List<IOException> suppressedExceptions, ClassLoader loader, boolean isTrusted) {
327      Element[] elements = new Element[files.size()];
328      int elementsPos = 0;
329      /*
330       * Open all files and load the (direct or contained) dex files up front.
331       */
332      for (File file : files) {
333          if (file.isDirectory()) {
334              // We support directories for looking up resources. Looking up resources in
335              // directories is useful for running libcore tests.
336              elements[elementsPos++] = new Element(file);
337          } else if (file.isFile()) {
338              String name = file.getName();
339
340              DexFile dex = null;
341              if (name.endsWith(DEX_SUFFIX)) {
342                  // Raw dex file (not inside a zip/jar).
343                  try {
344                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
345                      if (dex != null) {
346                          elements[elementsPos++] = new Element(dex, null);
347                      }
348                  } catch (IOException suppressed) {
349                      System.logE("Unable to load dex file: " + file, suppressed);
350                      suppressedExceptions.add(suppressed);
351                  }
352              } else {
353                  try {
354                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
355                  } catch (IOException suppressed) {
356                      /*
357                       * IOException might get thrown "legitimately" by the DexFile constructor if
358                       * the zip file turns out to be resource-only (that is, no classes.dex file
359                       * in it).
360                       * Let dex == null and hang on to the exception to add to the tea-leaves for
361                       * when findClass returns null.
362                       */
363                      suppressedExceptions.add(suppressed);
364                  }
365
366                  if (dex == null) {
367                      elements[elementsPos++] = new Element(file);
368                  } else {
369                      elements[elementsPos++] = new Element(dex, file);
370                  }
371              }
372              if (dex != null && isTrusted) {
373                dex.setTrusted();
374              }
375          } else {
376              System.logW("ClassLoader referenced unknown path: " + file);
377          }
378      }
379      if (elementsPos != elements.length) {
380          elements = Arrays.copyOf(elements, elementsPos);
381      }
382      return elements;
383    }

这里看关键代码块,name.endsWith(DEX_SUFFIX)这个代码块在判断是否为dex后缀,
我们跟踪第一个传入了file参数的方法,因为这肯定是dex文件后缀的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
341              if (name.endsWith(DEX_SUFFIX)) {
342                  // Raw dex file (not inside a zip/jar).
343                  try {
344                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
345                      if (dex != null) {
346                          elements[elementsPos++] = new Element(dex, null);
347                      }
348                  } catch (IOException suppressed) {
349                      System.logE("Unable to load dex file: " + file, suppressed);
350                      suppressedExceptions.add(suppressed);
351                  }
352              } else {
353                  try {
354                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
355                  } catch (IOException suppressed) {
356                      /*
357                       * IOException might get thrown "legitimately" by the DexFile constructor if
358                       * the zip file turns out to be resource-only (that is, no classes.dex file
359                       * in it).
360                       * Let dex == null and hang on to the exception to add to the tea-leaves for
361                       * when findClass returns null.
362                       */
363                      suppressedExceptions.add(suppressed);
364                  }

跟踪如下代码,我们进入loadDexFile代码,

1
344                      dex = loadDexFile(file, optimizedDirectory, loader, elements);

下面为loadDexFile方法,这里跟踪loadDex方法,因为返回值为DexFile,意思是dex文件,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
385    /**
386     * Constructs a {@code DexFile} instance, as appropriate depending on whether
387     * {@code optimizedDirectory} is {@code null}. An application image file may be associated with
388     * the {@code loader} if it is not null.
389     */
390    private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,
391                                       Element[] elements)
392            throws IOException {
393        if (optimizedDirectory == null) {
394            return new DexFile(file, loader, elements);
395        } else {
396            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
397            return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
398        }
399    }

这里需要看前面的代码
第二个参数为null

1
super(dexPath, null, librarySearchPath, parent);

因此跟踪下面的代码块

1
return new DexFile(file, loader, elements);

同样,这个方法传入了三个参数,跟踪三个参数的方法就行
下面的方法调用了this,得找相同的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
63    /*
64     * Private version with class loader argument.
65     *
66     * @param file
67     *            the File object referencing the actual DEX file
68     * @param loader
69     *            the class loader object creating the DEX file object
70     * @param elements
71     *            the temporary dex path list elements from DexPathList.makeElements
72     */
73    DexFile(File file, ClassLoader loader, DexPathList.Element[] elements)
74            throws IOException {
75        this(file.getPath(), loader, elements);
76    }

继续看下面的方法,file.getPath()的意思是获取路径,也就是dex文件名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
90    /*
91     * Private version with class loader argument.
92     *
93     * @param fileName
94     *            the filename of the DEX file
95     * @param loader
96     *            the class loader creating the DEX file object
97     * @param elements
98     *            the temporary dex path list elements from DexPathList.makeElements
99     */
100    DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
101        mCookie = openDexFile(fileName, null, 0, loader, elements);
102        mInternalCookie = mCookie;
103        mFileName = fileName;
104        //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
105    }

这里跟踪下面的方法,看fileName参数就行。

1
openDexFile(fileName, null, 0, loader, elements);

下面的代码开始进入c函数层了,这里是关键点,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
347    /*
348     * Open a DEX file.  The value returned is a magic VM cookie.  On
349     * failure, an IOException is thrown.
350     */
351    private static Object openDexFile(String sourceName, String outputName, int flags,
352            ClassLoader loader, DexPathList.Element[] elements) throws IOException {
353        // Use absolute paths to enable the use of relative paths when testing on host.
354        return openDexFileNative(new File(sourceName).getAbsolutePath(),
355                                 (outputName == null)
356                                     ? null
357                                     : new File(outputName).getAbsolutePath(),
358                                 flags,
359                                 loader,
360                                 elements);
361    }

这里看下面的函数

1
2
3
4
5
6
7
8
353        // Use absolute paths to enable the use of relative paths when testing on host.
354        return openDexFileNative(new File(sourceName).getAbsolutePath(),
355                                 (outputName == null)
356                                     ? null
357                                     : new File(outputName).getAbsolutePath(),
358                                 flags,
359                                 loader,
360                                 elements);
1
2
3
4
5
6
398    /*
399     * Open a DEX file.  The value returned is a magic VM cookie.  On
400     * failure, an IOException is thrown.
401     */
402    private static native Object openDexFileNative(String sourceName, String outputName, int flags,
403            ClassLoader loader, DexPathList.Element[] elements);

这里没有实现,因此需要找这个函数的代码块才行,
这里搜索openDexFileNative,跟踪/art/runtime/native/dalvik_system_DexFile.cc文件

在这里插入图片描述
如下代码块注册了openDexFileNative函数,

1
2
3
4
5
6
7
854  NATIVE_METHOD(DexFile, openDexFileNative,
855                "(Ljava/lang/String;"
856                "Ljava/lang/String;"
857                "I"
858                "Ljava/lang/ClassLoader;"
859                "[Ldalvik/system/DexPathList$Element;"
860                ")Ljava/lang/Object;"),

这里跟踪这个函数DexFile_openDexFileNative

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
266// TODO(calin): clean up the unused parameters (here and in libcore).
267static jobject DexFile_openDexFileNative(JNIEnv* env,
268                                         jclass,
269                                         jstring javaSourceName,
270                                         jstring javaOutputName ATTRIBUTE_UNUSED,
271                                         jint flags ATTRIBUTE_UNUSED,
272                                         jobject class_loader,
273                                         jobjectArray dex_elements) {
274  ScopedUtfChars sourceName(env, javaSourceName);
275  if (sourceName.c_str() == nullptr) {
276    return 0;
277  }
278
279  Runtime* const runtime = Runtime::Current();
280  ClassLinker* linker = runtime->GetClassLinker();
281  std::vector<std::unique_ptr<const DexFile>> dex_files;
282  std::vector<std::string> error_msgs;
283  const OatFile* oat_file = nullptr;
284
285  dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
286                                                               class_loader,
287                                                               dex_elements,
288                                                               /*out*/ &oat_file,
289                                                               /*out*/ &error_msgs);
290
291  if (!dex_files.empty()) {
292    jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
293    if (array == nullptr) {
294      ScopedObjectAccess soa(env);
295      for (auto& dex_file : dex_files) {
296        if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
297          dex_file.release();
298        }
299      }
300    }
301    return array;
302  } else {
303    ScopedObjectAccess soa(env);
304    CHECK(!error_msgs.empty());
305    // The most important message is at the end. So set up nesting by going forward, which will
306    // wrap the existing exception as a cause for the following one.
307    auto it = error_msgs.begin();
308    auto itEnd = error_msgs.end();
309    for ( ; it != itEnd; ++it) {
310      ThrowWrappedIOException("%s", it->c_str());
311    }
312
313    return nullptr;
314  }
315}

这里跟踪第3个参数,第一个参数env是默认的,第二个参数jclass也是默认的,
第三个参数为java方法传入的string字符串,这里跟踪参数javaSourceName

初始化对象sourceName

1
274  ScopedUtfChars sourceName(env, javaSourceName);

判断sourceName是否为空

1
2
3
275  if (sourceName.c_str() == nullptr) {
276    return 0;
277  }

定义一个容器存放dex文件

1
281  std::vector<std::unique_ptr<const DexFile>> dex_files;

oat为优化文件,OatFile*指针用于存放dex优化后的文件

1
283  const OatFile* oat_file = nullptr;

这里继续看关键代码,跟踪OpenDexFilesFromOat函数,
此函数传入了五个参数,这里跟踪五个参数的函数,

1
2
3
4
5
285  dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
286                                                               class_loader,
287                                                               dex_elements,
288                                                               /*out*/ &oat_file,
289                                                               /*out*/ &error_msgs);

如下为关键代码块,这里跟踪dex_location参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
394std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
395    const char* dex_location,
396    jobject class_loader,
397    jobjectArray dex_elements,
398    const OatFile** out_oat_file,
399    std::vector<std::string>* error_msgs) {
400  ScopedTrace trace(__FUNCTION__);
401  CHECK(dex_location != nullptr);
402  CHECK(error_msgs != nullptr);
403
404  // Verify we aren't holding the mutator lock, which could starve GC if we
405  // have to generate or relocate an oat file.
406  Thread* const self = Thread::Current();
407  Locks::mutator_lock_->AssertNotHeld(self);
408  Runtime* const runtime = Runtime::Current();
409
410  std::unique_ptr<ClassLoaderContext> context;
411  // If the class_loader is null there's not much we can do. This happens if a dex files is loaded
412  // directly with DexFile APIs instead of using class loaders.
413  if (class_loader == nullptr) {
414    LOG(WARNING) << "Opening an oat file without a class loader. "
415                 << "Are you using the deprecated DexFile APIs?";
416    context = nullptr;
417  } else {
418    context = ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements);
419  }
420
421  OatFileAssistant oat_file_assistant(dex_location,
422                                      kRuntimeISA,
423                                      !runtime->IsAotCompiler(),
424                                      only_use_system_oat_files_);
425
426  // Lock the target oat location to avoid races generating and loading the
427  // oat file.
428  std::string error_msg;
429  if (!oat_file_assistant.Lock(/*out*/&error_msg)) {
430    // Don't worry too much if this fails. If it does fail, it's unlikely we
431    // can generate an oat file anyway.
432    VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
433  }
434
435  const OatFile* source_oat_file = nullptr;
436
437  if (!oat_file_assistant.IsUpToDate()) {
438    // Update the oat file on disk if we can, based on the --compiler-filter
439    // option derived from the current runtime options.
440    // This may fail, but that's okay. Best effort is all that matters here.
441    // TODO(calin): b/64530081 b/66984396. Pass a null context to verify and compile
442    // secondary dex files in isolation (and avoid to extract/verify the main apk
443    // if it's in the class path). Note this trades correctness for performance
444    // since the resulting slow down is unacceptable in some cases until b/64530081
445    // is fixed.
446    // We still pass the class loader context when the classpath string of the runtime
447    // is not empty, which is the situation when ART is invoked standalone.
448    ClassLoaderContext* actual_context = Runtime::Current()->GetClassPathString().empty()
449        ? nullptr
450        : context.get();
451    switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/ false,
452                                            actual_context,
453                                            /*out*/ &error_msg)) {
454      case OatFileAssistant::kUpdateFailed:
455        LOG(WARNING) << error_msg;
456        break;
457
458      case OatFileAssistant::kUpdateNotAttempted:
459        // Avoid spamming the logs if we decided not to attempt making the oat
460        // file up to date.
461        VLOG(oat) << error_msg;
462        break;
463
464      case OatFileAssistant::kUpdateSucceeded:
465        // Nothing to do.
466        break;
467    }
468  }

这里跟踪如下函数oat_file_assistant,此函数为四个参数,

1
2
3
4
5
6
421  OatFileAssistant oat_file_assistant(dex_location,
422                                      kRuntimeISA,
423                                      !runtime->IsAotCompiler(),
424                                      only_use_system_oat_files_);
 
这里搜索OatFileAssistant ,找到cc文件

在这里插入图片描述
看到如下函数,这里跟踪父函数,父函数有7个参数

1
2
3
4
5
6
7
8
9
10
11
75OatFileAssistant::OatFileAssistant(const char* dex_location,
76                                   const InstructionSet isa,
77                                   bool load_executable,
78                                   bool only_load_system_executable)
79    : OatFileAssistant(dex_location,
80                       isa,
81                       load_executable,
82                       only_load_system_executable,
83                       -1 /* vdex_fd */,
84                       -1 /* oat_fd */,
85                       -1 /* zip_fd */) {}

父函数如下,还是跟踪第一个参数dex_location,这是一个char *存放字符串的指针,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
88OatFileAssistant::OatFileAssistant(const char* dex_location,
89                                   const InstructionSet isa,
90                                   bool load_executable,
91                                   bool only_load_system_executable,
92                                   int vdex_fd,
93                                   int oat_fd,
94                                   int zip_fd)
95    : isa_(isa),
96      load_executable_(load_executable),
97      only_load_system_executable_(only_load_system_executable),
98      odex_(this, /*is_oat_location*/ false),
99      oat_(this, /*is_oat_location*/ true),
100      zip_fd_(zip_fd) {
101  CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
102
103  if (zip_fd < 0) {
104    CHECK_LE(oat_fd, 0) << "zip_fd must be provided with valid oat_fd. zip_fd=" << zip_fd
105      << " oat_fd=" << oat_fd;
106    CHECK_LE(vdex_fd, 0) << "zip_fd must be provided with valid vdex_fd. zip_fd=" << zip_fd
107      << " vdex_fd=" << vdex_fd;;
108  }
109
110  dex_location_.assign(dex_location);
111
112  if (load_executable_ && isa != kRuntimeISA) {
113    LOG(WARNING) << "OatFileAssistant: Load executable specified, "
114      << "but isa is not kRuntimeISA. Will not attempt to load executable.";
115    load_executable_ = false;
116  }
117
118  // Get the odex filename.
119  std::string error_msg;
120  std::string odex_file_name;
121  if (DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name, &error_msg)) {
122    odex_.Reset(odex_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd);
123  } else {
124    LOG(WARNING) << "Failed to determine odex file name: " << error_msg;
125  }
126
127  if (!UseFdToReadFiles()) {
128    // Get the oat filename.
129    std::string oat_file_name;
130    if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) {
131      oat_.Reset(oat_file_name, false /* use_fd */);
132    } else {
133      LOG(WARNING) << "Failed to determine oat file name for dex location "
134                   << dex_location_ << ": " << error_msg;
135    }
136  }
137
138  // Check if the dex directory is writable.
139  // This will be needed in most uses of OatFileAssistant and so it's OK to
140  // compute it eagerly. (the only use which will not make use of it is
141  // OatFileAssistant::GetStatusDump())
142  size_t pos = dex_location_.rfind('/');
143  if (pos == std::string::npos) {
144    LOG(WARNING) << "Failed to determine dex file parent directory: " << dex_location_;
145  } else if (!UseFdToReadFiles()) {
146    // We cannot test for parent access when using file descriptors. That's ok
147    // because in this case we will always pick the odex file anyway.
148    std::string parent = dex_location_.substr(0, pos);
149    if (access(parent.c_str(), W_OK) == 0) {
150      dex_parent_writable_ = true;
151    } else {
152      VLOG(oat) << "Dex parent of " << dex_location_ << " is not writable: " << strerror(errno);
153    }
154  }
155}

如下函数拷贝了字符串给dex_location_,继续跟踪dex_location_

1
110  dex_location_.assign(dex_location);

如下函数传入了dex_location_参数,这个函数是将dex优化成oat文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
862bool OatFileAssistant::DexLocationToOatFilename(const std::string& location,
863                                                InstructionSet isa,
864                                                std::string* oat_filename,
865                                                std::string* error_msg) {
866  CHECK(oat_filename != nullptr);
867  CHECK(error_msg != nullptr);
868
869  std::string cache_dir = GetDalvikCache(GetInstructionSetString(isa));
870  if (cache_dir.empty()) {
871    *error_msg = "Dalvik cache directory does not exist";
872    return false;
873  }
874
875  // TODO: The oat file assistant should be the definitive place for
876  // determining the oat file name from the dex location, not
877  // GetDalvikCacheFilename.
878  return GetDalvikCacheFilename(location.c_str(), cache_dir.c_str(), oat_filename, error_msg);
879}

这里跟踪GetDalvikCacheFilename函数,传入四个参数

1
GetDalvikCacheFilename(location.c_str(), cache_dir.c_str(), oat_filename, error_msg);

如下代码判断dex,art,oat文件,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
269bool GetDalvikCacheFilename(const char* location, const char* cache_location,
270                            std::string* filename, std::string* error_msg) {
271  if (location[0] != '/') {
272    *error_msg = StringPrintf("Expected path in location to be absolute: %s", location);
273    return false;
274  }
275  std::string cache_file(&location[1]);  // skip leading slash
276  if (!android::base::EndsWith(location, ".dex") &&
277      !android::base::EndsWith(location, ".art") &&
278      !android::base::EndsWith(location, ".oat")) {
279    cache_file += "/";
280    cache_file += DexFileLoader::kClassesDex;
281  }
282  std::replace(cache_file.begin(), cache_file.end(), '/', '@');
283  *filename = StringPrintf("%s/%s", cache_location, cache_file.c_str());
284  return true;
285}

回到前面,继续跟踪如下函数

1
2
3
4
5
6
7
8
9
394std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
395    const char* dex_location,
396    jobject class_loader,
397    jobjectArray dex_elements,
398    const OatFile** out_oat_file,
399    std::vector<std::string>* error_msgs) {
400  ScopedTrace trace(__FUNCTION__);
401  CHECK(dex_location != nullptr);
402  CHECK(error_msgs != nullptr);

如下是此OpenDexFilesFromOat的后面的代码,这里跟踪oat_file_assistant

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
470  // Get the oat file on disk.
471  std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
472  VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()="
473            << reinterpret_cast<uintptr_t>(oat_file.get())
474            << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")";
475
476  if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
477    // Prevent oat files from being loaded if no class_loader or dex_elements are provided.
478    // This can happen when the deprecated DexFile.<init>(String) is called directly, and it
479    // could load oat files without checking the classpath, which would be incorrect.
480    // Take the file only if it has no collisions, or we must take it because of preopting.
481    bool accept_oat_file =
482        !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg);
483    if (!accept_oat_file) {
484      // Failed the collision check. Print warning.
485      if (Runtime::Current()->IsDexFileFallbackEnabled()) {
486        if (!oat_file_assistant.HasOriginalDexFiles()) {
487          // We need to fallback but don't have original dex files. We have to
488          // fallback to opening the existing oat file. This is potentially
489          // unsafe so we warn about it.
490          accept_oat_file = true;
491
492          LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. "
493                       << "Allow oat file use. This is potentially dangerous.";
494        } else {
495          // We have to fallback and found original dex files - extract them from an APK.
496          // Also warn about this operation because it's potentially wasteful.
497          LOG(WARNING) << "Found duplicate classes, falling back to extracting from APK : "
498                       << dex_location;
499          LOG(WARNING) << "NOTE: This wastes RAM and hurts startup performance.";
500        }
501      } else {
502        // TODO: We should remove this. The fact that we're here implies -Xno-dex-file-fallback
503        // was set, which means that we should never fallback. If we don't have original dex
504        // files, we should just fail resolution as the flag intended.
505        if (!oat_file_assistant.HasOriginalDexFiles()) {
506          accept_oat_file = true;
507        }
508
509        LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to "
510                        " load classes for " << dex_location;
511      }
512
513      LOG(WARNING) << error_msg;
514    }
515
516    if (accept_oat_file) {
517      VLOG(class_linker) << "Registering " << oat_file->GetLocation();
518      source_oat_file = RegisterOatFile(std::move(oat_file));
519      *out_oat_file = source_oat_file;
520    }
521  }
522
523  std::vector<std::unique_ptr<const DexFile>> dex_files;
524
525  // Load the dex files from the oat file.
526  if (source_oat_file != nullptr) {
527    bool added_image_space = false;
528    if (source_oat_file->IsExecutable()) {
529      // We need to throw away the image space if we are debuggable but the oat-file source of the
530      // image is not otherwise we might get classes with inlined methods or other such things.
531      std::unique_ptr<gc::space::ImageSpace> image_space;
532      if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) {
533        image_space = oat_file_assistant.OpenImageSpace(source_oat_file);
534      } else {
535        image_space = nullptr;
536      }
537      if (image_space != nullptr) {
538        ScopedObjectAccess soa(self);
539        StackHandleScope<1> hs(self);
540        Handle<mirror::ClassLoader> h_loader(
541            hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
542        // Can not load app image without class loader.
543        if (h_loader != nullptr) {
544          std::string temp_error_msg;
545          // Add image space has a race condition since other threads could be reading from the
546          // spaces array.
547          {
548            ScopedThreadSuspension sts(self, kSuspended);
549            gc::ScopedGCCriticalSection gcs(self,
550                                            gc::kGcCauseAddRemoveAppImageSpace,
551                                            gc::kCollectorTypeAddRemoveAppImageSpace);
552            ScopedSuspendAll ssa("Add image space");
553            runtime->GetHeap()->AddSpace(image_space.get());
554          }
555          {
556            ScopedTrace trace2(StringPrintf("Adding image space for location %s", dex_location));
557            added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(),
558                                                                         h_loader,
559                                                                         dex_elements,
560                                                                         dex_location,
561                                                                         /*out*/&dex_files,
562                                                                         /*out*/&temp_error_msg);
563          }
564          if (added_image_space) {
565            // Successfully added image space to heap, release the map so that it does not get
566            // freed.
567            image_space.release();
568
569            // Register for tracking.
570            for (const auto& dex_file : dex_files) {
571              dex::tracking::RegisterDexFile(dex_file.get());
572            }
573          } else {
574            LOG(INFO) << "Failed to add image file " << temp_error_msg;
575            dex_files.clear();
576            {
577              ScopedThreadSuspension sts(self, kSuspended);
578              gc::ScopedGCCriticalSection gcs(self,
579                                              gc::kGcCauseAddRemoveAppImageSpace,
580                                              gc::kCollectorTypeAddRemoveAppImageSpace);
581              ScopedSuspendAll ssa("Remove image space");
582              runtime->GetHeap()->RemoveSpace(image_space.get());
583            }
584            // Non-fatal, don't update error_msg.
585          }
586        }
587      }
588    }
589    if (!added_image_space) {
590      DCHECK(dex_files.empty());
591      dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
592
593      // Register for tracking.
594      for (const auto& dex_file : dex_files) {
595        dex::tracking::RegisterDexFile(dex_file.get());
596      }
597    }
598    if (dex_files.empty()) {
599      error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation());
600    } else {
601      // Opened dex files from an oat file, madvise them to their loaded state.
602       for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
603         OatDexFile::MadviseDexFile(*dex_file, MadviseState::kMadviseStateAtLoad);
604       }
605    }
606  }
607
608  // Fall back to running out of the original dex file if we couldn't load any
609  // dex_files from the oat file.
610  if (dex_files.empty()) {
611    if (oat_file_assistant.HasOriginalDexFiles()) {
612      if (Runtime::Current()->IsDexFileFallbackEnabled()) {
613        static constexpr bool kVerifyChecksum = true;
614        const ArtDexFileLoader dex_file_loader;
615        if (!dex_file_loader.Open(dex_location,
616                                  dex_location,
617                                  Runtime::Current()->IsVerificationEnabled(),
618                                  kVerifyChecksum,
619                                  /*out*/ &error_msg,
620                                  &dex_files)) {
621          LOG(WARNING) << error_msg;
622          error_msgs->push_back("Failed to open dex files from " + std::string(dex_location)
623                                + " because: " + error_msg);
624        }
625      } else {
626        error_msgs->push_back("Fallback mode disabled, skipping dex files.");
627      }
628    } else {
629      error_msgs->push_back("No original dex files found for dex location "
630          + std::string(dex_location));
631    }
632  }
633
634  return dex_files;
635}

下面函数获取oat文件,把文件存入容器中

1
2
470  // Get the oat file on disk.
471  std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());

这里跟踪 oat_file,此容器存放了Oat文件,也就是dex优化后的文件

由于返回值为dex文件的容器,这里详细跟踪 dex_files

1
523  std::vector<std::unique_ptr<const DexFile>> dex_files;

如下代码在操作dex文件
判断dex_files容器是否为空,前面并没有赋值因此一定为空。

1
dex_files.empty()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
610  if (dex_files.empty()) {
611    if (oat_file_assistant.HasOriginalDexFiles()) {
612      if (Runtime::Current()->IsDexFileFallbackEnabled()) {
613        static constexpr bool kVerifyChecksum = true;
614        const ArtDexFileLoader dex_file_loader;
615        if (!dex_file_loader.Open(dex_location,
616                                  dex_location,
617                                  Runtime::Current()->IsVerificationEnabled(),
618                                  kVerifyChecksum,
619                                  /*out*/ &error_msg,
620                                  &dex_files)) {
621          LOG(WARNING) << error_msg;
622          error_msgs->push_back("Failed to open dex files from " + std::string(dex_location)
623                                + " because: " + error_msg);
624        }
625      } else {
626        error_msgs->push_back("Fallback mode disabled, skipping dex files.");
627      }
628    } else {
629      error_msgs->push_back("No original dex files found for dex location "
630          + std::string(dex_location));
631    }
632  }
633
634  return dex_files;

继续跟踪

如下代码把dex_files文件容器的指针放入了,所以一定是在操作dex文件,
open与文件句柄有关,所以一定是在获取dex的文件句柄。

1
2
3
4
5
6
dex_file_loader.Open(dex_location,
616                                  dex_location,
617                                  Runtime::Current()->IsVerificationEnabled(),
618                                  kVerifyChecksum,
619                                  /*out*/ &error_msg,
620                                  &dex_files)

跟踪到此结束,有了句柄就能操作dex文件了,获取dex文件的所有信息,
比如方法,字符串,等~


[招生]科锐逆向工程师培训(2025年3月11日实地,远程教学同时开班, 第52期)!

收藏
免费
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册