解析dex2oat的实现

Posted 小道安全

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解析dex2oat的实现相关的知识,希望对你有一定的参考价值。

简介

android系统5.0及以上系统开始逐渐丢弃Dalvik虚拟机,由于ART虚拟机对内存分配和回收都做了算法优化,降低了内存碎片化程度,回收时间也得以缩短,所有android系统5.0及以上都在主推ART虚拟机。在ART虚拟机中ART则会将Dex通过dex2oat工具编译得到一个ELF文件,它是一个可执行的文件。所以下面我们就针对ART的dex2oat实现进行做分析。

dex2oat介绍

Dex2oat的全称是:dalvik excutable file to optimized art file,它是一个对 android系统下的dex文件,进行编译优化的程序。通过dex2oat的编译优化,可以大大的提高android系统的启动的速度和使用手机过程的的流畅度。
dex2oat在安卓手机环境下的存放位置为/system/bin/dex2oat

dex2oat在开源系统中的路径为\\art\\dex2oat\\dex2oat.cc。

为什么要使用dex2oat进行转换

在android系统中,Android 虚拟机可以识别到的是dex文件,App应用在使用过程中如果每次将dex文件加载进行内存,解释性执行字节码,效率就会变得非常低, 从而影响到用户在使用安卓手机的体验。通过利用dex2oat进行优化处理, 那么可以在android系统运行之前,利用合适的时机将dex文件字节码,提前转化为虚拟机上可以执行运行的机器码,后续直接从效率更高的机器码中运行,则运行阶段更加流畅,优化用户体验。

dex2oat代码

1.dex2oat类定义


class Dex2Oat {
 public:
 
 //创建函数,返回值为bool,
  static bool Create(Dex2Oat** p_dex2oat,
                     const RuntimeOptions& runtime_options,
                     const CompilerOptions& compiler_options,
                     Compiler::Kind compiler_kind,
                     InstructionSet instruction_set,
                     InstructionSetFeatures instruction_set_features,
                     VerificationResults* verification_results,
                     DexFileToMethodInlinerMap* method_inliner_map,
                     size_t thread_count)
      SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
      //判断参数传递进来的释放为空
    CHECK(verification_results != nullptr);
    CHECK(method_inliner_map != nullptr);
    //用智能指针方式进行去实例化dex2oat
    std::unique_ptr<Dex2Oat> dex2oat(new Dex2Oat(&compiler_options,
                                           compiler_kind,
                                           instruction_set,
                                           instruction_set_features,
                                           verification_results,
                                           method_inliner_map,
                                           thread_count));
    if (!dex2oat->CreateRuntime(runtime_options, instruction_set)) {
      *p_dex2oat = nullptr;
      return false;
    }
    *p_dex2oat = dex2oat.release();
    return true;
  }
    //dex2oat的虚构函数,用于释放操作。
  ~Dex2Oat() {
    delete runtime_;
    LogCompletionTime();
  }

  void LogCompletionTime() {
    LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_)
              << " (threads: " << thread_count_ << ")";
  }


  
  //从文件上获取到类名称
  std::set<std::string>* ReadImageClassesFromFile(const char* image_classes_filename) {
    std::unique_ptr<std::ifstream> image_classes_file(new std::ifstream(image_classes_filename,
                                                                  std::ifstream::in));
    if (image_classes_file.get() == nullptr) {
      LOG(ERROR) << "Failed to open image classes file " << image_classes_filename;
      return nullptr;
    }
    std::unique_ptr<std::set<std::string>> result(ReadImageClasses(*image_classes_file));
    image_classes_file->close();
    return result.release();
  }
  
 //读取imageclasses
  std::set<std::string>* ReadImageClasses(std::istream& image_classes_stream) {
    std::unique_ptr<std::set<std::string>> image_classes(new std::set<std::string>);
    while (image_classes_stream.good()) {
      std::string dot;
      std::getline(image_classes_stream, dot);
      if (StartsWith(dot, "#") || dot.empty()) {
        continue;
      }
      std::string descriptor(DotToDescriptor(dot.c_str()));
      image_classes->insert(descriptor);
    }
    return image_classes.release();
  }

  // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;)
  //从zip文件(apk其实就是个zip文件)读取类名称,读取到返回一个描述
  std::set<std::string>* ReadImageClassesFromZip(const char* zip_filename,
                                                         const char* image_classes_filename,
                                                         std::string* error_msg) {
         //通过智能指针进行打开zip压缩包,也就是apk包             
    std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(zip_filename, error_msg));
    //判断打开是否失败
    if (zip_archive.get() == nullptr) {
      return nullptr;
    }
    //进行遍历zip包获取zip包里面的文件信息
    std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(image_classes_filename, error_msg));
    if (zip_entry.get() == nullptr) {
      *error_msg = StringPrintf("Failed to find '%s' within '%s': %s", image_classes_filename,
                                zip_filename, error_msg->c_str());
      return nullptr;
    }
    std::unique_ptr<MemMap> image_classes_file(zip_entry->ExtractToMemMap(zip_filename,
                                                                          image_classes_filename,
                                                                          error_msg));
    if (image_classes_file.get() == nullptr) {
      *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", image_classes_filename,
                                zip_filename, error_msg->c_str());
      return nullptr;
    }
    const std::string image_classes_string(reinterpret_cast<char*>(image_classes_file->Begin()),
                                           image_classes_file->Size());
    std::istringstream image_classes_stream(image_classes_string);
    return ReadImageClasses(image_classes_stream);
  }

  bool PatchOatCode(const CompilerDriver* compiler_driver, File* oat_file,
                    const std::string& oat_location, std::string* error_msg) {
    // We asked to include patch information but we are not making an image. We need to fix
    // everything up manually.
    std::unique_ptr<ElfFile> elf_file(ElfFile::Open(oat_file, PROT_READ|PROT_WRITE,
                                                    MAP_SHARED, error_msg));
    if (elf_file.get() == NULL) {
      LOG(ERROR) << error_msg;
      return false;
    }
    {
      ReaderMutexLock mu(Thread::Current(), *Locks::mutator_lock_);
      return ElfPatcher::Patch(compiler_driver, elf_file.get(), oat_location, error_msg);
    }
  }
    //创建一个oat文件,返回一个常量指针
  const CompilerDriver* CreateOatFile(const std::string& boot_image_option,
                                      const std::string& android_root,
                                      bool is_host,
                                      const std::vector<const DexFile*>& dex_files,
                                      File* oat_file,
                                      const std::string& oat_location,
                                      const std::string& bitcode_filename,
                                      bool image,
                                      std::unique_ptr<std::set<std::string>>& image_classes,
                                      bool dump_stats,
                                      bool dump_passes,
                                      TimingLogger& timings,
                                      CumulativeLogger& compiler_phases_timings,
                                      std::string profile_file,
                                      SafeMap<std::string, std::string>* key_value_store) {
    CHECK(key_value_store != nullptr);

    // Handle and ClassLoader creation needs to come after Runtime::Create
    jobject class_loader = nullptr;
    //获取自身进程
    Thread* self = Thread::Current();
    //如果boot_image_option不为空的话,执行下面的代码
    if (!boot_image_option.empty()) {
    
      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
      std::vector<const DexFile*> class_path_files(dex_files);
      OpenClassPathFiles(runtime_->GetClassPathString(), class_path_files);
      ScopedObjectAccess soa(self);
      //循环遍历并类文件大小,并进行dex文件进行注册
      for (size_t i = 0; i < class_path_files.size(); i++) {
        class_linker->RegisterDexFile(*class_path_files[i]);
      }
      soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader);
      ScopedLocalRef<jobject> class_loader_local(soa.Env(),
          soa.Env()->AllocObject(WellKnownClasses::dalvik_system_PathClassLoader));
      class_loader = soa.Env()->NewGlobalRef(class_loader_local.get());
      
      Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
    }


    std::unique_ptr<CompilerDriver> driver(new CompilerDriver(compiler_options_,
                                                              verification_results_,
                                                              method_inliner_map_,
                                                              compiler_kind_,
                                                              instruction_set_,
                                                              instruction_set_features_,
                                                              image,
                                                              image_classes.release(),
                                                              thread_count_,
                                                              dump_stats,
                                                              dump_passes,
                                                              &compiler_phases_timings,
                                                              profile_file));

    driver->GetCompiler()->SetBitcodeFileName(*driver.get(), bitcode_filename);

    driver->CompileAll(class_loader, dex_files, &timings);

    TimingLogger::ScopedTiming t2("dex2oat OatWriter", &timings);
    std::string image_file_location;
    uint32_t image_file_location_oat_checksum = 0;
    uintptr_t image_file_location_oat_data_begin = 0;
    int32_t image_patch_delta = 0;
    if (!driver->IsImage()) {
      TimingLogger::ScopedTiming t3("Loading image checksum", &timings);
      gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
      image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum();
      image_file_location_oat_data_begin =
          reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatDataBegin());
      image_file_location = image_space->GetImageFilename();
      image_patch_delta = image_space->GetImageHeader().GetPatchDelta();
    }

    if (!image_file_location.empty()) {
      key_value_store->Put(OatHeader::kImageLocationKey, image_file_location);
    }

    //oat写入操作
    OatWriter oat_writer(dex_files, image_file_location_oat_checksum,
                         image_file_location_oat_data_begin,
                         image_patch_delta,
                         driver.get(),
                         &timings,
                         key_value_store);

    t2.NewTiming("Writing ELF");
    if (!driver->WriteElf(android_root, is_host, dex_files, &oat_writer, oat_file)) {
      LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath();
      return nullptr;
    }

    // Flush result to disk. Patching code will re-open the file (mmap), so ensure that our view
    // of the file already made it there and won't be re-ordered with writes from PatchOat or
    // image patching.
    oat_file->Flush();

    if (!driver->IsImage() && driver->GetCompilerOptions().GetIncludePatchInformation()) {
      t2.NewTiming("Patching ELF");
      std::string error_msg;
      if (!PatchOatCode(driver.get(), oat_file, oat_location, &error_msg)) {
        LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath() << ": " << error_msg;
        return nullptr;
      }
    }

    return driver.release();
  }
   //创建一个映射文件,成功返回true,失败返回false
  bool CreateImageFile(const std::string& image_filename,
                       uintptr_t image_base,
                       const std::string& oat_filename,
                       const std::string& oat_location,
                       const CompilerDriver& compiler)
      LOCKS_EXCLUDED(Locks::mutator_lock_) {
    uintptr_t oat_data_begin;
    {
      // ImageWriter is scoped so it can free memory before doing FixupElf
      ImageWriter image_writer(compiler);
      if (!image_writer.Write(image_filename, image_base, oat_filename, oat_location)) {
        LOG(ERROR) << "Failed to create image file " << image_filename;
        return false;
      }
      oat_data_begin = image_writer.GetOatDataBegin();
    }

    std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
    if (oat_file.get() == nullptr) {
      PLOG(ERROR) << "Failed to open ELF file: " << oat_filename;
      return false;
    }
    if (!ElfFixup::Fixup(oat_file.get(), oat_data_begin)) {
      LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath();
      return false;
    }
    return true;
  }

 private:
 //定义一个显示的dex2oat构造函数
  explicit Dex2Oat(const CompilerOptions* compiler_options,
                   Compiler::Kind compiler_kind,
                   InstructionSet instruction_set,
                   InstructionSetFeatures instruction_set_features,
                   VerificationResults* verification_results,
                   DexFileToMethodInlinerMap* method_inliner_map,
                   size_t thread_count)
      : compiler_options_(compiler_options),
        compiler_kind_(compiler_kind),
        instruction_set_(instruction_set),
        instruction_set_features_(instruction_set_features),
        verification_results_(verification_results),
        method_inliner_map_(method_inliner_map),
        runtime_(nullptr),
        thread_count_(thread_count),
        start_ns_(NanoTime()) {
    CHECK(compiler_options != nullptr);
    CHECK(verification_results != nullptr);
    CHECK(method_inliner_map != nullptr);
  }

  bool CreateRuntime(const RuntimeOptions& runtime_options, InstructionSet instruction_set)
      SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
    if (!Runtime::Create(runtime_options, false)) {
      LOG(ERROR) << "Failed to create runtime";
      return false;
    }
    Runtime* runtime = Runtime::Current();
    runtime->SetInstructionSet(instruction_set)以上是关于解析dex2oat的实现的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向ART 脱壳 ( dex2oat 脱壳 | /art/dex2oat/dex2oat.cc#Dex2oat 函数源码 )

Android 逆向ART 脱壳 ( dex2oat 脱壳 | aosp 中搜索 dex2oat 源码 | dex2oat.cc#main 主函数源码 )

Android性能优化之Android 10+ dex2oat实践

Android 逆向ART 函数抽取加壳 ② ( 禁用 dex2oat 简介 | TurboDex 中禁用 dex2oat 参考示例 )

Android 逆向ART 函数抽取加壳 ④ ( 对 libc.so#execve 函数进行内联 HOOK 操作 )

dexopt 和 dex2oat 的区别?