首页
论坛
课程
招聘
[原创]Android9.0脱壳时机点分析
2022-7-4 00:55 10470

[原创]Android9.0脱壳时机点分析

2022-7-4 00:55
10470

安卓9.0是采用的art虚拟机加载dex文件,与dvm虚拟机不一样,但是在java层的加载dex文件的方法还是
在BaseDexClassLoader类里面
我们看一下BaseDexClassLoader.java这个类
如下是这个类的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
3    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    }
80
123    public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {
124        // TODO We should support giving this a library search path maybe.
125        super(parent);
126        this.pathList = new DexPathList(this, dexFiles);
127    }

我们可以传入dex文件的路径把dex文件加载进内存,然后调用如下方法,获取dex里面的类,
我们可以传入想要获取的类的名字,
可以看到,这个方法里面又调用了pathList对象里面的findClass方法
如下方法判断是否有这个类,没有就抛出一个ClassNotFoundException的异常
有的话就返回获取到的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
129    @Override
130    protected Class<?> findClass(String name) throws ClassNotFoundException {
131        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
132        Class c = pathList.findClass(name, suppressedExceptions);
133        if (c == null) {
134            ClassNotFoundException cnfe = new ClassNotFoundException(
135                    "Didn't find class \"" + name + "\" on path: " + pathList);
136            for (Throwable t : suppressedExceptions) {
137                cnfe.addSuppressed(t);
138            }
139            throw cnfe;
140        }
141        return c;
142    }
143

获取到类之后,我们就可以通过反射来间接调用里面的方法,获取里面的变量等。
调用案例

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
package org.entity;
 
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
 
 
 
public class Test2 {
 
    public static void main(String[] args) {
            try {
            /*
            这里省略加载dex文件,获取class对象
 
                */
        //clzz为获取到的class对象
                Object classObj=clzz.newInstance();
                //newInstance创建实例将class类转换为对象
                //调用getMethods方法获取该类的所有方法
                Method[] methods = clzz.getMethods();
                //遍历方法
                for(Method m:methods){
                    if(m.getName().equals("xxxxxxx")){//找到xxxxxxx这个方法
                        try {
                            //invoke方法第一个参数是要调用的类
                            //第二个是要传入的参数
                            m.invoke(classObj, "xxxxxxxxxx");
                        } catch (IllegalArgumentException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
 
    }
 
}

我们回到如下构造方法中

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对象,传入了我们的dexpath路径,
我们看一下这个方法的构造方法

 

这个方法判断dexpath也就是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
60
61
62
130    public DexPathList(ClassLoader definingContext, String dexPath,
131            String librarySearchPath, File optimizedDirectory) {
132        this(definingContext, dexPath, librarySearchPath, optimizedDirectory, false);
133    }
134
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    }

我们看一下makeDexElements这个方法,因为传入了dexpath路径,optimizedDirectory优化文件存放路径等,
这个方法先是遍历了file文件集合,然后判断这个file对象是否是文件夹或者文件,
然后调用endsWith方法判断文件名结尾是否为.dex
如下是DEX_SUFFIX变量的定义
private static final String DEX_SUFFIX = ".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
60
61
62
63
64
65
66
67
68
69
315    /**
316     * Makes an array of dex/resource path elements, one per element of
317     * the given array.
318     */
319    private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
320            List<IOException> suppressedExceptions, ClassLoader loader) {
321        return makeDexElements(files, optimizedDirectory, suppressedExceptions, loader, false);
322    }
323
324
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    }

可以看到下面的判断分支都调用了loadDexFile方法,传入了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
25
26
27
28
29
30
31
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              }

我们看一下loadDexFile方法
可以看到有两个判断分支,一个是创建了一个DexFile对象,
一个是调用了DexFile对象里的,loadDex方法
主要区别就是这个优化dex文件的存放路径optimizedDirectory

1
2
3
4
5
6
7
8
9
10
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    }

我们看一下DexFile类的构造方法,还有loadDex方法

 

如下是DexFile类的构造方法

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
52    /**
53     * Opens a DEX file from a given File object.
54     *
55     * @deprecated Applications should use one of the standard classloaders such
56     *     as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
57     *     in a future Android release</b>.
58     */
59    @Deprecated
60    public DexFile(File file) throws IOException {
61        this(file.getPath());
62    }
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    }
77
78    /**
79     * Opens a DEX file from a given filename.
80     *
81     * @deprecated Applications should use one of the standard classloaders such
82     *     as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
83     *     in a future Android release</b>.
84     */
85    @Deprecated
86    public DexFile(String fileName) throws IOException {
87        this(fileName, null, null);
88    }
89
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    }
106
107    DexFile(ByteBuffer buf) throws IOException {
108        mCookie = openInMemoryDexFile(buf);
109        mInternalCookie = mCookie;
110        mFileName = null;
111    }
112
113    /**
114     * Opens a DEX file from a given filename, using a specified file
115     * to hold the optimized data.
116     *
117     * @param sourceName
118     *  Jar or APK file with "classes.dex".
119     * @param outputName
120     *  File that will hold the optimized form of the DEX data.
121     * @param flags
122     *  Enable optional features.
123     * @param loader
124     *  The class loader creating the DEX file object.
125     * @param elements
126     *  The temporary dex path list elements from DexPathList.makeElements
127     */
128    private DexFile(String sourceName, String outputName, int flags, ClassLoader loader,
129            DexPathList.Element[] elements) throws IOException {
130        if (outputName != null) {
131            try {
132                String parent = new File(outputName).getParent();
133                if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
134                    throw new IllegalArgumentException("Optimized data directory " + parent
135                            + " is not owned by the current user. Shared storage cannot protect"
136                            + " your application from code injection attacks.");
137                }
138            } catch (ErrnoException ignored) {
139                // assume we'll fail with a more contextual error later
140            }
141        }
142
143        mCookie = openDexFile(sourceName, outputName, flags, loader, elements);
144        mInternalCookie = mCookie;
145        mFileName = sourceName;
146        //System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName);
147    }

因为传入的是三个参数的构造方法,我们只需要看如下构造方法

1
2
3
4
5
6
7
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    }
106

可以看到调用了openDexFile方法

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    }

我们再看一下loadDex方法
这个方法传入了五个参数,

1
return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);

如下是loadDex方法的实现

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
149    /**
150     * Open a DEX file, specifying the file in which the optimized DEX
151     * data should be written.  If the optimized form exists and appears
152     * to be current, it will be used; if not, the VM will attempt to
153     * regenerate it.
154     *
155     * @deprecated Applications should use one of the standard classloaders such
156     *     as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed
157     *     in a future Android release</b>.
158     */
159    @Deprecated
160    static public DexFile loadDex(String sourcePathName, String outputPathName,
161        int flags) throws IOException {
162
163        /*
164         * TODO: we may want to cache previously-opened DexFile objects.
165         * The cache would be synchronized with close().  This would help
166         * us avoid mapping the same DEX more than once when an app
167         * decided to open it multiple times.  In practice this may not
168         * be a real issue.
169         */
170        return loadDex(sourcePathName, outputPathName, flags, null, null);
171    }
172
173    /*
174     * Private version of loadDex that also takes a class loader.
175     *
176     * @param sourcePathName
177     *  Jar or APK file with "classes.dex".  (May expand this to include
178     *  "raw DEX" in the future.)
179     * @param outputPathName
180     *  File that will hold the optimized form of the DEX data.
181     * @param flags
182     *  Enable optional features.  (Currently none defined.)
183     * @param loader
184     *  Class loader that is aloading the DEX file.
185     * @param elements
186     *  The temporary dex path list elements from DexPathList.makeElements
187     * @return
188     *  A new or previously-opened DexFile.
189     * @throws IOException
190     *  If unable to open the source or output file.
191     */
192    static DexFile loadDex(String sourcePathName, String outputPathName,
193        int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
194
195        /*
196         * TODO: we may want to cache previously-opened DexFile objects.
197         * The cache would be synchronized with close().  This would help
198         * us avoid mapping the same DEX more than once when an app
199         * decided to open it multiple times.  In practice this may not
200         * be a real issue.
201         */
202        return new DexFile(sourcePathName, outputPathName, flags, loader, elements);
203    }

我们可以看到这个方法最后还是调用了DexFile对象里面的构造方法,而且是五个参数的。

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
113    /**
114     * Opens a DEX file from a given filename, using a specified file
115     * to hold the optimized data.
116     *
117     * @param sourceName
118     *  Jar or APK file with "classes.dex".
119     * @param outputName
120     *  File that will hold the optimized form of the DEX data.
121     * @param flags
122     *  Enable optional features.
123     * @param loader
124     *  The class loader creating the DEX file object.
125     * @param elements
126     *  The temporary dex path list elements from DexPathList.makeElements
127     */
128    private DexFile(String sourceName, String outputName, int flags, ClassLoader loader,
129            DexPathList.Element[] elements) throws IOException {
130        if (outputName != null) {
131            try {
132                String parent = new File(outputName).getParent();
133                if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {
134                    throw new IllegalArgumentException("Optimized data directory " + parent
135                            + " is not owned by the current user. Shared storage cannot protect"
136                            + " your application from code injection attacks.");
137                }
138            } catch (ErrnoException ignored) {
139                // assume we'll fail with a more contextual error later
140            }
141        }
142
143        mCookie = openDexFile(sourceName, outputName, flags, loader, elements);
144        mInternalCookie = mCookie;
145        mFileName = sourceName;
146        //System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName);
147    }

最后面还是调用了openDexFile方法

1
2
3
4
5
6
7
8
9
10
11
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    }

我们跟踪一下openDexFileNative方法,这个方法是native修饰的,所以方法实现在c/c++层
我们可以看到这个方法是动态注册的

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
841static JNINativeMethod gMethods[] = {
842  NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
843  NATIVE_METHOD(DexFile,
844                defineClassNative,
845                "(Ljava/lang/String;"
846                "Ljava/lang/ClassLoader;"
847                "Ljava/lang/Object;"
848                "Ldalvik/system/DexFile;"
849                ")Ljava/lang/Class;"),
850  NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
851  NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
852  NATIVE_METHOD(DexFile, getDexOptNeeded,
853                "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)I"),
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;"),
861  NATIVE_METHOD(DexFile, createCookieWithDirectBuffer,
862                "(Ljava/nio/ByteBuffer;II)Ljava/lang/Object;"),
863  NATIVE_METHOD(DexFile, createCookieWithArray, "([BII)Ljava/lang/Object;"),
864  NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
865  NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
866  NATIVE_METHOD(DexFile,
867                getNonProfileGuidedCompilerFilter,
868                "(Ljava/lang/String;)Ljava/lang/String;"),
869  NATIVE_METHOD(DexFile,
870                getSafeModeCompilerFilter,
871                "(Ljava/lang/String;)Ljava/lang/String;"),
872  NATIVE_METHOD(DexFile, isBackedByOatFile, "(Ljava/lang/Object;)Z"),
873  NATIVE_METHOD(DexFile, getDexFileStatus,
874                "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
875  NATIVE_METHOD(DexFile, getDexFileOutputPaths,
876                "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
877  NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"),
878  NATIVE_METHOD(DexFile, getDexFileOptimizationStatus,
879                "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
880  NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V")
881};

如下是这个函数的实现

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
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}
316

我们看到java层传入的参数传入了OpenDexFilesFromOat这个函数,
这个函数判断了dex_location dex的文件路径 是否为空,判断classloader 类加载器是否为空

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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
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  }
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}

我们看一下如下函数,可以看到创建了一个OatFileAssistant 对象,调用了oat_file_assistant构造方法。
传入了java层的相关参数,我们跟进去看一下这个函数,这个构造方法传入了四个参数

1
2
3
4
421  OatFileAssistant oat_file_assistant(dex_location,
422                                      kRuntimeISA,
423                                      !runtime->IsAotCompiler(),
424                                      only_use_system_oat_files_);

可以看到这个构造函数又继续调用了OatFileAssistant函数

1
2
3
4
5
6
7
8
9
10
11
12
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 */) {}
86

如下是OatFileAssistant的重载函数,有七个参数,函数开头还是在判断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
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}

我们看一下如下函数DexLocationToOdexFilename,相关参数都传入了当前函数

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函数
我们看到这个函数返回值是bool类型的,获取DalvikCacheFilename,获取Dalvik缓存文件名。
通过指针形式给内存空间赋值。

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}

我们回到前面
可以看到这里定义了一个容器,
里面用来存放Dex文件对象的相关信息

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

在DexFile类里面
我们可以看到dex文件结构相关信息,比如checksum,stringids,header_,fieldids,methodids
在这里插入图片描述

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
96DexFile::DexFile(const uint8_t* base,
97                 size_t size,
98                 const uint8_t* data_begin,
99                 size_t data_size,
100                 const std::string& location,
101                 uint32_t location_checksum,
102                 const OatDexFile* oat_dex_file,
103                 std::unique_ptr<DexFileContainer> container,
104                 bool is_compact_dex)
105    : begin_(base),
106      size_(size),
107      data_begin_(data_begin),
108      data_size_(data_size),
109      location_(location),
110      location_checksum_(location_checksum),
111      header_(reinterpret_cast<const Header*>(base)),
112      string_ids_(reinterpret_cast<const StringId*>(base + header_->string_ids_off_)),
113      type_ids_(reinterpret_cast<const TypeId*>(base + header_->type_ids_off_)),
114      field_ids_(reinterpret_cast<const FieldId*>(base + header_->field_ids_off_)),
115      method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)),
116      proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
117      class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
118      method_handles_(nullptr),
119      num_method_handles_(0),
120      call_site_ids_(nullptr),
121      num_call_site_ids_(0),
122      oat_dex_file_(oat_dex_file),
123      container_(std::move(container)),
124      is_compact_dex_(is_compact_dex),
125      is_platform_dex_(false) {
126  CHECK(begin_ != nullptr) << GetLocation();
127  CHECK_GT(size_, 0U) << GetLocation();
128  // Check base (=header) alignment.
129  // Must be 4-byte aligned to avoid undefined behavior when accessing
130  // any of the sections via a pointer.
131  CHECK_ALIGNED(begin_, alignof(Header));
132
133  InitializeSectionsFromMapList();
134}
135
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
84  struct Header {
85    uint8_t magic_[8] = {};
86    uint32_t checksum_ = 0// See also location_checksum_
87    uint8_t signature_[kSha1DigestSize] = {};
88    uint32_t file_size_ = 0// size of entire file
89    uint32_t header_size_ = 0// offset to start of next section
90    uint32_t endian_tag_ = 0;
91    uint32_t link_size_ = 0// unused
92    uint32_t link_off_ = 0// unused
93    uint32_t map_off_ = 0// unused
94    uint32_t string_ids_size_ = 0// number of StringIds
95    uint32_t string_ids_off_ = 0// file offset of StringIds array
96    uint32_t type_ids_size_ = 0// number of TypeIds, we don't support more than 65535
97    uint32_t type_ids_off_ = 0// file offset of TypeIds array
98    uint32_t proto_ids_size_ = 0// number of ProtoIds, we don't support more than 65535
99    uint32_t proto_ids_off_ = 0// file offset of ProtoIds array
100    uint32_t field_ids_size_ = 0// number of FieldIds
101    uint32_t field_ids_off_ = 0// file offset of FieldIds array
102    uint32_t method_ids_size_ = 0// number of MethodIds
103    uint32_t method_ids_off_ = 0// file offset of MethodIds array
104    uint32_t class_defs_size_ = 0// number of ClassDefs
105    uint32_t class_defs_off_ = 0// file offset of ClassDef array
106    uint32_t data_size_ = 0// size of data section
107    uint32_t data_off_ = 0// file offset of data section
108
109    // Decode the dex magic version
110    uint32_t GetVersion() const;
111  };

在DexFile类里面还要检查魔数和版本的函数
计算Checksum的函数等。

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
150bool DexFile::CheckMagicAndVersion(std::string* error_msg) const {
151  if (!IsMagicValid()) {
152    std::ostringstream oss;
153    oss << "Unrecognized magic number in "  << GetLocation() << ":"
154            << " " << header_->magic_[0]
155            << " " << header_->magic_[1]
156            << " " << header_->magic_[2]
157            << " " << header_->magic_[3];
158    *error_msg = oss.str();
159    return false;
160  }
161  if (!IsVersionValid()) {
162    std::ostringstream oss;
163    oss << "Unrecognized version number in "  << GetLocation() << ":"
164            << " " << header_->magic_[4]
165            << " " << header_->magic_[5]
166            << " " << header_->magic_[6]
167            << " " << header_->magic_[7];
168    *error_msg = oss.str();
169    return false;
170  }
171  return true;
172}
63uint32_t DexFile::CalculateChecksum() const {
64  return CalculateChecksum(Begin(), Size());
65}
66
67uint32_t DexFile::CalculateChecksum(const uint8_t* begin, size_t size) {
68  const uint32_t non_sum_bytes = OFFSETOF_MEMBER(DexFile::Header, signature_);
69  return ChecksumMemoryRange(begin + non_sum_bytes, size - non_sum_bytes);
70}
71
72uint32_t DexFile::ChecksumMemoryRange(const uint8_t* begin, size_t size) {
73  return adler32(adler32(0L, Z_NULL, 0), begin, size);
74}
75
76int DexFile::GetPermissions() const {
77  CHECK(container_.get() != nullptr);
78  return container_->GetPermissions();
79}
80
81bool DexFile::IsReadOnly() const {
82  CHECK(container_.get() != nullptr);
83  return container_->IsReadOnly();
84}
85
86bool DexFile::EnableWrite() const {
87  CHECK(container_.get() != nullptr);
88  return container_->EnableWrite();
89}
90
91bool DexFile::DisableWrite() const {
92  CHECK(container_.get() != nullptr);
93  return container_->DisableWrite();
94}

我们回到前面,可以看到如下语句块
可以看到调用了LoadDexFiles函数,返回值就是dex_files

1
2
3
4
5
6
7
8
9
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    }

我们看一下LoadDexFiles这个函数

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
341std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
342    const OatFile &oat_file, const char *dex_location) {
343  std::vector<std::unique_ptr<const DexFile>> dex_files;
344  if (LoadDexFiles(oat_file, dex_location, &dex_files)) {
345    return dex_files;
346  } else {
347    return std::vector<std::unique_ptr<const DexFile>>();
348  }
349}
350
351bool OatFileAssistant::LoadDexFiles(
352    const OatFile &oat_file,
353    const std::string& dex_location,
354    std::vector<std::unique_ptr<const DexFile>>* out_dex_files) {
355  // Load the main dex file.
356  std::string error_msg;
357  const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
358      dex_location.c_str(), nullptr, &error_msg);
359  if (oat_dex_file == nullptr) {
360    LOG(WARNING) << error_msg;
361    return false;
362  }
363
364  std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
365  if (dex_file.get() == nullptr) {
366    LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
367    return false;
368  }
369  out_dex_files->push_back(std::move(dex_file));
370
371  // Load the rest of the multidex entries
372  for (size_t i = 1;; i++) {
373    std::string multidex_dex_location = DexFileLoader::GetMultiDexLocation(i, dex_location.c_str());
374    oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr);
375    if (oat_dex_file == nullptr) {
376      // There are no more multidex entries to load.
377      break;
378    }
379
380    dex_file = oat_dex_file->OpenDexFile(&error_msg);
381    if (dex_file.get() == nullptr) {
382      LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
383      return false;
384    }
385    out_dex_files->push_back(std::move(dex_file));
386  }
387  return true;
388}

我们可以看到关键函数,这个函数返回值就是dex_file

1
364  std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);

LoadDexFiles函数返回值为dex_file,LoadDexFiles函数又调用了OpenDexFile函数,
如下是OpenDexFile函数的定义
可以看到直接就返回了DexFile对象的指针,
这个DexFile对象里面有我们dex文件的所有信息。
我们可以选择在这个时机点脱壳,hook OpenDexFile函数的返回值即可

 

如下是关于OpenDexFile的两个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
113const DexFile* OpenDexFile(const OatFile::OatDexFile* oat_dex_file, std::string* error_msg) {
114  DCHECK(oat_dex_file != nullptr);
115  auto it = opened_dex_files.find(oat_dex_file);
116  if (it != opened_dex_files.end()) {
117    return it->second.get();
118  }
119  const DexFile* ret = oat_dex_file->OpenDexFile(error_msg).release();
120  opened_dex_files.emplace(oat_dex_file, std::unique_ptr<const DexFile>(ret));
121  return ret;
122}
1737std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
1738  ScopedTrace trace(__PRETTY_FUNCTION__);
1739  static constexpr bool kVerify = false;
1740  static constexpr bool kVerifyChecksum = false;
1741  const ArtDexFileLoader dex_file_loader;
1742  return dex_file_loader.Open(dex_file_pointer_,
1743                              FileSize(),
1744                              dex_file_location_,
1745                              dex_file_location_checksum_,
1746                              this,
1747                              kVerify,
1748                              kVerifyChecksum,
1749                              error_msg);
1750}

我们可以看到,这个OpenDexFile函数传入了dex_filepointer,FileSize,
因此也可以hook OpenDexFile里面的 open函数的参数,进行内存dump,获得dex文件,

 

如下是open函数的实现
第一个参数就是指针base,第二个参数是size

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
219std::unique_ptr<const DexFile> DexFileLoader::Open(const uint8_t* base,
220                                                   size_t size,
221                                                   const std::string& location,
222                                                   uint32_t location_checksum,
223                                                   const OatDexFile* oat_dex_file,
224                                                   bool verify,
225                                                   bool verify_checksum,
226                                                   std::string* error_msg) const {
227  return OpenCommon(base,
228                    size,
229                    /*data_base*/ nullptr,
230                    /*data_size*/ 0,
231                    location,
232                    location_checksum,
233                    oat_dex_file,
234                    verify,
235                    verify_checksum,
236                    error_msg,
237                    /*container*/ nullptr,
238                    /*verify_result*/ nullptr);
239}

总结:
java层通过调用BaseDexClassLoader加载器,传入了要加载的dex文件相关路径,然后调用了loadDexFile方法,
随后调用了native层的方法openDexFileNative,进行相关的参数判断,最后调用了OpenDexFile函数,把dex的文件相关信息加载进入了内存,返回值为DexFile对象指针,该对象里面含有dex文件的相关结构信息等。
我们可以通过hook这个OpenDexFile函数,获取返回值,得到dex文件。
也可以通过hook OpenDexFile里面 open函数的参数,进行内存dump获取文件。


恭喜ID[飞翔的猫咪]获看雪安卓应用安全能力认证高级安全工程师!!

最后于 2022-7-5 12:05 被白云精灵编辑 ,原因:
收藏
点赞7
打赏
分享
最新回复 (2)
雪    币: 1
活跃值: 活跃值 (340)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wx_沉默的逗比 活跃值 2022-7-21 20:55
2
0

最后于 2022-7-21 20:55 被wx_沉默的逗比编辑 ,原因:
雪    币: 1
活跃值: 活跃值 (340)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wx_沉默的逗比 活跃值 2022-7-21 20:55
3
0
游客
登录 | 注册 方可回帖
返回