首页
社区
课程
招聘
[原创]加壳脱壳知识点总结--Dex文件加载流程及脱壳实战
发表于: 2024-5-31 22:41 26441

[原创]加壳脱壳知识点总结--Dex文件加载流程及脱壳实战

2024-5-31 22:41
26441

总结记录下App启动流程中Dex动态加载流程以及相关知识,加深对App加壳以及脱壳原理的理解。
环境:Android 8.0.0

Zygote fork之后会在新进程中调用main()方法,然后 main() 方法会创建 ActivityThread 实例,并调用其 main() 方法,从而启动应用程序的主线程,这里也就是App相关的步骤,所以直接从ActiveThread开始说起。
ActivityThread 是 Android 系统中的一个核心类,负责管理应用程序的主线程以及应用程序中的各种组件(比如 Activity、Service、BroadcastReceiver 等)的生命周期、消息循环、消息处理等。

源码中这两句是比较关键的代码,第一句实例化ActivityThread类,之后调用attach方法,进行应用的初始化工作。

一些初始化工作可能依赖于系统的消息或事件的处理结果。例如,创建 Application 实例可能需要获取系统的一些状态或资源,所以在attach方法中初始化工作做好之后会进入消息循环等待,当系统发来消息就开始时调用handlebindApplication开始初始化。
这里分辨一下ActityThread.attach和handlebindApplication初始化的区别ActivityThread.attach() 主要负责初始化应用程序的运行环境和关键对象,而不是直接加载应用程序的组件(如 Activity)。handleBindApplication() 方法则更侧重于实际绑定应用程序,初始化应用程序的 Application、Activity、Service 等组件,并启动应用程序的主 Activity,所以说到了handlebindApplication这一步就App内的代码开始起作用了。
对于了解加壳脱壳来说handlebindApplication方法中的这两行代码比较关键,第一行最终会通过LoadApk类中的newApplication创建Application对象并调用attachBaseContext方法,第二行调用Application的OnCreate方法,这俩个方法也是App代码中最先运行的两个方法。

到这里App开始调用第一个Activity的OnCreate方法。

先介绍一下安卓中的四个类加载器

再说双亲委派,双亲委派是当一个类加载器收到加载类的请求时使用的机制,这里借用一张图说明。
https://cloud.tencent.com/developer/article/2251252
图片描述
总结就是两步,第一步当类加载器加载一个类时如果没有找到就会向上询问父类加载器是否加载过没有就继续向上直到BootClassLoader,这里到了第二步,如果BootClassLoader也没有加载过,BootClassLoader自己如果可以加载,则BootClassLoader会直接加载,如果不可以会一路向下执行这样的判断,直到最开始的子加载器。
这里要注意加载器的父子关心并不是类的继承父子关系。

整体加壳的实现,原Dex会被加密保存在某个地方,那一定有一个原Dex加解密的过程,那这个过程一定是在App自身逻辑代码运行之前,所以根据对App启动流程的分析原Dex的解密时机在Application的attachBaseContext方法,OnCreate方法调用的时候,壳代码一般会重写这两个方法,这里可以看一个例子,壳代码继承了Application并重写了两个方法。
图片描述
图片描述
除了加原Dex解密之外,还有一个重要的步骤就是加载解密后的Dex替换壳Dex。这一步就涉及Dex的加载以及双亲委派机制了。
一个App正常启动后加载Dex文件使用的是默认类加载器PathClassLoader,这个加载其不能用于加载外部Dex,通过之前的介绍,壳通常用的是DexClassLoader和InMemoryDexClassLoader来进行解密后的Dex加载,假设我们的代码是这样的。

这样运行后会报错,ClassNotFound,这里的话引出另一个问题这两个类加载的Dex,仅仅只是加载了一个普通的类进来,并没有生命周期,这样的话运行起来就会报错ClassNotFound,对于这个为什么没有生命周期这个问题,我找了一些startActivity调用流程的文章最终发现了可能的原因,以下这段话来自ChatGPT

也就是说startActivity方法调用后会执行performLaunchActivity方法。

这段代码主要调用newActivity创建一个Activity实例,当Activity 实例对象被添加到 Activity 栈中,并由系统管理时,生命周期就会正常触发。

再看一下newActivity的代码,使用了传入的ClassLoader来加载类
这里调用getClassLoader获取系统默认的PathClassLoader,所以传入的是PathClassLoader,PathClassLoader是DexClassLoader和InMemoryDexClassLoader的父加载器,所以按双亲委派的流程是会报错的。
这里解决方法有很多种,以为例DexClassLoader这里列三种(1)替换PathClassLoader(2)改变父子加载器关系把DexClassLoader改为PathClassLoader的父加载器。(3)合并PathClassLoader和DexClassLoader中的dexElements数组。

接下来是DexClassLoader和InMemoryDexClassLoader两种动态加载方式的源码分析这里就和脱壳有关了。

DexClassLoader

BaseDexClassLoader(String dexPath, File optimizedDirectory,String librarySearchPath, ClassLoader parent)

public DexPathList(ClassLoader definingContext, String dexPath,String librarySearchPath, File optimizedDirectory)

makeDexElements

loadDexFile(File file, File optimizedDirectory, ClassLoader loader,Element[] elements)

DexFile loadDex(String sourcePathName, String outputPathName,int flags, ClassLoader loader, DexPathList.Element[] elements)

DexFile(String sourceName, String outputName, int flags, ClassLoader loader,DexPathList.Element[] elements)

openDexFile(String sourceName, String outputName, int flags,
ClassLoader loader, DexPathList.Element[] elements)

这里进入ART中 openDexFileNative 函数

OatFileManager::OpenDexFilesFromOat,这里有一个Dex2Oat编译流程用于优化编译效率,但是由于oat优化会有.oat文件落地,通过oat2dex即可获得原Dex,所以大部分壳都会阻断Dex2Oat流程,这里就不跟进分析直接看dex_files.empty()之后的代码。

DexFile::Open

DexFile::OpenFile

DexFile::OpenCommon

流程总结:DexClassLoader->BaseDexClassLoader->DexPathList->makeDexElements->loadDexFile->loadDex->DexFile(String sourceName, ...)->openDexFile->(进入native)openDexFileNativc->OatFileManager::OpenDexFilesFromOat->DexFile::Open->DexFile::OpenFile->DexFile::OpenCommon->new Dexfile()

有两个重载,这里选第一个,用于加载一个dex。

这里调用了父类构造函数,跟进到父类BaseDexClassLoader中。

public DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles)

makeInMemoryDexElements(ByteBuffer[] dexFiles,
List<IOException> suppressedExceptions)

DexFile(ByteBuffer buf)
这个方法中可以看到出现了DexFile对象和一个叫做mCookie的属性,这也是整体加固脱壳的一个关键点。

openInMemoryDexFile(ByteBuffer buf)

使用了两个native函数,这里就跟进到到ART中了。
DexFile_createCookieWithDirectBuffer,这里已经可以看到很多有关Dex起始地址和大小的脱壳点。

CreateSingleDexFileCookie

CreateDexFile

DexFile::Open

OpenCommon

流程总结:InMemoryDexClassLoader->BaseDexClassLoader->DexPathList->makeInMemoryDexElements->DexFile(ByteBuffer buf)->openInMemoryDexFile->(进入native)DexFile_createCookieWithDirectBuffer->CreateSingleDexFileCookie->CreateDexFile->DexFile::Open->OpenCommon->new Dexfile()
到这里就结束了,最终是new了一个native层的DexFile对象并返回指向这个对象的指针,Java层的mCookie的值也就知道了,是指向Native层Dexfile对象的指针,在这个过程中可以看到有很多的脱壳点,重点是在于dex的起始地址和大小。

如果是修改内核源码的方式,脱壳点其实相当的多,这里方便演示使用frida来脱壳,先选一个frida的脱壳点,通过分析InMemoryDexClassLoader和DexClassLoader他们最终都会调用OpenCommon函数,所以我们选择这个,主要看脱壳代码。

简单说一下代码原理,首先通过遍历符号找到OpenCommon之后选择一个hook时机,这里选择lic的open函数当打开的路径包含.dex就开始hook并保存。
图片描述
这里有两个Dex,最大的那个就是。
原代码:
图片描述
脱壳后的代码,这里OnCreate被壳VMP保护,但是这一行System.loadLibrary("myapplication");这里我们是已经看到了
图片描述

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
 
        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);
 
        Environment.initForCurrentUser();
 
        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());
 
        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
 
        Process.setArgV0("<pre-initialized>");
 
        Looper.prepareMainLooper();
 
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
 
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
 
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
 
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
 
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
 
        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);
 
        Environment.initForCurrentUser();
 
        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());
 
        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
 
        Process.setArgV0("<pre-initialized>");
 
        Looper.prepareMainLooper();
 
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
 
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
 
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
 
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();
 
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
ActivityThread thread = new ActivityThread();
       thread.attach(false);
ActivityThread thread = new ActivityThread();
       thread.attach(false);
Application App = data.info.makeApplication(data.restrictedBackupMode, null);
 mInstrumentation.callApplicationOnCreate(App);
Application App = data.info.makeApplication(data.restrictedBackupMode, null);
 mInstrumentation.callApplicationOnCreate(App);
dexClassLoader = new DexClassLoader("/sdcard/4.dex", context.getApplicationContext().getCacheDir().getAbsolutePath(), null, pathClassloader);
      try {
          Class TestActivityClass = dexClassLoader.loadClass("com.kanxue.test02.TestActivity");
          Log.e("class", TestActivityClass.toString());
          context.startActivity(new Intent(context, TestActivityClass));
      } catch (ClassNotFoundException e) {
          e.printStackTrace();
      }
dexClassLoader = new DexClassLoader("/sdcard/4.dex", context.getApplicationContext().getCacheDir().getAbsolutePath(), null, pathClassloader);
      try {
          Class TestActivityClass = dexClassLoader.loadClass("com.kanxue.test02.TestActivity");
          Log.e("class", TestActivityClass.toString());
          context.startActivity(new Intent(context, TestActivityClass));
      } catch (ClassNotFoundException e) {
          e.printStackTrace();
      }
当你调用 startActivity() 启动一个 Activity 时,实际上是向系统发送了一个启动 Activity 的请求,系统会通过 ActivityThread 来处理这个请求。在 ActivityThread 中,当收到启动 Activity 的请求时,会调用 handleLaunchActivity() 方法来处理,而在 handleLaunchActivity() 方法中会最终调用 performLaunchActivity() 方法来执行 Activity 的启动流程
当你调用 startActivity() 启动一个 Activity 时,实际上是向系统发送了一个启动 Activity 的请求,系统会通过 ActivityThread 来处理这个请求。在 ActivityThread 中,当收到启动 Activity 的请求时,会调用 handleLaunchActivity() 方法来处理,而在 handleLaunchActivity() 方法中会最终调用 performLaunchActivity() 方法来执行 Activity 的启动流程
/frameworks/base/core/java/android/app/ActivityThread.java:
 performLaunchActivity()部分代码
 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
  try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
  }
  ...
  }
/frameworks/base/core/java/android/app/ActivityThread.java:
 performLaunchActivity()部分代码
 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
  try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
  }
  ...
  }
public Activity newActivity(ClassLoader cl, String className,
          Intent intent)
          throws InstantiationException, IllegalAccessException,
          ClassNotFoundException {
     return (Activity)cl.loadClass(className).newInstance();
  }
public Activity newActivity(ClassLoader cl, String className,
          Intent intent)
          throws InstantiationException, IllegalAccessException,
          ClassNotFoundException {
     return (Activity)cl.loadClass(className).newInstance();
  }
public DexClassLoader(String dexPath, String optimizedDirectory,
           String librarySearchPath, ClassLoader parent) {
       super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
   }
public DexClassLoader(String dexPath, String optimizedDirectory,
           String librarySearchPath, ClassLoader parent) {
       super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
   }
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
          String librarySearchPath, ClassLoader parent) {
      super(parent);
     this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
 
      if (reporter != null) {
          reporter.report(this.pathList.getDexPaths());
      }
  }
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
          String librarySearchPath, ClassLoader parent) {
      super(parent);
     this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
 
      if (reporter != null) {
          reporter.report(this.pathList.getDexPaths());
      }
  }
public DexPathList(ClassLoader definingContext, String dexPath,
           String librarySearchPath, File optimizedDirectory) {
...
     this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,                                           suppressedExceptions, definingContext);
...
}
public DexPathList(ClassLoader definingContext, String dexPath,
           String librarySearchPath, File optimizedDirectory) {
...
     this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,                                           suppressedExceptions, definingContext);
...
}
private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
309            List<IOException> suppressedExceptions, ClassLoader loader) {
310      Element[] elements = new Element[files.size()];
311      int elementsPos = 0;
312      /*
313       * Open all files and load the (direct or contained) dex files up front.
314       */
315      for (File file : files) {
316          if (file.isDirectory()) {
317              // We support directories for looking up resources. Looking up resources in
318              // directories is useful for running libcore tests.
319              elements[elementsPos++] = new Element(file);
320          } else if (file.isFile()) {
321              String name = file.getName();
322
323              if (name.endsWith(DEX_SUFFIX)) {
324                  // Raw dex file (not inside a zip/jar).
325                  try {
326                      DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);
...
}
private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
309            List<IOException> suppressedExceptions, ClassLoader loader) {
310      Element[] elements = new Element[files.size()];
311      int elementsPos = 0;
312      /*
313       * Open all files and load the (direct or contained) dex files up front.
314       */
315      for (File file : files) {
316          if (file.isDirectory()) {
317              // We support directories for looking up resources. Looking up resources in
318              // directories is useful for running libcore tests.
319              elements[elementsPos++] = new Element(file);
320          } else if (file.isFile()) {
321              String name = file.getName();
322
323              if (name.endsWith(DEX_SUFFIX)) {
324                  // Raw dex file (not inside a zip/jar).
325                  try {
326                      DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);
...
}
private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,Element[] elements)
           throws IOException {
      if (optimizedDirectory == null) {
           return new DexFile(file, loader, elements);
       } else {
           String optimizedPath = optimizedPathFor(file, optimizedDirectory);
           return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
       }
   }
private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader,Element[] elements)
           throws IOException {
      if (optimizedDirectory == null) {
           return new DexFile(file, loader, elements);
       } else {
           String optimizedPath = optimizedPathFor(file, optimizedDirectory);
           return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);
       }
   }
static DexFile loadDex(String sourcePathName, String outputPathName,
      int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
 
      return new DexFile(sourcePathName, outputPathName, flags, loader, elements);
  }
static DexFile loadDex(String sourcePathName, String outputPathName,
      int flags, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
 
      return new DexFile(sourcePathName, outputPathName, flags, loader, elements);
  }
  private DexFile(String sourceName, String outputName, int flags, ClassLoader loader,
            DexPathList.Element[] elements) throws IOException {
...
        mCookie = openDexFile(sourceName, outputName, flags, loader, elements);
        mInternalCookie = mCookie;
        mFileName = sourceName;
        //System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName);
    }
  private DexFile(String sourceName, String outputName, int flags, ClassLoader loader,
            DexPathList.Element[] elements) throws IOException {
...
        mCookie = openDexFile(sourceName, outputName, flags, loader, elements);
        mInternalCookie = mCookie;
        mFileName = sourceName;
        //System.out.println("DEX FILE cookie is " + mCookie + " sourceName=" + sourceName + " outputName=" + outputName);
    }
private static Object openDexFile(String sourceName, String outputName, int flags,
           ClassLoader loader, DexPathList.Element[] elements) throws IOException {
       // Use absolute paths to enable the use of relative paths when testing on host.
       return openDexFileNative(new File(sourceName).getAbsolutePath(),
                                (outputName == null)
                                    ? null
                                    : new File(outputName).getAbsolutePath(),
                                flags,
                                loader,
                                elements);
   }
private static Object openDexFile(String sourceName, String outputName, int flags,
           ClassLoader loader, DexPathList.Element[] elements) throws IOException {
       // Use absolute paths to enable the use of relative paths when testing on host.
       return openDexFileNative(new File(sourceName).getAbsolutePath(),
                                (outputName == null)
                                    ? null
                                    : new File(outputName).getAbsolutePath(),
                                flags,
                                loader,
                                elements);
   }
static jobject DexFile_openDexFileNative(JNIEnv* env,
                                         jclass,
                                         jstring javaSourceName,
                                         jstring javaOutputName ATTRIBUTE_UNUSED,
                                         jint flags ATTRIBUTE_UNUSED,
                                         jobject class_loader,
                                         jobjectArray dex_elements) {
  ScopedUtfChars sourceName(env, javaSourceName);
  if (sourceName.c_str() == nullptr) {
    return 0;
  }
 
  Runtime* const runtime = Runtime::Current();
  ClassLinker* linker = runtime->GetClassLinker();
  std::vector<std::unique_ptr<const DexFile>> dex_files;
  std::vector<std::string> error_msgs;
  const OatFile* oat_file = nullptr;
 
   dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),class_loader,dex_elements,/*out*/ &oat_file,/*out*/ &error_msgs);
  if (!dex_files.empty()) {
    jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
    if (array == nullptr) {
      ScopedObjectAccess soa(env);
      for (auto& dex_file : dex_files) {
        if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
          dex_file.release();
        }
      }
    }
    return array;
  } else {
    ScopedObjectAccess soa(env);
    CHECK(!error_msgs.empty());
    // The most important message is at the end. So set up nesting by going forward, which will
    // wrap the existing exception as a cause for the following one.
    auto it = error_msgs.begin();
    auto itEnd = error_msgs.end();
    for ( ; it != itEnd; ++it) {
      ThrowWrappedIOException("%s", it->c_str());
    }
 
    return nullptr;
  }
}
static jobject DexFile_openDexFileNative(JNIEnv* env,
                                         jclass,
                                         jstring javaSourceName,
                                         jstring javaOutputName ATTRIBUTE_UNUSED,
                                         jint flags ATTRIBUTE_UNUSED,
                                         jobject class_loader,
                                         jobjectArray dex_elements) {
  ScopedUtfChars sourceName(env, javaSourceName);
  if (sourceName.c_str() == nullptr) {
    return 0;
  }
 
  Runtime* const runtime = Runtime::Current();
  ClassLinker* linker = runtime->GetClassLinker();
  std::vector<std::unique_ptr<const DexFile>> dex_files;
  std::vector<std::string> error_msgs;
  const OatFile* oat_file = nullptr;
 
   dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),class_loader,dex_elements,/*out*/ &oat_file,/*out*/ &error_msgs);
  if (!dex_files.empty()) {
    jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
    if (array == nullptr) {
      ScopedObjectAccess soa(env);
      for (auto& dex_file : dex_files) {
        if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
          dex_file.release();
        }
      }
    }
    return array;
  } else {
    ScopedObjectAccess soa(env);
    CHECK(!error_msgs.empty());
    // The most important message is at the end. So set up nesting by going forward, which will
    // wrap the existing exception as a cause for the following one.
    auto it = error_msgs.begin();
    auto itEnd = error_msgs.end();
    for ( ; it != itEnd; ++it) {
      ThrowWrappedIOException("%s", it->c_str());
    }
 
    return nullptr;
  }
}
std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( const char* dex_location,
    jobject class_loader,
    jobjectArray dex_elements,
    const OatFile** out_oat_file,
    std::vector<std::string>* error_msgs) {
...
if (!oat_file_assistant.IsUpToDate()) {
    // Update the oat file on disk if we can, based on the --compiler-filter
    // option derived from the current runtime options.
    // This may fail, but that's okay. Best effort is all that matters here.
    switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/false, /*out*/ &error_msg)) {
      case OatFileAssistant::kUpdateFailed:
        LOG(WARNING) << error_msg;
        break;
 
      case OatFileAssistant::kUpdateNotAttempted:
        // Avoid spamming the logs if we decided not to attempt making the oat
        // file up to date.
        VLOG(oat) << error_msg;
        break;
 
      case OatFileAssistant::kUpdateSucceeded:
        // Nothing to do.
        break;
    }
  }
...
 if (dex_files.empty()) {
    if (oat_file_assistant.HasOriginalDexFiles()) {
      if (Runtime::Current()->IsDexFileFallbackEnabled()) {
        static constexpr bool kVerifyChecksum = true;
        if (!DexFile::Open(
            dex_location, dex_location, kVerifyChecksum, /*out*/ &error_msg, &dex_files)) {
          LOG(WARNING) << error_msg;
          error_msgs->push_back("Failed to open dex files from " + std::string(dex_location)
                                + " because: " + error_msg);
        }
      } else {
        error_msgs->push_back("Fallback mode disabled, skipping dex files.");
      }
    } else {
      error_msgs->push_back("No original dex files found for dex location "
          + std::string(dex_location));
    }
  }
 
}
std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( const char* dex_location,
    jobject class_loader,
    jobjectArray dex_elements,
    const OatFile** out_oat_file,
    std::vector<std::string>* error_msgs) {
...
if (!oat_file_assistant.IsUpToDate()) {
    // Update the oat file on disk if we can, based on the --compiler-filter
    // option derived from the current runtime options.
    // This may fail, but that's okay. Best effort is all that matters here.
    switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/false, /*out*/ &error_msg)) {
      case OatFileAssistant::kUpdateFailed:
        LOG(WARNING) << error_msg;
        break;
 
      case OatFileAssistant::kUpdateNotAttempted:
        // Avoid spamming the logs if we decided not to attempt making the oat
        // file up to date.
        VLOG(oat) << error_msg;
        break;
 
      case OatFileAssistant::kUpdateSucceeded:
        // Nothing to do.
        break;
    }
  }
...
 if (dex_files.empty()) {
    if (oat_file_assistant.HasOriginalDexFiles()) {
      if (Runtime::Current()->IsDexFileFallbackEnabled()) {
        static constexpr bool kVerifyChecksum = true;
        if (!DexFile::Open(
            dex_location, dex_location, kVerifyChecksum, /*out*/ &error_msg, &dex_files)) {
          LOG(WARNING) << error_msg;
          error_msgs->push_back("Failed to open dex files from " + std::string(dex_location)
                                + " because: " + error_msg);
        }
      } else {
        error_msgs->push_back("Fallback mode disabled, skipping dex files.");
      }
    } else {
      error_msgs->push_back("No original dex files found for dex location "
          + std::string(dex_location));
    }
  }
 
}
bool DexFile::Open(const char* filename,
                   const std::string& location,
                   bool verify_checksum,
                   std::string* error_msg,
                   std::vector<std::unique_ptr<const DexFile>>* dex_files) {
  ScopedTrace trace(std::string("Open dex file ") + std::string(location));
  DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
  uint32_t magic;
  File fd = OpenAndReadMagic(filename, &magic, error_msg);
  if (fd.Fd() == -1) {
    DCHECK(!error_msg->empty());
    return false;
  }
  if (IsZipMagic(magic)) {
    return DexFile::OpenZip(fd.Release(), location, verify_checksum, error_msg, dex_files);
  }
  if (IsDexMagic(magic)) {
    std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.Release(),
                                                              location,
                                                              /* verify */ true,
                                                              verify_checksum,
                                                              error_msg));
    if (dex_file.get() != nullptr) {
      dex_files->push_back(std::move(dex_file));
      return true;
    } else {
      return false;
    }
  }
  *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
  return false;
}
bool DexFile::Open(const char* filename,
                   const std::string& location,
                   bool verify_checksum,
                   std::string* error_msg,
                   std::vector<std::unique_ptr<const DexFile>>* dex_files) {
  ScopedTrace trace(std::string("Open dex file ") + std::string(location));
  DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
  uint32_t magic;
  File fd = OpenAndReadMagic(filename, &magic, error_msg);
  if (fd.Fd() == -1) {
    DCHECK(!error_msg->empty());
    return false;
  }
  if (IsZipMagic(magic)) {
    return DexFile::OpenZip(fd.Release(), location, verify_checksum, error_msg, dex_files);
  }
  if (IsDexMagic(magic)) {
    std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.Release(),
                                                              location,
                                                              /* verify */ true,
                                                              verify_checksum,
                                                              error_msg));
    if (dex_file.get() != nullptr) {
      dex_files->push_back(std::move(dex_file));
      return true;
    } else {
      return false;
    }
  }
  *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
  return false;
}
2std::unique_ptr<const DexFile> DexFile::OpenFile(int fd,
                                                 const std::string& location,
                                                 bool verify,
                                                 bool verify_checksum,
                                                 std::string* error_msg) {
  ScopedTrace trace(std::string("Open dex file ") + std::string(location));
  CHECK(!location.empty());
  std::unique_ptr<MemMap> map;
  {
    File delayed_close(fd, /* check_usage */ false);
    struct stat sbuf;
    memset(&sbuf, 0, sizeof(sbuf));
    if (fstat(fd, &sbuf) == -1) {
      *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(),
                                strerror(errno));
      return nullptr;
    }
    if (S_ISDIR(sbuf.st_mode)) {
      *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
      return nullptr;
    }
    size_t length = sbuf.st_size;
    map.reset(MemMap::MapFile(length,
                              PROT_READ,
                              MAP_PRIVATE,
                              fd,
                              0,
                              /*low_4gb*/false,
                              location.c_str(),
                              error_msg));
    if (map == nullptr) {
      DCHECK(!error_msg->empty());
      return nullptr;
    }
  }
 
  if (map->Size() < sizeof(DexFile::Header)) {
    *error_msg = StringPrintf(
        "DexFile: failed to open dex file '%s' that is too short to have a header",
        location.c_str());
    return nullptr;
  }
 
  const Header* dex_header = reinterpret_cast<const Header*>(map->Begin());
 
  std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
                                                 map->Size(),
                                                 location,
                                                 dex_header->checksum_,
                                                 kNoOatDexFile,
                                                 verify,
                                                 verify_checksum,
                                                 error_msg);
  if (dex_file != nullptr) {
    dex_file->mem_map_.reset(map.release());
  }
 
  return dex_file;
}
2std::unique_ptr<const DexFile> DexFile::OpenFile(int fd,
                                                 const std::string& location,
                                                 bool verify,
                                                 bool verify_checksum,
                                                 std::string* error_msg) {
  ScopedTrace trace(std::string("Open dex file ") + std::string(location));
  CHECK(!location.empty());
  std::unique_ptr<MemMap> map;
  {
    File delayed_close(fd, /* check_usage */ false);
    struct stat sbuf;
    memset(&sbuf, 0, sizeof(sbuf));
    if (fstat(fd, &sbuf) == -1) {
      *error_msg = StringPrintf("DexFile: fstat '%s' failed: %s", location.c_str(),
                                strerror(errno));
      return nullptr;
    }
    if (S_ISDIR(sbuf.st_mode)) {
      *error_msg = StringPrintf("Attempt to mmap directory '%s'", location.c_str());
      return nullptr;
    }

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

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