【Android 原创】Android9.0脱壳时机点分析

admin 2022年7月5日19:53:44评论52 views字数 39769阅读132分33秒阅读模式

作者坛账号:白云点缀的蓝


安卓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    }6768    /**69     * @hide70     */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);7576        if (reporter != null) {77            reportClassLoaderChain();78        }79    }80123    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    @Override130    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);7576        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    }134135    DexPathList(ClassLoader definingContext, String dexPath,136            String librarySearchPath, File optimizedDirectory, boolean isTrusted) {137        if (definingContext == null) {138            throw new NullPointerException("definingContext == null");139        }140141        if (dexPath == null) {142            throw new NullPointerException("dexPath == null");143        }144145        if (optimizedDirectory != null) {146            if (!optimizedDirectory.exists())  {147                throw new IllegalArgumentException(148                        "optimizedDirectory doesn't exist: "149                        + optimizedDirectory);150            }151152            if (!(optimizedDirectory.canRead()153                            && optimizedDirectory.canWrite())) {154                throw new IllegalArgumentException(155                        "optimizedDirectory not readable/writable: "156                        + optimizedDirectory);157            }158        }159160        this.definingContext = definingContext;161162        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();163        // save dexPath for BaseDexClassLoader164        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,165                                           suppressedExceptions, definingContext, isTrusted);166167        // Native libraries may exist in both the system and168        // 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 directories172        //   1.2. Path to libraries in apk-files173        //   2. The VM's library path from the system property for system libraries174        //      also known as java.library.path175        //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);182183        this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);184185        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 of317     * 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    }323324325    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 in335              // directories is useful for running libcore tests.336              elements[elementsPos++] = new Element(file);337          } else if (file.isFile()) {338              String name = file.getName();339340              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 if358                       * the zip file turns out to be resource-only (that is, no classes.dex file359                       * in it).360                       * Let dex == null and hang on to the exception to add to the tea-leaves for361                       * when findClass returns null.362                       */363                      suppressedExceptions.add(suppressed);364                  }365366                  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 if358                       * the zip file turns out to be resource-only (that is, no classes.dex file359                       * in it).360                       * Let dex == null and hang on to the exception to add to the tea-leaves for361                       * when findClass returns null.362                       */363                      suppressedExceptions.add(suppressed);364                  }365366                  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 such56     *     as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed57     *     in a future Android release</b>.58     */59    @Deprecated60    public DexFile(File file) throws IOException {61        this(file.getPath());62    }63    /*64     * Private version with class loader argument.65     *66     * @param file67     *            the File object referencing the actual DEX file68     * @param loader69     *            the class loader object creating the DEX file object70     * @param elements71     *            the temporary dex path list elements from DexPathList.makeElements72     */73    DexFile(File file, ClassLoader loader, DexPathList.Element[] elements)74            throws IOException {75        this(file.getPath(), loader, elements);76    }7778    /**79     * Opens a DEX file from a given filename.80     *81     * @deprecated Applications should use one of the standard classloaders such82     *     as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed83     *     in a future Android release</b>.84     */85    @Deprecated86    public DexFile(String fileName) throws IOException {87        this(fileName, null, null);88    }8990    /*91     * Private version with class loader argument.92     *93     * @param fileName94     *            the filename of the DEX file95     * @param loader96     *            the class loader creating the DEX file object97     * @param elements98     *            the temporary dex path list elements from DexPathList.makeElements99     */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    }106107    DexFile(ByteBuffer buf) throws IOException {108        mCookie = openInMemoryDexFile(buf);109        mInternalCookie = mCookie;110        mFileName = null;111    }112113    /**114     * Opens a DEX file from a given filename, using a specified file115     * to hold the optimized data.116     *117     * @param sourceName118     *  Jar or APK file with "classes.dex".119     * @param outputName120     *  File that will hold the optimized form of the DEX data.121     * @param flags122     *  Enable optional features.123     * @param loader124     *  The class loader creating the DEX file object.125     * @param elements126     *  The temporary dex path list elements from DexPathList.makeElements127     */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 " + parent135                            + " 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 later140            }141        }142143        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.  On349     * 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                                     ? null357                                     : 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 DEX151     * data should be written.  If the optimized form exists and appears152     * to be current, it will be used; if not, the VM will attempt to153     * regenerate it.154     *155     * @deprecated Applications should use one of the standard classloaders such156     *     as {@link dalvik.system.PathClassLoader} instead. <b>This API will be removed157     *     in a future Android release</b>.158     */159    @Deprecated160    static public DexFile loadDex(String sourcePathName, String outputPathName,161        int flags) throws IOException {162163        /*164         * TODO: we may want to cache previously-opened DexFile objects.165         * The cache would be synchronized with close().  This would help166         * us avoid mapping the same DEX more than once when an app167         * decided to open it multiple times.  In practice this may not168         * be a real issue.169         */170        return loadDex(sourcePathName, outputPathName, flags, null, null);171    }172173    /*174     * Private version of loadDex that also takes a class loader.175     *176     * @param sourcePathName177     *  Jar or APK file with "classes.dex".  (May expand this to include178     *  "raw DEX" in the future.)179     * @param outputPathName180     *  File that will hold the optimized form of the DEX data.181     * @param flags182     *  Enable optional features.  (Currently none defined.)183     * @param loader184     *  Class loader that is aloading the DEX file.185     * @param elements186     *  The temporary dex path list elements from DexPathList.makeElements187     * @return188     *  A new or previously-opened DexFile.189     * @throws IOException190     *  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 {194195        /*196         * TODO: we may want to cache previously-opened DexFile objects.197         * The cache would be synchronized with close().  This would help198         * us avoid mapping the same DEX more than once when an app199         * decided to open it multiple times.  In practice this may not200         * 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 file115     * to hold the optimized data.116     *117     * @param sourceName118     *  Jar or APK file with "classes.dex".119     * @param outputName120     *  File that will hold the optimized form of the DEX data.121     * @param flags122     *  Enable optional features.123     * @param loader124     *  The class loader creating the DEX file object.125     * @param elements126     *  The temporary dex path list elements from DexPathList.makeElements127     */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 " + parent135                            + " 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 later140            }141        }142143        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                                     ? null357                                     : 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  }278279  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;284285  dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),286                                                               class_loader,287                                                               dex_elements,288                                                               /*out*/ &oat_file,289                                                               /*out*/ &error_msgs);290291  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 will306    // 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    }312313    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);403404  // Verify we aren't holding the mutator lock, which could starve GC if we405  // 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();409410  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 loaded412  // 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  }420421  OatFileAssistant oat_file_assistant(dex_location,422                                      kRuntimeISA,423                                      !runtime->IsAotCompiler(),424                                      only_use_system_oat_files_);425426  // Lock the target oat location to avoid races generating and loading the427  // 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 we431    // can generate an oat file anyway.432    VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;433  }434435  const OatFile* source_oat_file = nullptr;436437  if (!oat_file_assistant.IsUpToDate()) {438    // Update the oat file on disk if we can, based on the --compiler-filter439    // 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 compile442    // secondary dex files in isolation (and avoid to extract/verify the main apk443    // if it's in the class path). Note this trades correctness for performance444    // since the resulting slow down is unacceptable in some cases until b/64530081445    // is fixed.446    // We still pass the class loader context when the classpath string of the runtime447    // is not empty, which is the situation when ART is invoked standalone.448    ClassLoaderContext* actual_context = Runtime::Current()->GetClassPathString().empty()449        ? nullptr450        : 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;457458      case OatFileAssistant::kUpdateNotAttempted:459        // Avoid spamming the logs if we decided not to attempt making the oat460        // file up to date.461        VLOG(oat) << error_msg;462        break;463464      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) << ")";475476  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 it479    // 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 to488          // fallback to opening the existing oat file. This is potentially489          // unsafe so we warn about it.490          accept_oat_file = true;491492          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-fallback503        // was set, which means that we should never fallback. If we don't have original dex504        // files, we should just fail resolution as the flag intended.505        if (!oat_file_assistant.HasOriginalDexFiles()) {506          accept_oat_file = true;507        }508509        LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to "510                        " load classes for " << dex_location;511      }512513      LOG(WARNING) << error_msg;514    }515516    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  }522523  std::vector<std::unique_ptr<const DexFile>> dex_files;524525  // 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 the530      // 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 the546          // 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 get566            // freed.567            image_space.release();568569            // 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);592593      // 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  }607608  // Fall back to running out of the original dex file if we couldn't load any609  // 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  }633634  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";102103  if (zip_fd < 0) {104    CHECK_LE(oat_fd, 0) << "zip_fd must be provided with valid oat_fd. zip_fd=" << zip_fd105      << " oat_fd=" << oat_fd;106    CHECK_LE(vdex_fd, 0) << "zip_fd must be provided with valid vdex_fd. zip_fd=" << zip_fd107      << " vdex_fd=" << vdex_fd;;108  }109110  dex_location_.assign(dex_location);111112  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  }117118  // 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  }126127  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  }137138  // Check if the dex directory is writable.139  // This will be needed in most uses of OatFileAssistant and so it's OK to140  // compute it eagerly. (the only use which will not make use of it is141  // 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 ok147    // 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);868869  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  }874875  // TODO: The oat file assistant should be the definitive place for876  // determining the oat file name from the dex location, not877  // 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 slash276  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_等
【Android 原创】Android9.0脱壳时机点分析

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 accessing130  // any of the sections via a pointer.131  CHECK_ALIGNED(begin_, alignof(Header));132133  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 file89    uint32_t header_size_ = 0;  // offset to start of next section90    uint32_t endian_tag_ = 0;91    uint32_t link_size_ = 0;  // unused92    uint32_t link_off_ = 0;  // unused93    uint32_t map_off_ = 0;  // unused94    uint32_t string_ids_size_ = 0;  // number of StringIds95    uint32_t string_ids_off_ = 0;  // file offset of StringIds array96    uint32_t type_ids_size_ = 0;  // number of TypeIds, we don't support more than 6553597    uint32_t type_ids_off_ = 0;  // file offset of TypeIds array98    uint32_t proto_ids_size_ = 0;  // number of ProtoIds, we don't support more than 6553599    uint32_t proto_ids_off_ = 0;  // file offset of ProtoIds array100    uint32_t field_ids_size_ = 0;  // number of FieldIds101    uint32_t field_ids_off_ = 0;  // file offset of FieldIds array102    uint32_t method_ids_size_ = 0;  // number of MethodIds103    uint32_t method_ids_off_ = 0;  // file offset of MethodIds array104    uint32_t class_defs_size_ = 0;  // number of ClassDefs105    uint32_t class_defs_off_ = 0;  // file offset of ClassDef array106    uint32_t data_size_ = 0;  // size of data section107    uint32_t data_off_ = 0;  // file offset of data section108109    // Decode the dex magic version110    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}6667uint32_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}7172uint32_t DexFile::ChecksumMemoryRange(const uint8_t* begin, size_t size) {73  return adler32(adler32(0L, Z_NULL, 0), begin, size);74}7576int DexFile::GetPermissions() const {77  CHECK(container_.get() != nullptr);78  return container_->GetPermissions();79}8081bool DexFile::IsReadOnly() const {82  CHECK(container_.get() != nullptr);83  return container_->IsReadOnly();84}8586bool DexFile::EnableWrite() const {87  CHECK(container_.get() != nullptr);88  return container_->EnableWrite();89}9091bool 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);592593      // 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}350351bool 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  }363364  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));370371  // Load the rest of the multidex entries372  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    }379380    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

原文始发于微信公众号(吾爱破解论坛):【Android 原创】Android9.0脱壳时机点分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月5日19:53:44
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【Android 原创】Android9.0脱壳时机点分析http://cn-sec.com/archives/1159129.html

发表评论

匿名网友 填写信息