-
-
[原创] 安卓9.0dex文件加载分析
-
发表于: 2025-3-30 11:33 1301
-
在分析dex文件加载过程之前,先看看dex如何被系统识别与调用。
1;apk包在安装后dex会存放在根目录,data/data/目录下。
2;加载前需要获取dex文件路径,然后优化。
3;优化后dex文件字节码加载器会初始化生成对象。
4;生成对象后即可调用里面的方法了。
5;反射的原理无非就是获取dex文件里面的类,
然后用类初始化成对象,利用对象调用里面的方法。
以上用代码表示如下,
1 2 3 4 5 6 7 8 9 10 11 | String dexPath = "/data/data/your.package.name/files/plugin.dex" ; // DEX文件路径 File optimizedDir = getDir( "outdex" , Context.MODE_PRIVATE); // 生成一个优化 DexClassLoader dexClassLoader; String AbsolutePath=optimizedDir.getAbsolutePath(); dexClassLoader = new DexClassLoader(dexPath,AbsolutePath , null ,getClassLoader()); // 使用反射机制获取类和方法 String className = "com.example.PluginClass" ; // 目标类的全名 Class<?> loadedClass = dexClassLoader.loadClass(className); Method method = loadedClass.getMethod( "yourMethodName" ); // 方法名 method.invoke(loadedClass.newInstance()); // 调用方法 |
这里我们关键分析DexClassLoader类,这个类加载了dex文件。
源代码来自;http://androidxref.com/
进入网站后搜索DexClassLoader,右侧select all全选,
搜索后点击点击DexClassLoader.java,这个文件是关键点
如下是关键代码
可以看到DexClassLoader 继承了BaseDexClassLoader ,
也就是说有BaseDexClassLoader 里面的所有代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 35public class DexClassLoader extends BaseDexClassLoader { 36 /** 37 * Creates a {@code DexClassLoader} that finds interpreted and native 38 * code. Interpreted classes are found in a set of DEX files contained 39 * in Jar or APK files. 40 * 41 * <p>The path lists are separated using the character specified by the 42 * {@code path.separator} system property, which defaults to {@code :}. 43 * 44 * @param dexPath the list of jar/apk files containing classes and 45 * resources, delimited by {@code File.pathSeparator}, which 46 * defaults to {@code ":"} on Android 47 * @param optimizedDirectory this parameter is deprecated and has no effect since API level 26. 48 * @param librarySearchPath the list of directories containing native 49 * libraries, delimited by {@code File.pathSeparator}; may be 50 * {@code null} 51 * @param parent the parent class loader 52 */ 53 public DexClassLoader(String dexPath, String optimizedDirectory, 54 String librarySearchPath, ClassLoader parent) { 55 super (dexPath, null , librarySearchPath, parent); 56 } 57 } 58 |
这里看一下关键方法,第一个参数为dexPath 也就是dex文件路径,
这里只看dex文件路径就好了,这个是加载dex文件的关键,
1 2 3 4 | 53 public DexClassLoader(String dexPath, String optimizedDirectory, 54 String librarySearchPath, ClassLoader parent) { 55 super (dexPath, null , librarySearchPath, parent); 56 } |
DexClassLoader方法调用了super方法,也就是调用了BaseDexClassLoader 里面的方法,
1 | super (dexPath, null , librarySearchPath, parent); |
这里继续跟踪,搜索DexClassLoader
进去后看见关键方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 63 public BaseDexClassLoader(String dexPath, File optimizedDirectory, 64 String librarySearchPath, ClassLoader parent) { 65 this (dexPath, optimizedDirectory, librarySearchPath, parent, false ); 66 } 67 68 /** 69 * @hide 70 */ 71 public BaseDexClassLoader(String dexPath, File optimizedDirectory, 72 String librarySearchPath, ClassLoader parent, boolean isTrusted) { 73 super (parent); 74 this .pathList = new DexPathList( this , dexPath, librarySearchPath, null , isTrusted); 75 76 if (reporter != null ) { 77 reportClassLoaderChain(); 78 } 79 } |
调用BaseDexClassLoader时由于传入了四个参数,
1 | super (dexPath, null , librarySearchPath, parent); |
我们只跟踪如下方法,
如下方法调用了this,多传入了一个参数false,
this的意思是调用相同的方法,但是参数不一样,
1 2 3 4 | 63 public BaseDexClassLoader(String dexPath, File optimizedDirectory, 64 String librarySearchPath, ClassLoader parent) { 65 this (dexPath, optimizedDirectory, librarySearchPath, parent, false ); 66 } |
我们继续跟踪如下方法
1 2 3 4 5 6 7 8 9 | 71 public BaseDexClassLoader(String dexPath, File optimizedDirectory, 72 String librarySearchPath, ClassLoader parent, boolean isTrusted) { 73 super (parent); 74 this .pathList = new DexPathList( this , dexPath, librarySearchPath, null , isTrusted); 75 76 if (reporter != null ) { 77 reportClassLoaderChain(); 78 } 79 } |
上面的方法初始化了DexPathList对象,
继续跟踪,进入DexPathList方法里面,这个方法传入了上面说的5个参数。
进入DexPathList.java文件后,跟踪如下方法,这个方法传入了五个参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | 135 DexPathList(ClassLoader definingContext, String dexPath, 136 String librarySearchPath, File optimizedDirectory, boolean isTrusted) { 137 if (definingContext == null ) { 138 throw new NullPointerException( "definingContext == null" ); 139 } 140 141 if (dexPath == null ) { 142 throw new NullPointerException( "dexPath == null" ); 143 } 144 145 if (optimizedDirectory != null ) { 146 if (!optimizedDirectory.exists()) { 147 throw new IllegalArgumentException( 148 "optimizedDirectory doesn't exist: " 149 + optimizedDirectory); 150 } 151 152 if (!(optimizedDirectory.canRead() 153 && optimizedDirectory.canWrite())) { 154 throw new IllegalArgumentException( 155 "optimizedDirectory not readable/writable: " 156 + optimizedDirectory); 157 } 158 } 159 160 this .definingContext = definingContext; 161 162 ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>(); 163 // save dexPath for BaseDexClassLoader 164 this .dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, 165 suppressedExceptions, definingContext, isTrusted); 166 167 // Native libraries may exist in both the system and 168 // application library paths, and we use this search order: 169 // 170 // 1. This class loader's library path for application libraries (librarySearchPath): 171 // 1.1. Native library directories 172 // 1.2. Path to libraries in apk-files 173 // 2. The VM's library path from the system property for system libraries 174 // also known as java.library.path 175 // 176 // This order was reversed prior to Gingerbread; see http://b/2933456. 177 this .nativeLibraryDirectories = splitPaths(librarySearchPath, false ); 178 this .systemNativeLibraryDirectories = 179 splitPaths(System.getProperty( "java.library.path" ), true ); 180 List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories); 181 allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories); 182 183 this .nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories); 184 185 if (suppressedExceptions.size() > 0 ) { 186 this .dexElementsSuppressedExceptions = 187 suppressedExceptions.toArray( new IOException[suppressedExceptions.size()]); 188 } else { 189 dexElementsSuppressedExceptions = null ; 190 } 191 } 192 |
以上方法具体是在检测dex文件的相关信息,例如是否存在,
这里继续跟踪dexPath参数,
如下代码传入了dexPath参数,
1 2 3 | 163 // save dexPath for BaseDexClassLoader 164 this .dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, 165 suppressedExceptions, definingContext, isTrusted); |
继续跟踪splitDexPath方法
可以看到此方法调用了splitPaths方法,但传入了两个参数,
这里继续寻找两个参数的此方法,
1 2 3 4 5 6 7 8 | 256 /** 257 * Splits the given dex path string into elements using the path 258 * separator, pruning out any elements that do not refer to existing 259 * and readable files. 260 */ 261 private static List<File> splitDexPath(String path) { 262 return splitPaths(path, false ); 263 } |
如下方法为两个参数的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 265 /** 266 * Splits the given path strings into file elements using the path 267 * separator, combining the results and filtering out elements 268 * that don't exist, aren't readable, or aren't either a regular 269 * file or a directory (as specified). Either string may be empty 270 * or {@code null}, in which case it is ignored. If both strings 271 * are empty or {@code null}, or all elements get pruned out, then 272 * this returns a zero-element list. 273 */ 274 private static List<File> splitPaths(String searchPath, boolean directoriesOnly) { 275 List<File> result = new ArrayList<>(); 276 277 if (searchPath != null ) { 278 for (String path : searchPath.split(File.pathSeparator)) { 279 if (directoriesOnly) { 280 try { 281 StructStat sb = Libcore.os.stat(path); 282 if (!S_ISDIR(sb.st_mode)) { 283 continue ; 284 } 285 } catch (ErrnoException ignored) { 286 continue ; 287 } 288 } 289 result.add( new File(path)); 290 } 291 } 292 293 return result; 294 } |
此方法的返回值为List<File>,返回值里面为文件的集合,因此此方法
初始化了dex文件,把dex文件加载进了内存
由于返回值为dex文件的内存集合
这里只看如下代码
1 | result.add( new File(path)); |
这里的add方法把dex的数据放进了result集合里面,
继续看如下方法,result来自以下代码,这里初始化了集合,
File的意思为文件的意思,这个集合用来存放文件集合,
1 | List<File> result = new ArrayList<>(); |
到这里这个方法makeDexElements就结束了,如下代码返回了result,
因此我们需要继续跟踪result返回值,
1 | 293 return result; |
回到前面的代码,这里我们看返回值dexElements
1 2 3 | 163 // save dexPath for BaseDexClassLoader 164 this .dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, 165 suppressedExceptions, definingContext, isTrusted); |
这里我们继续跟踪makeDexElements方法,这个方法传入了result返回值。
这个方法是五个参数的,我们找五个参数的这个方法。
相关代码如下,这里我们跟踪files的参数,这个参数为dex的内存文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | 325 private static Element[] makeDexElements(List<File> files, File optimizedDirectory, 326 List<IOException> suppressedExceptions, ClassLoader loader, boolean isTrusted) { 327 Element[] elements = new Element[files.size()]; 328 int elementsPos = 0 ; 329 /* 330 * Open all files and load the (direct or contained) dex files up front. 331 */ 332 for (File file : files) { 333 if (file.isDirectory()) { 334 // We support directories for looking up resources. Looking up resources in 335 // directories is useful for running libcore tests. 336 elements[elementsPos++] = new Element(file); 337 } else if (file.isFile()) { 338 String name = file.getName(); 339 340 DexFile dex = null; 341 if (name.endsWith(DEX_SUFFIX)) { 342 // Raw dex file (not inside a zip/jar). 343 try { 344 dex = loadDexFile(file, optimizedDirectory, loader, elements); 345 if (dex != null) { 346 elements[elementsPos++] = new Element(dex, null); 347 } 348 } catch (IOException suppressed) { 349 System.logE("Unable to load dex file: " + file, suppressed); 350 suppressedExceptions.add(suppressed); 351 } 352 } else { 353 try { 354 dex = loadDexFile(file, optimizedDirectory, loader, elements); 355 } catch (IOException suppressed) { 356 /* 357 * IOException might get thrown "legitimately" by the DexFile constructor if 358 * the zip file turns out to be resource-only (that is, no classes.dex file 359 * in it). 360 * Let dex == null and hang on to the exception to add to the tea-leaves for 361 * when findClass returns null. 362 */ 363 suppressedExceptions.add(suppressed); 364 } 365 366 if (dex == null ) { 367 elements[elementsPos++] = new Element(file); 368 } else { 369 elements[elementsPos++] = new Element(dex, file); 370 } 371 } 372 if (dex != null && isTrusted) { 373 dex.setTrusted(); 374 } 375 } else { 376 System.logW( "ClassLoader referenced unknown path: " + file); 377 } 378 } 379 if (elementsPos != elements.length) { 380 elements = Arrays.copyOf(elements, elementsPos); 381 } 382 return elements; 383 } |
这里看关键代码块,name.endsWith(DEX_SUFFIX)这个代码块在判断是否为dex后缀,
我们跟踪第一个传入了file参数的方法,因为这肯定是dex文件后缀的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 341 if (name.endsWith(DEX_SUFFIX)) { 342 // Raw dex file (not inside a zip/jar). 343 try { 344 dex = loadDexFile(file, optimizedDirectory, loader, elements); 345 if (dex != null ) { 346 elements[elementsPos++] = new Element(dex, null ); 347 } 348 } catch (IOException suppressed) { 349 System.logE( "Unable to load dex file: " + file, suppressed); 350 suppressedExceptions.add(suppressed); 351 } 352 } else { 353 try { 354 dex = loadDexFile(file, optimizedDirectory, loader, elements); 355 } catch (IOException suppressed) { 356 /* 357 * IOException might get thrown "legitimately" by the DexFile constructor if 358 * the zip file turns out to be resource-only (that is, no classes.dex file 359 * in it). 360 * Let dex == null and hang on to the exception to add to the tea-leaves for 361 * when findClass returns null. 362 */ 363 suppressedExceptions.add(suppressed); 364 } |
跟踪如下代码,我们进入loadDexFile代码,
1 | 344 dex = loadDexFile(file, optimizedDirectory, loader, elements); |
下面为loadDexFile方法,这里跟踪loadDex方法,因为返回值为DexFile,意思是dex文件,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 385 /** 386 * Constructs a {@code DexFile} instance, as appropriate depending on whether 387 * {@code optimizedDirectory} is {@code null}. An application image file may be associated with 388 * the {@code loader} if it is not null. 389 */ 390 private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader, 391 Element[] elements) 392 throws IOException { 393 if (optimizedDirectory == null ) { 394 return new DexFile(file, loader, elements); 395 } else { 396 String optimizedPath = optimizedPathFor(file, optimizedDirectory); 397 return DexFile.loadDex(file.getPath(), optimizedPath, 0 , loader, elements); 398 } 399 } |
这里需要看前面的代码
第二个参数为null
1 | super (dexPath, null , librarySearchPath, parent); |
因此跟踪下面的代码块
1 | return new DexFile(file, loader, elements); |
同样,这个方法传入了三个参数,跟踪三个参数的方法就行
下面的方法调用了this,得找相同的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 63 /* 64 * Private version with class loader argument. 65 * 66 * @param file 67 * the File object referencing the actual DEX file 68 * @param loader 69 * the class loader object creating the DEX file object 70 * @param elements 71 * the temporary dex path list elements from DexPathList.makeElements 72 */ 73 DexFile(File file, ClassLoader loader, DexPathList.Element[] elements) 74 throws IOException { 75 this (file.getPath(), loader, elements); 76 } |
继续看下面的方法,file.getPath()的意思是获取路径,也就是dex文件名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 90 /* 91 * Private version with class loader argument. 92 * 93 * @param fileName 94 * the filename of the DEX file 95 * @param loader 96 * the class loader creating the DEX file object 97 * @param elements 98 * the temporary dex path list elements from DexPathList.makeElements 99 */ 100 DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException { 101 mCookie = openDexFile(fileName, null , 0 , loader, elements); 102 mInternalCookie = mCookie; 103 mFileName = fileName; 104 //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName); 105 } |
这里跟踪下面的方法,看fileName参数就行。
1 | openDexFile(fileName, null , 0 , loader, elements); |
下面的代码开始进入c函数层了,这里是关键点,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 347 /* 348 * Open a DEX file. The value returned is a magic VM cookie. On 349 * failure, an IOException is thrown. 350 */ 351 private static Object openDexFile(String sourceName, String outputName, int flags, 352 ClassLoader loader, DexPathList.Element[] elements) throws IOException { 353 // Use absolute paths to enable the use of relative paths when testing on host. 354 return openDexFileNative( new File(sourceName).getAbsolutePath(), 355 (outputName == null ) 356 ? null 357 : new File(outputName).getAbsolutePath(), 358 flags, 359 loader, 360 elements); 361 } |
这里看下面的函数
1 2 3 4 5 6 7 8 | 353 // Use absolute paths to enable the use of relative paths when testing on host. 354 return openDexFileNative( new File(sourceName).getAbsolutePath(), 355 (outputName == null ) 356 ? null 357 : new File(outputName).getAbsolutePath(), 358 flags, 359 loader, 360 elements); |
1 2 3 4 5 6 | 398 /* 399 * Open a DEX file. The value returned is a magic VM cookie. On 400 * failure, an IOException is thrown. 401 */ 402 private static native Object openDexFileNative(String sourceName, String outputName, int flags, 403 ClassLoader loader, DexPathList.Element[] elements); |
这里没有实现,因此需要找这个函数的代码块才行,
这里搜索openDexFileNative,跟踪/art/runtime/native/dalvik_system_DexFile.cc文件
如下代码块注册了openDexFileNative函数,
1 2 3 4 5 6 7 | 854 NATIVE_METHOD(DexFile, openDexFileNative, 855 "(Ljava/lang/String;" 856 "Ljava/lang/String;" 857 "I" 858 "Ljava/lang/ClassLoader;" 859 "[Ldalvik/system/DexPathList$Element;" 860 ")Ljava/lang/Object;" ), |
这里跟踪这个函数DexFile_openDexFileNative
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 266 // TODO(calin): clean up the unused parameters (here and in libcore). 267static jobject DexFile_openDexFileNative(JNIEnv* env, 268 jclass, 269 jstring javaSourceName, 270 jstring javaOutputName ATTRIBUTE_UNUSED, 271 jint flags ATTRIBUTE_UNUSED, 272 jobject class_loader, 273 jobjectArray dex_elements) { 274 ScopedUtfChars sourceName(env, javaSourceName); 275 if (sourceName.c_str() == nullptr) { 276 return 0; 277 } 278 279 Runtime* const runtime = Runtime::Current(); 280 ClassLinker* linker = runtime->GetClassLinker(); 281 std::vector<std::unique_ptr< const DexFile>> dex_files; 282 std::vector<std::string> error_msgs; 283 const OatFile* oat_file = nullptr; 284 285 dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(), 286 class_loader, 287 dex_elements, 288 /*out*/ &oat_file, 289 /*out*/ &error_msgs); 290 291 if (!dex_files.empty()) { 292 jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files); 293 if (array == nullptr) { 294 ScopedObjectAccess soa(env); 295 for ( auto & dex_file : dex_files) { 296 if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) { 297 dex_file.release(); 298 } 299 } 300 } 301 return array; 302 } else { 303 ScopedObjectAccess soa(env); 304 CHECK(!error_msgs.empty()); 305 // The most important message is at the end. So set up nesting by going forward, which will 306 // wrap the existing exception as a cause for the following one. 307 auto it = error_msgs.begin(); 308 auto itEnd = error_msgs.end(); 309 for ( ; it != itEnd; ++it) { 310 ThrowWrappedIOException( "%s" , it->c_str()); 311 } 312 313 return nullptr; 314 } 315} |
这里跟踪第3个参数,第一个参数env是默认的,第二个参数jclass也是默认的,
第三个参数为java方法传入的string字符串,这里跟踪参数javaSourceName
初始化对象sourceName
1 | 274 ScopedUtfChars sourceName(env, javaSourceName); |
判断sourceName是否为空
1 2 3 | 275 if (sourceName.c_str() == nullptr) { 276 return 0; 277 } |
定义一个容器存放dex文件
1 | 281 std::vector<std::unique_ptr< const DexFile>> dex_files; |
oat为优化文件,OatFile*指针用于存放dex优化后的文件
1 | 283 const OatFile* oat_file = nullptr; |
这里继续看关键代码,跟踪OpenDexFilesFromOat函数,
此函数传入了五个参数,这里跟踪五个参数的函数,
1 2 3 4 5 | 285 dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(), 286 class_loader, 287 dex_elements, 288 /*out*/ &oat_file, 289 /*out*/ &error_msgs); |
如下为关键代码块,这里跟踪dex_location参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | 394std::vector<std::unique_ptr< const DexFile>> OatFileManager::OpenDexFilesFromOat( 395 const char * dex_location, 396 jobject class_loader, 397 jobjectArray dex_elements, 398 const OatFile** out_oat_file, 399 std::vector<std::string>* error_msgs) { 400 ScopedTrace trace(__FUNCTION__); 401 CHECK(dex_location != nullptr); 402 CHECK(error_msgs != nullptr); 403 404 // Verify we aren't holding the mutator lock, which could starve GC if we 405 // have to generate or relocate an oat file. 406 Thread* const self = Thread::Current(); 407 Locks::mutator_lock_->AssertNotHeld(self); 408 Runtime* const runtime = Runtime::Current(); 409 410 std::unique_ptr<ClassLoaderContext> context; 411 // If the class_loader is null there's not much we can do. This happens if a dex files is loaded 412 // directly with DexFile APIs instead of using class loaders. 413 if (class_loader == nullptr) { 414 LOG(WARNING) << "Opening an oat file without a class loader. " 415 << "Are you using the deprecated DexFile APIs?" ; 416 context = nullptr; 417 } else { 418 context = ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements); 419 } 420 421 OatFileAssistant oat_file_assistant(dex_location, 422 kRuntimeISA, 423 !runtime->IsAotCompiler(), 424 only_use_system_oat_files_); 425 426 // Lock the target oat location to avoid races generating and loading the 427 // oat file. 428 std::string error_msg; 429 if (!oat_file_assistant.Lock( /*out*/ &error_msg)) { 430 // Don't worry too much if this fails. If it does fail, it's unlikely we 431 // can generate an oat file anyway. 432 VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg; 433 } 434 435 const OatFile* source_oat_file = nullptr; 436 437 if (!oat_file_assistant.IsUpToDate()) { 438 // Update the oat file on disk if we can, based on the --compiler-filter 439 // option derived from the current runtime options. 440 // This may fail, but that's okay. Best effort is all that matters here. 441 // TODO(calin): b/64530081 b/66984396. Pass a null context to verify and compile 442 // secondary dex files in isolation (and avoid to extract/verify the main apk 443 // if it's in the class path). Note this trades correctness for performance 444 // since the resulting slow down is unacceptable in some cases until b/64530081 445 // is fixed. 446 // We still pass the class loader context when the classpath string of the runtime 447 // is not empty, which is the situation when ART is invoked standalone. 448 ClassLoaderContext* actual_context = Runtime::Current()->GetClassPathString().empty() 449 ? nullptr 450 : context.get(); 451 switch (oat_file_assistant.MakeUpToDate( /*profile_changed*/ false , 452 actual_context, 453 /*out*/ &error_msg)) { 454 case OatFileAssistant::kUpdateFailed: 455 LOG(WARNING) << error_msg; 456 break ; 457 458 case OatFileAssistant::kUpdateNotAttempted: 459 // Avoid spamming the logs if we decided not to attempt making the oat 460 // file up to date. 461 VLOG(oat) << error_msg; 462 break ; 463 464 case OatFileAssistant::kUpdateSucceeded: 465 // Nothing to do. 466 break ; 467 } 468 } |
这里跟踪如下函数oat_file_assistant,此函数为四个参数,
1 2 3 4 5 6 | 421 OatFileAssistant oat_file_assistant(dex_location, 422 kRuntimeISA, 423 !runtime->IsAotCompiler(), 424 only_use_system_oat_files_); 这里搜索OatFileAssistant ,找到cc文件 |
看到如下函数,这里跟踪父函数,父函数有7个参数
1 2 3 4 5 6 7 8 9 10 11 | 75OatFileAssistant::OatFileAssistant( const char * dex_location, 76 const InstructionSet isa, 77 bool load_executable, 78 bool only_load_system_executable) 79 : OatFileAssistant(dex_location, 80 isa, 81 load_executable, 82 only_load_system_executable, 83 -1 /* vdex_fd */ , 84 -1 /* oat_fd */ , 85 -1 /* zip_fd */ ) {} |
父函数如下,还是跟踪第一个参数dex_location,这是一个char *存放字符串的指针,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 88OatFileAssistant::OatFileAssistant( const char * dex_location, 89 const InstructionSet isa, 90 bool load_executable, 91 bool only_load_system_executable, 92 int vdex_fd, 93 int oat_fd, 94 int zip_fd) 95 : isa_(isa), 96 load_executable_(load_executable), 97 only_load_system_executable_(only_load_system_executable), 98 odex_( this , /*is_oat_location*/ false ), 99 oat_( this , /*is_oat_location*/ true ), 100 zip_fd_(zip_fd) { 101 CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location" ; 102 103 if (zip_fd < 0) { 104 CHECK_LE(oat_fd, 0) << "zip_fd must be provided with valid oat_fd. zip_fd=" << zip_fd 105 << " oat_fd=" << oat_fd; 106 CHECK_LE(vdex_fd, 0) << "zip_fd must be provided with valid vdex_fd. zip_fd=" << zip_fd 107 << " vdex_fd=" << vdex_fd;; 108 } 109 110 dex_location_.assign(dex_location); 111 112 if (load_executable_ && isa != kRuntimeISA) { 113 LOG(WARNING) << "OatFileAssistant: Load executable specified, " 114 << "but isa is not kRuntimeISA. Will not attempt to load executable." ; 115 load_executable_ = false ; 116 } 117 118 // Get the odex filename. 119 std::string error_msg; 120 std::string odex_file_name; 121 if (DexLocationToOdexFilename(dex_location_, isa_, &odex_file_name, &error_msg)) { 122 odex_.Reset(odex_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd); 123 } else { 124 LOG(WARNING) << "Failed to determine odex file name: " << error_msg; 125 } 126 127 if (!UseFdToReadFiles()) { 128 // Get the oat filename. 129 std::string oat_file_name; 130 if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) { 131 oat_.Reset(oat_file_name, false /* use_fd */ ); 132 } else { 133 LOG(WARNING) << "Failed to determine oat file name for dex location " 134 << dex_location_ << ": " << error_msg; 135 } 136 } 137 138 // Check if the dex directory is writable. 139 // This will be needed in most uses of OatFileAssistant and so it's OK to 140 // compute it eagerly. (the only use which will not make use of it is 141 // OatFileAssistant::GetStatusDump()) 142 size_t pos = dex_location_.rfind( '/' ); 143 if (pos == std::string::npos) { 144 LOG(WARNING) << "Failed to determine dex file parent directory: " << dex_location_; 145 } else if (!UseFdToReadFiles()) { 146 // We cannot test for parent access when using file descriptors. That's ok 147 // because in this case we will always pick the odex file anyway. 148 std::string parent = dex_location_.substr(0, pos); 149 if (access(parent.c_str(), W_OK) == 0) { 150 dex_parent_writable_ = true ; 151 } else { 152 VLOG(oat) << "Dex parent of " << dex_location_ << " is not writable: " << strerror ( errno ); 153 } 154 } 155} |
如下函数拷贝了字符串给dex_location_,继续跟踪dex_location_
1 | 110 dex_location_.assign(dex_location); |
如下函数传入了dex_location_参数,这个函数是将dex优化成oat文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 862bool OatFileAssistant::DexLocationToOatFilename( const std::string& location, 863 InstructionSet isa, 864 std::string* oat_filename, 865 std::string* error_msg) { 866 CHECK(oat_filename != nullptr); 867 CHECK(error_msg != nullptr); 868 869 std::string cache_dir = GetDalvikCache(GetInstructionSetString(isa)); 870 if (cache_dir.empty()) { 871 *error_msg = "Dalvik cache directory does not exist" ; 872 return false ; 873 } 874 875 // TODO: The oat file assistant should be the definitive place for 876 // determining the oat file name from the dex location, not 877 // GetDalvikCacheFilename. 878 return GetDalvikCacheFilename(location.c_str(), cache_dir.c_str(), oat_filename, error_msg); 879} |
这里跟踪GetDalvikCacheFilename函数,传入四个参数
1 | GetDalvikCacheFilename(location.c_str(), cache_dir.c_str(), oat_filename, error_msg); |
如下代码判断dex,art,oat文件,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 269bool GetDalvikCacheFilename( const char * location, const char * cache_location, 270 std::string* filename, std::string* error_msg) { 271 if (location[0] != '/' ) { 272 *error_msg = StringPrintf( "Expected path in location to be absolute: %s" , location); 273 return false ; 274 } 275 std::string cache_file(&location[1]); // skip leading slash 276 if (!android::base::EndsWith(location, ".dex" ) && 277 !android::base::EndsWith(location, ".art" ) && 278 !android::base::EndsWith(location, ".oat" )) { 279 cache_file += "/" ; 280 cache_file += DexFileLoader::kClassesDex; 281 } 282 std::replace(cache_file.begin(), cache_file.end(), '/' , '@' ); 283 *filename = StringPrintf( "%s/%s" , cache_location, cache_file.c_str()); 284 return true ; 285} |
回到前面,继续跟踪如下函数
1 2 3 4 5 6 7 8 9 | 394std::vector<std::unique_ptr< const DexFile>> OatFileManager::OpenDexFilesFromOat( 395 const char * dex_location, 396 jobject class_loader, 397 jobjectArray dex_elements, 398 const OatFile** out_oat_file, 399 std::vector<std::string>* error_msgs) { 400 ScopedTrace trace(__FUNCTION__); 401 CHECK(dex_location != nullptr); 402 CHECK(error_msgs != nullptr); |
如下是此OpenDexFilesFromOat的后面的代码,这里跟踪oat_file_assistant
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | 470 // Get the oat file on disk. 471 std::unique_ptr< const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release()); 472 VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()=" 473 << reinterpret_cast < uintptr_t >(oat_file.get()) 474 << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false ) << ")" ; 475 476 if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { 477 // Prevent oat files from being loaded if no class_loader or dex_elements are provided. 478 // This can happen when the deprecated DexFile.<init>(String) is called directly, and it 479 // could load oat files without checking the classpath, which would be incorrect. 480 // Take the file only if it has no collisions, or we must take it because of preopting. 481 bool accept_oat_file = 482 !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg); 483 if (!accept_oat_file) { 484 // Failed the collision check. Print warning. 485 if (Runtime::Current()->IsDexFileFallbackEnabled()) { 486 if (!oat_file_assistant.HasOriginalDexFiles()) { 487 // We need to fallback but don't have original dex files. We have to 488 // fallback to opening the existing oat file. This is potentially 489 // unsafe so we warn about it. 490 accept_oat_file = true ; 491 492 LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. " 493 << "Allow oat file use. This is potentially dangerous." ; 494 } else { 495 // We have to fallback and found original dex files - extract them from an APK. 496 // Also warn about this operation because it's potentially wasteful. 497 LOG(WARNING) << "Found duplicate classes, falling back to extracting from APK : " 498 << dex_location; 499 LOG(WARNING) << "NOTE: This wastes RAM and hurts startup performance." ; 500 } 501 } else { 502 // TODO: We should remove this. The fact that we're here implies -Xno-dex-file-fallback 503 // was set, which means that we should never fallback. If we don't have original dex 504 // files, we should just fail resolution as the flag intended. 505 if (!oat_file_assistant.HasOriginalDexFiles()) { 506 accept_oat_file = true ; 507 } 508 509 LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to " 510 " load classes for " << dex_location; 511 } 512 513 LOG(WARNING) << error_msg; 514 } 515 516 if (accept_oat_file) { 517 VLOG(class_linker) << "Registering " << oat_file->GetLocation(); 518 source_oat_file = RegisterOatFile(std::move(oat_file)); 519 *out_oat_file = source_oat_file; 520 } 521 } 522 523 std::vector<std::unique_ptr< const DexFile>> dex_files; 524 525 // Load the dex files from the oat file. 526 if (source_oat_file != nullptr) { 527 bool added_image_space = false ; 528 if (source_oat_file->IsExecutable()) { 529 // We need to throw away the image space if we are debuggable but the oat-file source of the 530 // image is not otherwise we might get classes with inlined methods or other such things. 531 std::unique_ptr<gc::space::ImageSpace> image_space; 532 if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) { 533 image_space = oat_file_assistant.OpenImageSpace(source_oat_file); 534 } else { 535 image_space = nullptr; 536 } 537 if (image_space != nullptr) { 538 ScopedObjectAccess soa(self); 539 StackHandleScope<1> hs(self); 540 Handle<mirror::ClassLoader> h_loader( 541 hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader))); 542 // Can not load app image without class loader. 543 if (h_loader != nullptr) { 544 std::string temp_error_msg; 545 // Add image space has a race condition since other threads could be reading from the 546 // spaces array. 547 { 548 ScopedThreadSuspension sts(self, kSuspended); 549 gc::ScopedGCCriticalSection gcs(self, 550 gc::kGcCauseAddRemoveAppImageSpace, 551 gc::kCollectorTypeAddRemoveAppImageSpace); 552 ScopedSuspendAll ssa( "Add image space" ); 553 runtime->GetHeap()->AddSpace(image_space.get()); 554 } 555 { 556 ScopedTrace trace2(StringPrintf( "Adding image space for location %s" , dex_location)); 557 added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(), 558 h_loader, 559 dex_elements, 560 dex_location, 561 /*out*/ &dex_files, 562 /*out*/ &temp_error_msg); 563 } 564 if (added_image_space) { 565 // Successfully added image space to heap, release the map so that it does not get 566 // freed. 567 image_space.release(); 568 569 // Register for tracking. 570 for ( const auto & dex_file : dex_files) { 571 dex::tracking::RegisterDexFile(dex_file.get()); 572 } 573 } else { 574 LOG(INFO) << "Failed to add image file " << temp_error_msg; 575 dex_files.clear(); 576 { 577 ScopedThreadSuspension sts(self, kSuspended); 578 gc::ScopedGCCriticalSection gcs(self, 579 gc::kGcCauseAddRemoveAppImageSpace, 580 gc::kCollectorTypeAddRemoveAppImageSpace); 581 ScopedSuspendAll ssa( "Remove image space" ); 582 runtime->GetHeap()->RemoveSpace(image_space.get()); 583 } 584 // Non-fatal, don't update error_msg. 585 } 586 } 587 } 588 } 589 if (!added_image_space) { 590 DCHECK(dex_files.empty()); 591 dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location); 592 593 // Register for tracking. 594 for ( const auto & dex_file : dex_files) { 595 dex::tracking::RegisterDexFile(dex_file.get()); 596 } 597 } 598 if (dex_files.empty()) { 599 error_msgs->push_back( "Failed to open dex files from " + source_oat_file->GetLocation()); 600 } else { 601 // Opened dex files from an oat file, madvise them to their loaded state. 602 for ( const std::unique_ptr< const DexFile>& dex_file : dex_files) { 603 OatDexFile::MadviseDexFile(*dex_file, MadviseState::kMadviseStateAtLoad); 604 } 605 } 606 } 607 608 // Fall back to running out of the original dex file if we couldn't load any 609 // dex_files from the oat file. 610 if (dex_files.empty()) { 611 if (oat_file_assistant.HasOriginalDexFiles()) { 612 if (Runtime::Current()->IsDexFileFallbackEnabled()) { 613 static constexpr bool kVerifyChecksum = true ; 614 const ArtDexFileLoader dex_file_loader; 615 if (!dex_file_loader.Open(dex_location, 616 dex_location, 617 Runtime::Current()->IsVerificationEnabled(), 618 kVerifyChecksum, 619 /*out*/ &error_msg, 620 &dex_files)) { 621 LOG(WARNING) << error_msg; 622 error_msgs->push_back( "Failed to open dex files from " + std::string(dex_location) 623 + " because: " + error_msg); 624 } 625 } else { 626 error_msgs->push_back( "Fallback mode disabled, skipping dex files." ); 627 } 628 } else { 629 error_msgs->push_back( "No original dex files found for dex location " 630 + std::string(dex_location)); 631 } 632 } 633 634 return dex_files; 635} |
下面函数获取oat文件,把文件存入容器中
1 2 | 470 // Get the oat file on disk. 471 std::unique_ptr< const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release()); |
这里跟踪 oat_file,此容器存放了Oat文件,也就是dex优化后的文件
由于返回值为dex文件的容器,这里详细跟踪 dex_files
1 | 523 std::vector<std::unique_ptr< const DexFile>> dex_files; |
如下代码在操作dex文件
判断dex_files容器是否为空,前面并没有赋值因此一定为空。
1 | dex_files.empty() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 610 if (dex_files.empty()) { 611 if (oat_file_assistant.HasOriginalDexFiles()) { 612 if (Runtime::Current()->IsDexFileFallbackEnabled()) { 613 static constexpr bool kVerifyChecksum = true ; 614 const ArtDexFileLoader dex_file_loader; 615 if (!dex_file_loader.Open(dex_location, 616 dex_location, 617 Runtime::Current()->IsVerificationEnabled(), 618 kVerifyChecksum, 619 /*out*/ &error_msg, 620 &dex_files)) { 621 LOG(WARNING) << error_msg; 622 error_msgs->push_back( "Failed to open dex files from " + std::string(dex_location) 623 + " because: " + error_msg); 624 } 625 } else { 626 error_msgs->push_back( "Fallback mode disabled, skipping dex files." ); 627 } 628 } else { 629 error_msgs->push_back( "No original dex files found for dex location " 630 + std::string(dex_location)); 631 } 632 } 633 634 return dex_files; |
继续跟踪
如下代码把dex_files文件容器的指针放入了,所以一定是在操作dex文件,
open与文件句柄有关,所以一定是在获取dex的文件句柄。
1 2 3 4 5 6 | dex_file_loader.Open(dex_location, 616 dex_location, 617 Runtime::Current()->IsVerificationEnabled(), 618 kVerifyChecksum, 619 /*out*/ &error_msg, 620 &dex_files) |
跟踪到此结束,有了句柄就能操作dex文件了,获取dex文件的所有信息,
比如方法,字符串,等~
赞赏
- [原创] 逆向破解所有会员 1880
- [原创] 元气桌面逆向分析 1667
- [原创] 安卓9.0dex文件加载分析 1302
- [原创] 安卓9.0loadLibrary源码分析 1358
- [原创]悟饭游戏厅逆向破解 2428