作者论坛账号:白云点缀的蓝
安卓9.0是采用的art虚拟机加载dex文件,与dvm虚拟机不一样,但是在java层的加载dex文件的方法还是
在BaseDexClassLoader类里面
我们看一下BaseDexClassLoader.java这个类
如下是这个类的构造方法
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 }
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的异常
有的话就返回获取到的类
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
获取到类之后,我们就可以通过反射来间接调用里面的方法,获取里面的变量等。
调用案例
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();
}
}
}
我们回到如下构造方法中
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文件路径是否为空。
然后判断优化后的文件存放路径是否为空,判断这个存放文件夹是否存在
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";
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构造方法的那几个参数
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
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类的构造方法
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 }
因为传入的是三个参数的构造方法,我们只需要看如下构造方法
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方法
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方法
这个方法传入了五个参数,
return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
如下是loadDex方法的实现
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对象里面的构造方法,而且是五个参数的。
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方法
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++层
我们可以看到这个方法是动态注册的
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};
如下是这个函数的实现
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 类加载器是否为空
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层的相关参数,我们跟进去看一下这个函数,这个构造方法传入了四个参数
421 OatFileAssistant oat_file_assistant(dex_location,
422 kRuntimeISA,
423 !runtime->IsAotCompiler(),
424 only_use_system_oat_files_);
可以看到这个构造函数又继续调用了OatFileAssistant函数
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 是否为空
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,相关参数都传入了当前函数
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缓存文件名。
通过指针形式给内存空间赋值。
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文件对象的相关信息
复制代码 隐藏代码std::vector<std::unique_ptr<const DexFile>> dex_files;
在DexFile类里面
我们可以看到dex文件结构相关信息,比如checksum,stringids,header_,fieldids,method_ids_等
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
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的函数等。
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
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这个函数
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
复制代码 隐藏代码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的两个函数
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
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获取文件。
--官方论坛
www.52pojie.cn
--推荐给朋友
公众微信号:吾爱破解论坛
或搜微信号:pojie_52