文章目录
前言一、/art/dex2oat/#Dex2oat 函数源码二、/art/dex2oat/#Setup 函数源码 ( 脱壳点 )前言
在上一篇博客 【Android 逆向】ART 脱壳 ( dex2oat 脱壳 | aosp 中搜索 dex2oat 源码 | #main 主函数源码 ) 中 , 分析到 dex2oat 工具源码中的主函数为 /art/dex2oat/#main , 在该函数中调用了 /art/dex2oat/#Dex2oat 函数 ;
在将 dex 文件编译为 oat 文件的过程中 , 只要出现了 DexFile 对象 , 就可以将该对象对应的 dex 文件导出 , 即 dex 脱壳 , 该过程的脱壳点很多 ;
脱壳方法参考 【Android 逆向】ART 脱壳 ( 修改 /art/runtime/#OpenCommon 系统源码进行脱壳 ) 博客 , 在脱壳点添加将内存中的 dex 文件 dump 到本地 SD 卡中的源码 , 然后在编译好的系统中运行要脱壳的应用 , 即可完成脱壳操作 ;
一、/art/dex2oat/#Dex2oat 函数源码
在 /art/dex2oat/#Dex2oat 函数中 , 调用了 /art/dex2oat/#Setup 函数 , 其中就遍历了 DexFile 对象 , 在遍历时可以将内存中的 dex 数据 dump 到 SD 卡中 ;
在调用的 /art/dex2oat/#CompileApp 函数中 , 也有脱壳点 ;
/art/dex2oat/#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
二、/art/dex2oat/#Setup 函数源码 ( 脱壳点 )
在 /art/dex2oat/#Setup 函数的最后位置 , 逐个遍历 dex 文件 , 此时是可以拿到 dex_file 直接导出 dex 文件数据到 SD 卡中 , 此处可以进行脱壳 ;
只要出现了DexFile
实例对象 , 就可以进行脱壳操作 ;
/art/dex2oat/#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/#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/29790079compiler_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;}
源码路径 :/art/dex2oat/#Setup
如果觉得《【Android 逆向】ART 脱壳 ( dex2oat 脱壳 | /art/dex2oat/dex2oat.cc#Dex2oat 函数源码 )》对你有帮助,请点赞、收藏,并留下你的观点哦!