天天看點

【Android 逆向】ART 脫殼 ( dex2oat 脫殼 | /art/dex2oat/dex2oat.cc#Dex2oat 函數源碼 )

文章目錄

  • 前言
  • 一、/art/dex2oat/dex2oat.cc#Dex2oat 函數源碼
  • 二、/art/dex2oat/dex2oat.cc#Setup 函數源碼 ( 脫殼點 )

前言

在上一篇部落格 【Android 逆向】ART 脫殼 ( dex2oat 脫殼 | aosp 中搜尋 dex2oat 源碼 | dex2oat.cc#main 主函數源碼 )​ 中 , 分析到 dex2oat 工具源碼中的主函數為 /art/dex2oat/dex2oat.cc#main​ , 在該函數中調用了 /art/dex2oat/dex2oat.cc#Dex2oat 函數 ;

在将 dex 檔案編譯為 oat 檔案的過程中 , 隻要出現了 DexFile 對象 , 就可以将該對象對應的 dex 檔案導出 , 即 dex 脫殼 , 該過程的脫殼點很多 ;

脫殼方法參考 【Android 逆向】ART 脫殼 ( 修改 /art/runtime/dex_file.cc#OpenCommon 系統源碼進行脫殼 ) 部落格 , 在脫殼點添加将記憶體中的 dex 檔案 dump 到本地 SD 卡中的源碼 , 然後在編譯好的系統中運作要脫殼的應用 , 即可完成脫殼操作 ;

一、/art/dex2oat/dex2oat.cc#Dex2oat 函數源碼

在 /art/dex2oat/dex2oat.cc#Dex2oat 函數中 , 調用了 /art/dex2oat/dex2oat.cc#Setup 函數 , 其中就周遊了 DexFile 對象 , 在周遊時可以将記憶體中的 dex 資料 dump 到 SD 卡中 ;

在調用的 /art/dex2oat/dex2oat.cc#CompileApp 函數中 , 也有脫殼點 ;

​/art/dex2oat/dex2oat.cc#Dex2oat 函數源碼 :​

static dex2oat::ReturnCode Dex2oat(int argc, char** argv) {
  b13564922();

  TimingLogger timings("compiler", false, false);

  // 在堆上而不是堆棧上配置設定'dex2oat',如Clang
  // 可能産生的堆棧幀對于此函數或
  // 将其内聯的函數(如main),這些函數不适合
  // “-Wframe大于”選項的要求。
  std::unique_ptr<Dex2Oat> dex2oat = MakeUnique<Dex2Oat>(&timings);

  // 解析參數。參數錯誤将導緻UsageError中的exit(exit_失敗)。
  dex2oat->ParseArgs(argc, argv);

  // 如果需要,處理概要檔案資訊以進行概要檔案引導編譯。
  // 此操作涉及I/O。
  if (dex2oat->UseProfile()) {
    if (!dex2oat->LoadProfile()) {
      LOG(ERROR) << "Failed to process profile file";
      return dex2oat::ReturnCode::kOther;
    }
  }

  art::MemMap::Init();  // For ZipEntry::ExtractToMemMap, and vdex.

  // 盡早檢查編譯結果是否可以寫入
  if (!dex2oat->OpenFile()) {
    return dex2oat::ReturnCode::kOther;
  }

  // 當以下任一項為真時,列印整行:
  //   1)調試生成
  //   2)編譯圖像
  //   3)使用--host編譯
  //   4)在主機上編譯(不是目标版本)
  // 否則,列印剝離的指令行。
  if (kIsDebugBuild || dex2oat->IsBootImage() || dex2oat->IsHost() || !kIsTargetBuild) {
    LOG(INFO) << CommandLine();
  } else {
    LOG(INFO) << StrippedCommandLine();
  }

  // 核心跳轉 
  dex2oat::ReturnCode setup_code = dex2oat->Setup();
  if (setup_code != dex2oat::ReturnCode::kNoFailure) {
    dex2oat->EraseOutputFiles();
    return setup_code;
  }

  // 幫助在裝置上調試。可用于确定哪個dalvikvm執行個體調用了dex2oat
  // 例如。由工具/對分搜尋/對分搜尋使用。皮耶。
  VLOG(compiler) << "Running dex2oat (parent PID = " << getppid() << ")";

  dex2oat::ReturnCode result;
  if (dex2oat->IsImage()) {
    result = CompileImage(*dex2oat);
  } else {
    result = CompileApp(*dex2oat);
  }

  dex2oat->Shutdown();
  return result;
}      

​源碼路徑 :​ /art/dex2oat/dex2oat.cc#Dex2oat

二、/art/dex2oat/dex2oat.cc#Setup 函數源碼 ( 脫殼點 )

在 /art/dex2oat/dex2oat.cc#Setup 函數的最後位置 , 逐個周遊 dex 檔案 , 此時是可以拿到 dex_file 直接導出 dex 檔案資料到 SD 卡中 , 此處可以進行脫殼 ;

隻要出現了 DexFile 執行個體對象 , 就可以進行脫殼操作 ;

​/art/dex2oat/dex2oat.cc#Setup 函數源碼 :​

// 確定dex緩存保持活動狀态,因為我們不希望在編譯期間發生類解除安裝。
  for (const auto& dex_file : dex_files_) {
    ScopedObjectAccess soa(self);
    dex_caches_.push_back(soa.AddLocalReference<jobject>(
        class_linker->RegisterDexFile(*dex_file,
                                      soa.Decode<mirror::ClassLoader>(class_loader_).Ptr())));
    if (dex_caches_.back() == nullptr) {
      soa.Self()->AssertPendingException();
      soa.Self()->ClearException();
      PLOG(ERROR) << "Failed to register dex file.";
      return dex2oat::ReturnCode::kOther;
    }
    // 預注冊dex檔案,以便在編譯和驗證期間無需鎖定即可通路驗證結果。
    verification_results_->AddDexFile(dex_file);
  }      

​/art/dex2oat/dex2oat.cc#Setup 函數源碼 :​

// 設定編譯環境。包括啟動運作時和加載/打開引導類路徑。
dex2oat::ReturnCode Setup() {
  TimingLogger::ScopedTiming t("dex2oat Setup", timings_);

  if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
    return dex2oat::ReturnCode::kOther;
  }

  verification_results_.reset(new VerificationResults(compiler_options_.get()));
  callbacks_.reset(new QuickCompilerCallbacks(
      verification_results_.get(),
      IsBootImage() ?
          CompilerCallbacks::CallbackMode::kCompileBootImage :
          CompilerCallbacks::CallbackMode::kCompileApp));

  RuntimeArgumentMap runtime_options;
  if (!PrepareRuntimeOptions(&runtime_options)) {
    return dex2oat::ReturnCode::kOther;
  }

  CreateOatWriters();
  if (!AddDexFileSources()) {
    return dex2oat::ReturnCode::kOther;
  }

  if (IsBootImage() && image_filenames_.size() > 1) {
  // 如果我們正在編譯引導映像,請将引導類路徑存儲到鍵值存儲中。
  // 我們需要這個多圖像的情況。
    key_value_store_->Put(OatHeader::kBootClassPathKey,
                          gc::space::ImageSpace::GetMultiImageBootClassPath(dex_locations_,
                                                                            oat_filenames_,
                                                                            image_filenames_));
  }

  if (!IsBootImage()) {
    // 編譯應用程式時,盡早建立運作時以檢索oat頭所需的映像位置鍵。
    if (!CreateRuntime(std::move(runtime_options))) {
      return dex2oat::ReturnCode::kCreateRuntime;
    }

    if (CompilerFilter::DependsOnImageChecksum(compiler_options_->GetCompilerFilter())) {
      TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
      std::vector<gc::space::ImageSpace*> image_spaces =
          Runtime::Current()->GetHeap()->GetBootImageSpaces();
      image_file_location_oat_checksum_ = image_spaces[0]->GetImageHeader().GetOatChecksum();
      image_file_location_oat_data_begin_ =
          reinterpret_cast<uintptr_t>(image_spaces[0]->GetImageHeader().GetOatDataBegin());
      image_patch_delta_ = image_spaces[0]->GetImageHeader().GetPatchDelta();
      // Store the boot image filename(s).
      std::vector<std::string> image_filenames;
      for (const gc::space::ImageSpace* image_space : image_spaces) {
        image_filenames.push_back(image_space->GetImageFilename());
      }
      std::string image_file_location = android::base::Join(image_filenames, ':');
      if (!image_file_location.empty()) {
        key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location);
      }
    } else {
      image_file_location_oat_checksum_ = 0u;
      image_file_location_oat_data_begin_ = 0u;
      image_patch_delta_ = 0;
    }

    // Open dex files for class path.
    std::vector<std::string> class_path_locations =
        GetClassPathLocations(runtime_->GetClassPathString());
    OpenClassPathFiles(class_path_locations,
                       &class_path_files_,
                       &opened_oat_files_,
                       runtime_->GetInstructionSet(),
                       classpath_dir_);

    // Store the classpath we have right now.
    std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
    std::string encoded_class_path;
    if (class_path_locations.size() == 1 &&
        class_path_locations[0] == OatFile::kSpecialSharedLibrary) {
      // When passing the special shared library as the classpath, it is the only path.
      encoded_class_path = OatFile::kSpecialSharedLibrary;
    } else {
      encoded_class_path = OatFile::EncodeDexFileDependencies(class_path_files, classpath_dir_);
    }
    key_value_store_->Put(OatHeader::kClassPathKey, encoded_class_path);
  }

  // 現在我們已經完成了key\u value\u store\u,開始編寫oat檔案。
  {
    TimingLogger::ScopedTiming t_dex("Writing and opening dex files", timings_);
    rodata_.reserve(oat_writers_.size());
    for (size_t i = 0, size = oat_writers_.size(); i != size; ++i) {
      rodata_.push_back(elf_writers_[i]->StartRoData());
      // 直接将dex檔案解壓或複制到oat檔案。
      std::unique_ptr<MemMap> opened_dex_files_map;
      std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
    // 無需驗證以下各項的dex檔案:
    // 1)Dexlayout,因為它進行了驗證。也可能無法通過驗證,因為
    // 我們不更新dex校驗和。
    // 2)當我們有一個vdex檔案,這意味着它已經被驗證。
      const bool verify = !DoDexLayoutOptimizations() && (input_vdex_file_ == nullptr);
      if (!oat_writers_[i]->WriteAndOpenDexFiles(
          kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(),
          rodata_.back(),
          instruction_set_,
          instruction_set_features_.get(),
          key_value_store_.get(),
          verify,
          update_input_vdex_,
          &opened_dex_files_map,
          &opened_dex_files)) {
        return dex2oat::ReturnCode::kOther;
      }
      dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files));
      if (opened_dex_files_map != nullptr) {
        opened_dex_files_maps_.push_back(std::move(opened_dex_files_map));
        for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files) {
          dex_file_oat_index_map_.emplace(dex_file.get(), i);
          opened_dex_files_.push_back(std::move(dex_file));
        }
      } else {
        DCHECK(opened_dex_files.empty());
      }
    }
  }

  dex_files_ = MakeNonOwningPointerVector(opened_dex_files_);

  // 我們不得不将互換決定推遲到現在,因為這是我們真正需要的時候
  // 了解我們将要使用的dex檔案。

  // 確定我們還沒有建立驅動程式。
  CHECK(driver_ == nullptr);
  // 如果我們使用交換檔案,請確定我們高于門檻值以使其成為必要。
  if (swap_fd_ != -1) {
    if (!UseSwap(IsBootImage(), dex_files_)) {
      close(swap_fd_);
      swap_fd_ = -1;
      VLOG(compiler) << "Decided to run without swap.";
    } else {
      LOG(INFO) << "Large app, accepted running with swap.";
    }
  }
  // 請注意,dex2oat不會關閉 swap_fd_。編譯器驅動程式的交換空間可以做到這一點。

  // 如果由于大小原因需要降級編譯器篩選器,請立即執行該檢查。
  if (!IsBootImage() && IsVeryLarge(dex_files_)) {
    if (!CompilerFilter::IsAsGoodAs(CompilerFilter::kExtract,
                                    compiler_options_->GetCompilerFilter())) {
      LOG(INFO) << "Very large app, downgrading to extract.";
    // 注意:這個更改不會反映在鍵值存儲中,這是必須的
    // 在加載dex檔案之前完成。目前需要此設定
    // 從DexFile對象擷取大小。
    // TODO:重構。b/29790079
      compiler_options_->SetCompilerFilter(CompilerFilter::kExtract);
    }
  }

  if (IsBootImage()) {
    // 對于啟動映像,将打開的dex檔案傳遞給運作時::Create()。
    // 注意:運作時獲得這些dex檔案的所有權。
    runtime_options.Set(RuntimeArgumentMap::BootClassPathDexList, &opened_dex_files_);
    if (!CreateRuntime(std::move(runtime_options))) {
      return dex2oat::ReturnCode::kOther;
    }
  }

  // 如果我們正在處理映像,請重寫編譯器過濾器以強制進行完整編譯。必須是
  // 在導緻驗證的WellKnownClasses::Init之前完成。注意:不強制
  // 類初始值設定項的編譯。
  // 當我們在本機中時,請抓住機會初始化衆所周知的類。
  Thread* self = Thread::Current();
  WellKnownClasses::Init(self->GetJniEnv());

  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
  if (!IsBootImage()) {
    constexpr bool kSaveDexInput = false;
    if (kSaveDexInput) {
      SaveDexInput();
    }

    // 句柄和類加載器的建立需要在Runtime::Create之後進行。
    ScopedObjectAccess soa(self);

    // 類路徑:首先是給定的類路徑。
    std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);

    // 然後我們将編譯dex檔案。是以,我們将首先解析類路徑。
    class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end());

    class_loader_ = class_linker->CreatePathClassLoader(self, class_path_files);
  }

  // 確定打開的dex檔案對于dex到dex轉換是可寫的。
  for (const std::unique_ptr<MemMap>& map : opened_dex_files_maps_) {
    if (!map->Protect(PROT_READ | PROT_WRITE)) {
      PLOG(ERROR) << "Failed to make .dex files writeable.";
      return dex2oat::ReturnCode::kOther;
    }
  }

  // 確定dex緩存保持活動狀态,因為我們不希望在編譯期間發生類解除安裝。
  for (const auto& dex_file : dex_files_) {
    ScopedObjectAccess soa(self);
    dex_caches_.push_back(soa.AddLocalReference<jobject>(
        class_linker->RegisterDexFile(*dex_file,
                                      soa.Decode<mirror::ClassLoader>(class_loader_).Ptr())));
    if (dex_caches_.back() == nullptr) {
      soa.Self()->AssertPendingException();
      soa.Self()->ClearException();
      PLOG(ERROR) << "Failed to register dex file.";
      return dex2oat::ReturnCode::kOther;
    }
    // 預注冊dex檔案,以便在編譯和驗證期間無需鎖定即可通路驗證結果。
    verification_results_->AddDexFile(dex_file);
  }

  return dex2oat::ReturnCode::kNoFailure;
}