您当前的位置: 首页 >  ar

韩曙亮

暂无认证

  • 0浏览

    0关注

    1068博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【Android 逆向】ART 函数抽取加壳 ( ART 下的函数抽取恢复时机 | 禁用 dex2oat 机制源码分析 )

韩曙亮 发布时间:2021-12-19 18:24:46 ,浏览量:0

文章目录
  • 一、ART 下的函数抽取恢复时机
  • 二、禁用 dex2oat 机制源码分析
    • 1、oat_file_assistant.cc#GenerateOatFileNoChecks 源码分析
    • 2、oat_file_assistant.cc#Dex2Oat 源码分析
    • 3、exec_utils.cc#Exec 源码分析
    • 4、exec_utils.cc#ExecAndReturnCode 源码分析

一、ART 下的函数抽取恢复时机

ART 下的函数抽取恢复时机 :

  • 恢复抽取函数早于 oat 文件编译 : 在 ART 虚拟机下 , 需要将 dex 文件编译生成为 oat 文件 , 将 dex 文件中的 函数指令 抽取出来 , 必须 在 生成 oat 文件之前 , 将从 抽取的函数指令恢复 ;

  • 禁用 dex2oat 机制 : 如果 禁用 dex2oat 的编译过程 , 则 恢复 被抽取的 函数指令 , 不在受 该条件限制 , 不是必须在 dex2oat 之前恢复 , 可以稍晚一些再恢复函数指令 ;

如果选择第一种方案 , 在 dex2oat 之前进行恢复 , 这没有任何意义 , dex2oat 编译后 , 生成的 oat 文件是完整的 , 此时 可以 完整的将 oat 文件 dump 到 SD 卡中 , 基本等于没有加固 , 还是一个一代壳 ;

因此 , 大部分加固厂商 , 选择 禁用 dex2oat 机制 ; 这样处于安全考虑 , 牺牲了应用的运行效率 ;

二、禁用 dex2oat 机制源码分析

在之前的博客章节 【Android 逆向】ART 脱壳 ( DexClassLoader 脱壳 | oat_file_assistant.cc 中涉及的 oat 文件生成流程 ) 四、oat_file_assistant.cc#GenerateOatFileNoChecks 函数分析 中 , 分析到 , dex2oat 编译过程中 , 需要调用 /art/runtime/oat_file_assistant.cc#GenerateOatFileNoChecks 函数 ;

1、oat_file_assistant.cc#GenerateOatFileNoChecks 源码分析

在该函数中 , 调用 dex2oat 进行编译 ;

在 /art/runtime/oat_file_assistant.cc#GenerateOatFileNoChecks 函数 中的 核心跳转语句是 调用了 /art/runtime/oat_file_assistant.cc#Dex2Oat 函数 ;

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忽略丢失的dex文件,不报告错误。
  // 请在此处明确检查,以便正确检测错误。
  // TODO:为什么dex2oat会这样做?
  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;
  }

  // 如果这是odex位置,我们需要创建odex文件布局 (../oat/isa/..)
  if (!info.IsOatLocation()) {
    if (!PrepareOdexDirectories(dex_location_, oat_file_name, isa_, error_msg)) {
      return kUpdateNotAttempted;
    }
  }

  // 设置oat和vdex文件的权限。
  // 当组和其他人传播时,用户总是被读取和写入
  // 原始dex文件的读取权限。
  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 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 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 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)) {
    // 手动删除oat和vdex文件。这样可以确保没有垃圾
    // 如果进程意外死亡,则剩余。
    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;
  }

  // 标记 odex 件已更改,我们应该尝试重新加载。
  info.Reset();
  return kUpdateSucceeded;
}

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

2、oat_file_assistant.cc#Dex2Oat 源码分析

在 /art/runtime/oat_file_assistant.cc#Dex2Oat 函数中 , 调用了 /art/runtime/exec_utils.cc#Exec 函数 ;

bool OatFileAssistant::Dex2Oat(const std::vector& 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 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 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

3、exec_utils.cc#Exec 源码分析

在 /art/runtime/exec_utils.cc#Exec 函数中 , 调用了 /art/runtime/exec_utils.cc#ExecAndReturnCode 函数 ;

bool Exec(std::vector& arg_vector, std::string* error_msg) {
  // ★ 核心跳转
  int status = ExecAndReturnCode(arg_vector, error_msg);
  if (status != 0) {
    const std::string command_line(android::base::Join(arg_vector, ' '));
    *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
                              command_line.c_str());
    return false;
  }
  return true;
}

源码路径 : /art/runtime/exec_utils.cc#Exec

4、exec_utils.cc#ExecAndReturnCode 源码分析

在 /art/runtime/exec_utils.cc#ExecAndReturnCode 函数中 , 调用了

execve(program, &args[0], envp);

函数 , 通过 hook 该 execve 函数 , 可以禁用 dex2oat ;

int ExecAndReturnCode(std::vector& arg_vector, std::string* error_msg) {
  const std::string command_line(android::base::Join(arg_vector, ' '));
  CHECK_GE(arg_vector.size(), 1U)             
关注
打赏
1663594092
查看更多评论
0.0428s