Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | oat_file_assistant.cc 中涉及的 oat 文件生成流程 )
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | oat_file_assistant.cc 中涉及的 oat 文件生成流程 )相关的知识,希望对你有一定的参考价值。
文章目录
前言
在上一篇博客 【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)
以上是关于Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | oat_file_assistant.cc 中涉及的 oat 文件生成流程 )的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向ART 脱壳 ( InMemoryDexClassLoader 脱壳 | DexFile 构造函数及相关调用函数 | Android 源码中查找 native 函数 )
Android 逆向ART 脱壳 ( 修改 /art/runtime/dex_file.cc#OpenCommon 系统源码进行脱壳 )
Android 逆向ART 脱壳 ( InMemoryDexClassLoader 脱壳 | InMemoryDexClassLoader 类加载器脱壳点总结 )
Android 逆向ART 脱壳 ( InMemoryDexClassLoader 脱壳 | 加固厂商在 ART 下使用的两种类加载器 | InMemoryDexClassLoader 源码 )(代码
Android 逆向ART 脱壳 ( InMemoryDexClassLoader 脱壳 | dex_file.cc 中创建 DexFile 实例对象的相关函数分析 )
Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段