文章目录

  • 前言
  • 一、dalvik_system_DexFile.cc#DexFile_openDexFileNative 函数分析
  • 二、oat_file_manager.cc#OpenDexFilesFromOat 函数分析
  • 三、oat_file_assistant.cc#MakeUpToDate 函数分析
  • 四、oat_file_assistant.cc#GenerateOatFileNoChecks 函数分析
  • 五、oat_file_assistant.cc#Dex2Oat 函数分析

前言


在上一篇博客 【Android 逆向】ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 ) 中 , 分析了 ART 虚拟机下 DexClassLoader 类加载器加载 dex 文件的 Java 层流程 , 与 Dalvik 虚拟机下基本一致 , 从 native 层开始不一致 , 本篇博客开始分析 native 层的类加载流程 ;

一、dalvik_system_DexFile.cc#DexFile_openDexFileNative 函数分析


在下面的 DexFile_openDexFileNative 方法中 ,

jstring javaSourceName 参数是要加载的 dex 文件路径 ,

下面调用的 OpenDexFilesFromOat 函数 , 是生成 oat 文件的关键流程入口 ;

  dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),class_loader,dex_elements,/*out*/ &oat_file,/*out*/ &error_msgs);

dalvik_system_DexFile.cc#DexFile_openDexFileNative 源码 :

// TODO(calin): clean up the unused parameters (here and in libcore).
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;// OAT 文件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;}
}

源码路径 : /art/runtime/native/dalvik_system_DexFile.cc#DexFile_openDexFileNative

二、oat_file_manager.cc#OpenDexFilesFromOat 函数分析


先声明 oat 文件对象 ,

  // 声明 OatFile 对象const OatFile* source_oat_file = nullptr;

然后判断是否有生成 oat 文件 , 如果是第一次调用 , 肯定没有生成 oat 文件 ,

!oat_file_assistant.IsUpToDate()

如果没有生成 oat 文件 , 则执行

oat_file_assistant.MakeUpToDate(/*profile_changed*/false, /*out*/ &error_msg)

方法 , 开始生成 oat 文件 ;

oat_file_manager.cc#OpenDexFilesFromOat 函数源码 :

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) {ScopedTrace trace(__FUNCTION__);CHECK(dex_location != nullptr);CHECK(error_msgs != nullptr);// 验证我们没有持有mutator锁,如果我们必须生成或重新定位oat文件,这可能会导致GC饥饿。Thread* const self = Thread::Current();Locks::mutator_lock_->AssertNotHeld(self);Runtime* const runtime = Runtime::Current();OatFileAssistant oat_file_assistant(dex_location,kRuntimeISA,!runtime->IsAotCompiler());// 锁定目标oat位置以避免生成和加载oat文件。std::string error_msg;if (!oat_file_assistant.Lock(/*out*/&error_msg)) {// Don't worry too much if this fails. If it does fail, it's unlikely we// can generate an oat file anyway.VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;}// 声明 OatFile 对象const OatFile* source_oat_file = nullptr;// 判断是否有生成 oat 文件 , 如果是第一次调用 , 肯定没有生成 oat 文件if (!oat_file_assistant.IsUpToDate()) {// 如果可以,根据从当前运行时选项派生的--compiler-filter选项更新磁盘上的oat文件// 这可能会失败,但没关系。这里最重要的是尽最大努力。// ★ 核心跳转 switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/false, /*out*/ &error_msg)) {case OatFileAssistant::kUpdateFailed:LOG(WARNING) << error_msg;break;case OatFileAssistant::kUpdateNotAttempted:// 如果我们决定不尝试更新oat文件,请避免滥发日志。VLOG(oat) << error_msg;break;case OatFileAssistant::kUpdateSucceeded:// Nothing to do.break;}}// Get the oat file on disk.std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());if (oat_file != nullptr) {// 仅当文件没有冲突时才获取该文件,否则我们必须获取该文件,因为存在预选项。bool accept_oat_file =!HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg);if (!accept_oat_file) {// 冲突检查失败。打印警告。if (Runtime::Current()->IsDexFileFallbackEnabled()) {if (!oat_file_assistant.HasOriginalDexFiles()) {// 我们需要回退,但没有原始的dex文件。// 我们必须回退到打开现有的oat文件。// 这可能不安全,因此我们对此发出警告。accept_oat_file = true;LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. "<< "Allow oat file use. This is potentially dangerous.";} else {// 我们必须回退并找到原始的dex文件-从APK中提取它们。// 同时警告此操作,因为它可能会造成浪费。LOG(WARNING) << "Found duplicate classes, falling back to extracting from APK : "<< dex_location;LOG(WARNING) << "NOTE: This wastes RAM and hurts startup performance.";}} else {// TODO: 我们应该删除这个。我们在这里的事实意味着没有设置-Xno dex文件回退,// 这意味着我们永远不应该回退。// 如果我们没有原始的dex文件,我们应该按照标志的意图失败解析。if (!oat_file_assistant.HasOriginalDexFiles()) {accept_oat_file = true;}LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to "" load classes for " << dex_location;}LOG(WARNING) << error_msg;}if (accept_oat_file) {VLOG(class_linker) << "Registering " << oat_file->GetLocation();source_oat_file = RegisterOatFile(std::move(oat_file));*out_oat_file = source_oat_file;}}std::vector<std::unique_ptr<const DexFile>> dex_files;// Load the dex files from the oat file.if (source_oat_file != nullptr) {bool added_image_space = false;if (source_oat_file->IsExecutable()) {std::unique_ptr<gc::space::ImageSpace> image_space =kEnableAppImage ? oat_file_assistant.OpenImageSpace(source_oat_file) : nullptr;if (image_space != nullptr) {ScopedObjectAccess soa(self);StackHandleScope<1> hs(self);Handle<mirror::ClassLoader> h_loader(hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));// Can not load app image without class loader.if (h_loader != nullptr) {std::string temp_error_msg;// Add image space has a race condition since other threads could be reading from the// spaces array.{ScopedThreadSuspension sts(self, kSuspended);gc::ScopedGCCriticalSection gcs(self,gc::kGcCauseAddRemoveAppImageSpace,gc::kCollectorTypeAddRemoveAppImageSpace);ScopedSuspendAll ssa("Add image space");runtime->GetHeap()->AddSpace(image_space.get());}{ScopedTrace trace2(StringPrintf("Adding image space for location %s", dex_location));added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(),h_loader,dex_elements,dex_location,/*out*/&dex_files,/*out*/&temp_error_msg);}if (added_image_space) {// Successfully added image space to heap, release the map so that it does not get// freed.image_space.release();} else {LOG(INFO) << "Failed to add image file " << temp_error_msg;dex_files.clear();{ScopedThreadSuspension sts(self, kSuspended);gc::ScopedGCCriticalSection gcs(self,gc::kGcCauseAddRemoveAppImageSpace,gc::kCollectorTypeAddRemoveAppImageSpace);ScopedSuspendAll ssa("Remove image space");runtime->GetHeap()->RemoveSpace(image_space.get());}// Non-fatal, don't update error_msg.}}}}if (!added_image_space) {DCHECK(dex_files.empty());dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);}if (dex_files.empty()) {error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation());}}// Fall back to running out of the original dex file if we couldn't load any// dex_files from the oat file.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));}}return dex_files;
}

源码路径 : /art/runtime/oat_file_manager.cc#OpenDexFilesFromOat

三、oat_file_assistant.cc#MakeUpToDate 函数分析


在 oat_file_assistant.cc#MakeUpToDate 函数中 , 最终调用了 GenerateOatFileNoChecks 函数 , 执行下一步操作 ;

oat_file_assistant.cc#MakeUpToDate 函数源码 :

OatFileAssistant::ResultOfAttemptToUpdate
OatFileAssistant::MakeUpToDate(bool profile_changed, std::string* error_msg) {CompilerFilter::Filter target;if (!GetRuntimeCompilerFilterOption(&target, error_msg)) {return kUpdateNotAttempted;}OatFileInfo& info = GetBestInfo();switch (info.GetDexOptNeeded(target, profile_changed)) {case kNoDexOptNeeded:return kUpdateSucceeded;// TODO: 现在,不要为我们打电话的各种方式而烦恼// dex2oat生成oat文件。始终生成oat文件,就像它// KDEX2O是从头开始的。case kDex2OatFromScratch:case kDex2OatForBootImage:case kDex2OatForRelocation:case kDex2OatForFilter:// ★ 核心跳转 return GenerateOatFileNoChecks(info, target, error_msg);}UNREACHABLE();
}

源码路径 : /art/runtime/oat_file_assistant.cc#MakeUpToDate

四、oat_file_assistant.cc#GenerateOatFileNoChecks 函数分析


先判断 Dex2Oat 当前是否可用 , 如果不可用 , 直接返回 ;

!runtime->IsDex2OatEnabled()

oat_file_assistant.cc#GenerateOatFileNoChecks 函数源码 :

OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks(OatFileAssistant::OatFileInfo& info, CompilerFilter::Filter filter, std::string* error_msg) {CHECK(error_msg != nullptr);Runtime* runtime = Runtime::Current();// 判断 Dex2Oat 当前是否可用 , 如果不可用 , 直接返回 if (!runtime->IsDex2OatEnabled()) {*error_msg = "Generation of oat file for dex location " + dex_location_+ " not attempted because dex2oat is disabled.";return kUpdateNotAttempted;}if (info.Filename() == nullptr) {*error_msg = "Generation of oat file for dex location " + dex_location_+ " not attempted because the oat file name could not be determined.";return kUpdateNotAttempted;}const std::string& oat_file_name = *info.Filename();const std::string& vdex_file_name = GetVdexFilename(oat_file_name);// dex2oat ignores missing dex files and doesn't report an error.// Check explicitly here so we can detect the error properly.// TODO: Why does dex2oat behave that way?struct stat dex_path_stat;if (TEMP_FAILURE_RETRY(stat(dex_location_.c_str(), &dex_path_stat)) != 0) {*error_msg = "Could not access dex location " + dex_location_ + ":" + strerror(errno);return kUpdateNotAttempted;}// If this is the odex location, we need to create the odex file layout (../oat/isa/..)if (!info.IsOatLocation()) {if (!PrepareOdexDirectories(dex_location_, oat_file_name, isa_, error_msg)) {return kUpdateNotAttempted;}}// Set the permissions for the oat and the vdex files.// The user always gets read and write while the group and others propagate// the reading access of the original dex file.mode_t file_mode = S_IRUSR | S_IWUSR |(dex_path_stat.st_mode & S_IRGRP) |(dex_path_stat.st_mode & S_IROTH);std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_file_name.c_str()));if (vdex_file.get() == nullptr) {*error_msg = "Generation of oat file " + oat_file_name+ " not attempted because the vdex file " + vdex_file_name+ " could not be opened.";return kUpdateNotAttempted;}if (fchmod(vdex_file->Fd(), file_mode) != 0) {*error_msg = "Generation of oat file " + oat_file_name+ " not attempted because the vdex file " + vdex_file_name+ " could not be made world readable.";return kUpdateNotAttempted;}std::unique_ptr<File> oat_file(OS::CreateEmptyFile(oat_file_name.c_str()));if (oat_file.get() == nullptr) {*error_msg = "Generation of oat file " + oat_file_name+ " not attempted because the oat file could not be created.";return kUpdateNotAttempted;}if (fchmod(oat_file->Fd(), file_mode) != 0) {*error_msg = "Generation of oat file " + oat_file_name+ " not attempted because the oat file could not be made world readable.";oat_file->Erase();return kUpdateNotAttempted;}std::vector<std::string> args;args.push_back("--dex-file=" + dex_location_);args.push_back("--output-vdex-fd=" + std::to_string(vdex_file->Fd()));args.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));args.push_back("--oat-location=" + oat_file_name);args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));// ★ 核心跳转 if (!Dex2Oat(args, error_msg)) {// Manually delete the oat and vdex files. This ensures there is no garbage// left over if the process unexpectedly died.vdex_file->Erase();unlink(vdex_file_name.c_str());oat_file->Erase();unlink(oat_file_name.c_str());return kUpdateFailed;}if (vdex_file->FlushCloseOrErase() != 0) {*error_msg = "Unable to close vdex file " + vdex_file_name;unlink(vdex_file_name.c_str());return kUpdateFailed;}if (oat_file->FlushCloseOrErase() != 0) {*error_msg = "Unable to close oat file " + oat_file_name;unlink(oat_file_name.c_str());return kUpdateFailed;}// Mark that the odex file has changed and we should try to reload.info.Reset();return kUpdateSucceeded;
}

源码路径 : /art/runtime/oat_file_assistant.cc#GenerateOatFileNoChecks

五、oat_file_assistant.cc#Dex2Oat 函数分析


在 oat_file_assistant.cc#Dex2Oat 函数中 , 主要是准备 Dex2Oat 可执行二进制程序的相关参数 ,

最终调用了

Exec(argv, error_msg)

函数 , 完成 Dex2Oat 执行的过程 ;

oat_file_assistant.cc#Dex2Oat 函数源码 :

bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args,std::string* error_msg) {Runtime* runtime = Runtime::Current();std::string image_location = ImageLocation();if (image_location.empty()) {*error_msg = "No image location found for Dex2Oat.";return false;}std::vector<std::string> argv;argv.push_back(runtime->GetCompilerExecutable());argv.push_back("--runtime-arg");argv.push_back("-classpath");argv.push_back("--runtime-arg");std::string class_path = runtime->GetClassPathString();if (class_path == "") {class_path = OatFile::kSpecialSharedLibrary;}argv.push_back(class_path);if (runtime->IsJavaDebuggable()) {argv.push_back("--debuggable");}runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);if (!runtime->IsVerificationEnabled()) {argv.push_back("--compiler-filter=verify-none");}if (runtime->MustRelocateIfPossible()) {argv.push_back("--runtime-arg");argv.push_back("-Xrelocate");} else {argv.push_back("--runtime-arg");argv.push_back("-Xnorelocate");}if (!kIsTargetBuild) {argv.push_back("--host");}argv.push_back("--boot-image=" + image_location);std::vector<std::string> compiler_options = runtime->GetCompilerOptions();argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());argv.insert(argv.end(), args.begin(), args.end());std::string command_line(android::base::Join(argv, ' '));// ★ 核心跳转return Exec(argv, error_msg);
}

源码路径 : /art/runtime/oat_file_assistant.cc#Dex2Oat

【Android 逆向】ART 脱壳 ( DexClassLoader 脱壳 | oat_file_assistant.cc 中涉及的 oat 文件生成流程 )相关推荐

  1. 【Android 逆向】ART 脱壳 ( DexClassLoader 脱壳 | exec_utils.cc 中执行 Dex 编译为 Oat 文件的 Exec 和 ExecAndReturnC函数 )

    文章目录 前言 一.exec_utils.cc#Exec 函数分析 二.exec_utils.cc#ExecAndReturnCode 函数分析 前言 在上一篇博客 [Android 逆向]ART 脱 ...

  2. 【Android 逆向】ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )

    文章目录 一.DexClassLoader 源码分析 二.参考 Dalvik 下的 DexClassLoader 类加载流程 一.DexClassLoader 源码分析 ART 虚拟机下的 DexCl ...

  3. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | RawDexFile.cpp 分析 | dvmRawDexFileOpen函数读取 DEX 文件 )

    文章目录 前言 一.RawDexFile.cpp 中 dvmRawDexFileOpen() 方法分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ( DexClassLoader 加载 ...

  4. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | 查找 DexFile 对应的C代码 | dalvik_system_DexFile.cpp 分析 )

    文章目录 前言 一.查找 DexFile 对应的 C++ 代码 1.根据 Native 文件命名惯例查找 C++ 代码 2.根据方法名查找 二.dalvik_system_DexFile.cpp 源码 ...

  5. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexFile loadDexFile 函数 | 构造函数 | openDexFile 函数 )

    文章目录 前言 一.DexFile.loadDexFile 函数分析 二.DexFile 构造函数分析 三.DexFile.openDexFile 函数分析 前言 上一篇博客 [Android 逆向] ...

  6. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexPathList 中根据 File 加载 DexFile | loadDexFile 分析 )

    文章目录 前言 一.根据 File 加载 DexFile 二.DexPathList.loadDexFile 函数分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ( DexClassLo ...

  7. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexPathList 构造函数分析 | makeDexElements 函数分析 )

    文章目录 前言 一.DexPathList 构造函数分析 二.DexPathList.makeDexElements 函数分析 三.Element 类分析 前言 上一篇博客 [Android 逆向]整 ...

  8. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )

    文章目录 前言 一.DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 二./bin/dexopt 源码分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ...

  9. 【Android 逆向】整体加固脱壳 ( 脱壳点简介 | 修改系统源码进行脱壳 )

    文章目录 一.脱壳点简介 二.修改系统源码进行脱壳 一.脱壳点简介 在上一篇博客 [Android 逆向]整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 rewriteD ...

最新文章

  1. ubuntu安装Android SDK,adb,fastboot
  2. phaser.min.js_如何使用Phaser 3,Express和Socket.IO构建多人纸牌游戏
  3. yunyang tensorflow-yolov3 启动evaluate.py报错:Could not create cudnn handle: CUDNN_STATUS_ALLOC_FAILED原因
  4. tomcat中配置jndi数据源以便spring获取
  5. 三星Galaxy Note 10+真机照公布:居中开孔前摄实锤
  6. Flutter布局锦囊---男女性别单选
  7. java web mvc spring_Java下Web MVC的领跑者:SpringMVC
  8. weblogic 启动 startWebLogic.sh
  9. 破解,汉化,越狱,解锁,为什么中国的大神总是“昙花一现”?
  10. Android手机会中电脑病毒么,安卓手机中木马病毒怎么办
  11. 【K8S集群安装二】K8S集群安装步骤
  12. 智能车心得分享(五)-- 电磁排布
  13. BugTracker配置
  14. 2022 最新华为面经分享:Java 高分面试指南
  15. Hadoop集群上的Hive安装时进行初始化元数据信息出现错误HiveSchemaTool:Parsing failed. Reason: Missing required option:
  16. python微信公众号翻译功能怎么用_Watson使用指南(七)在微信公众号中实现识图作诗功能...
  17. 常见的BlockingQueue有哪些
  18. 我用 Python 找出了删除我微信的所有人并将他们自动化删除了
  19. 基于 CNT 的射频辐射热计开发研究的 CPX-VF 低温探针台
  20. Android (仿支付宝) 收益进度条

热门文章

  1. Android:EditText 属性
  2. dubbo服务者配置说明
  3. webform开发经验(一):Asp.Net获取Checkbox选中的值
  4. SwiftSuspenders 1.6 浅出深入 深入 2
  5. 在进度面前,质量该不该打折?
  6. Documentum中关于日期时间类型字段的特殊处理
  7. CN.Text开发笔记—利用反射将数据读入实体类
  8. c++ 动态规划(数塔)
  9. BZOJ3709: [PA2014]Bohater
  10. JAVA课程设计——“小羊吃蓝莓”小游戏