Android 逆向ART 函数抽取加壳 ⑥ ( 函数抽取后续操作 “ 还原被抽取的函数 “ | LoadClass 类加载 | LoadClassMembers 类成员加载 )

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 逆向ART 函数抽取加壳 ⑥ ( 函数抽取后续操作 “ 还原被抽取的函数 “ | LoadClass 类加载 | LoadClassMembers 类成员加载 )相关的知识,希望对你有一定的参考价值。

文章目录


两篇博客中 , 简单介绍了 禁用 dex2oat 机制 的原理 , 下面开始 实现 dex2oat 禁用功能 ;


【Android 逆向】ART 函数抽取加壳 ③ ( 禁用 dex2oat 操作 HOOK 点介绍 | 集成 InLineHook ) 博客中 , 介绍了 HOOK 点 , 以及 集成 HOOK C 代码的库 InLineHook ;


【Android 逆向】ART 函数抽取加壳 ④ ( 对 libc.so#execve 函数进行内联 HOOK 操作 ) 博客中 , 对 libc.so#execve 函数 进行了 内联 HOOK 操作 , 可以对该函数进行拦截 ;


【Android 逆向】ART 函数抽取加壳 ⑥ ( unistd.h#execve 函数分析 | 使用自定义的 myexecve 函数替换 libc.so#execve 函数 ) 博客实现 自定义的 myexecve 函数 替换 libc.so#execve 函数 ;


本篇博客开始分析 函数收取后续操作 , 查找还原被抽取函数的时机 ;





一、函数抽取后续操作 " 还原被抽取的函数 "



在上一篇博客 【Android 逆向】ART 函数抽取加壳 ⑥ ( unistd.h#execve 函数分析 | 使用自定义的 myexecve 函数替换 libc.so#execve 函数 ) 中 实现了 自定义的 myexecve 函数 替换 libc.so#execve 函数 ;

下面还需要 在源码中找到一个时机点 , 还原 被抽取的函数 ;

函数抽取加壳 操作 , 打包到 APK 应用中的 Dex 字节码文件中函数是被抽取出来的 , 执行前必须将函数还原 ,

  • 第一步 关闭 dex2oat 机制 ,
  • 第二步 还原被抽取的函数 ;




二、class_linker.cc#LoadClass 类加载操作



还原被抽取函数的 时机是 类加载器 ClassLoader 加载 Class 字节码类 流程中的一个时间点 , 该时间点必须是 类加载之后 , 函数调用之前 ;


在 ART 虚拟机中 , 调用函数前 , 需要对函数所在的类进行 加载 以及 链接 操作 ;

android 源码中的 " art/runtime/class_linker.cc#LoadClass " 函数中 , 加载字节码类 ;

void ClassLinker::LoadClass(Thread* self,
                            const DexFile& dex_file,
                            const DexFile::ClassDef& dex_class_def,
                            Handle<mirror::Class> klass) 
  const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
  if (class_data == nullptr) 
    return;  // no fields or methods - for example a marker interface
  
  LoadClassMembers(self, dex_file, class_data, klass);

源码地址 : http://androidxref.com/8.0.0_r4/xref/art/runtime/class_linker.cc#3119





三、class_linker.cc#LoadClassMembers 类成员加载操作



" art/runtime/class_linker.cc#LoadClass " 函数 中调用 LoadClassMembers 函数 , 该函数的作用是加载 类的成员 , 包括 成员字段成员变量 ;

在 LoadClassMembers 函数中 , 首先加载了静态字段 , 然后加载实例字段 , 最后加载 成员函数 ;

在加载函数的过程中 , 调用了 LoadMethod 方法 , 可以作为加载函数的时机点 ;

LoadMethod(dex_file, it, klass, method);

art/runtime/class_linker.cc#LoadClassMembers 函数源码如下 :

void ClassLinker::LoadClassMembers(Thread* self,
                                   const DexFile& dex_file,
                                   const uint8_t* class_data,
                                   Handle<mirror::Class> klass) 
  
    //注意:在设置字段和方法数组之前,我们不能暂停线程,否则
	//Class::VisitFieldRoots可能缺少某些字段或方法。
    ScopedAssertNoThreadSuspension nts(__FUNCTION__);
    //加载静态字段。
	//我们允许class_data_item中相同字段的重复定义
	//但忽略此处的重复索引b/21868015。
    LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader());
    ClassDataItemIterator it(dex_file, class_data);
    LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(self,
                                                                allocator,
                                                                it.NumStaticFields());
    size_t num_sfields = 0;
    uint32_t last_field_idx = 0u;
    for (; it.HasNextStaticField(); it.Next()) 
      uint32_t field_idx = it.GetMemberIndex();
      DCHECK_GE(field_idx, last_field_idx);  // Ordering enforced by DexFileVerifier.
      if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) 
        DCHECK_LT(num_sfields, it.NumStaticFields());
        LoadField(it, klass, &sfields->At(num_sfields));
        ++num_sfields;
        last_field_idx = field_idx;
      
    

    // 加载实例字段.
    LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self,
                                                                allocator,
                                                                it.NumInstanceFields());
    size_t num_ifields = 0u;
    last_field_idx = 0u;
    for (; it.HasNextInstanceField(); it.Next()) 
      uint32_t field_idx = it.GetMemberIndex();
      DCHECK_GE(field_idx, last_field_idx);  // Ordering enforced by DexFileVerifier.
      if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) 
        DCHECK_LT(num_ifields, it.NumInstanceFields());
        LoadField(it, klass, &ifields->At(num_ifields));
        ++num_ifields;
        last_field_idx = field_idx;
      
    

    if (UNLIKELY(num_sfields != it.NumStaticFields()) ||
        UNLIKELY(num_ifields != it.NumInstanceFields())) 
      LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor()
          << " (unique static fields: " << num_sfields << "/" << it.NumStaticFields()
          << ", unique instance fields: " << num_ifields << "/" << it.NumInstanceFields() << ")";
      // 注意:不要缩小过度分配的sfields/ifield,只需设置大小即可。
      if (sfields != nullptr) 
        sfields->SetSize(num_sfields);
      
      if (ifields != nullptr) 
        ifields->SetSize(num_ifields);
      
    
    // 设置字段数组。
    klass->SetSFieldsPtr(sfields);
    DCHECK_EQ(klass->NumStaticFields(), num_sfields);
    klass->SetIFieldsPtr(ifields);
    DCHECK_EQ(klass->NumInstanceFields(), num_ifields);
    // 加载方法.
    bool has_oat_class = false;
    const OatFile::OatClass oat_class =
        (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler())
            ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class)
            : OatFile::OatClass::Invalid();
    const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr;
    klass->SetMethodsPtr(
        AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()),
        it.NumDirectMethods(),
        it.NumVirtualMethods());
    size_t class_def_method_index = 0;
    uint32_t last_dex_method_index = DexFile::kDexNoIndex;
    size_t last_class_def_method_index = 0;
    // TODO 这些应该真正使用迭代器。
    for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) 
      // 新建 ArtMethod 对象 , 表示一个函数
      ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);
      LoadMethod(dex_file, it, klass, method);
      LinkCode(this, method, oat_class_ptr, class_def_method_index);
      uint32_t it_method_index = it.GetMemberIndex();
      if (last_dex_method_index == it_method_index) 
        // 重复案例
        method->SetMethodIndex(last_class_def_method_index);
       else 
        method->SetMethodIndex(class_def_method_index);
        last_dex_method_index = it_method_index;
        last_class_def_method_index = class_def_method_index;
      
      class_def_method_index++;
    
    for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) 
      ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
      LoadMethod(dex_file, it, klass, method);
      DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
      LinkCode(this, method, oat_class_ptr, class_def_method_index);
      class_def_method_index++;
    
    DCHECK(!it.HasNext());
  
  // 确保卡片上有标记,以便记住的套餐能够拾取本地根。
  Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass.Get());
  self->AllowThreadSuspension();

源码地址 : http://androidxref.com/8.0.0_r4/xref/art/runtime/class_linker.cc#LoadClassMembers

以上是关于Android 逆向ART 函数抽取加壳 ⑥ ( 函数抽取后续操作 “ 还原被抽取的函数 “ | LoadClass 类加载 | LoadClassMembers 类成员加载 )的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向ART 函数抽取加壳 ⑤ ( unistd.h#execve 函数分析 | 使用自定义的 myexecve 函数替换 libc.so#execve 函数 )

Android 逆向ART 函数抽取加壳 ③ ( 禁用 dex2oat 操作 HOOK 点介绍 | 集成 InLineHook )

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

Android 逆向ART 函数抽取加壳 ② ( 禁用 dex2oat 简介 | TurboDex 中禁用 dex2oat 参考示例 )

Android 逆向加壳技术识别 ( 函数抽取 与 Native 化加壳的区分 | VMP 加壳与 Dex2C 加壳的区分 )

Android 逆向脱壳解决方案 ( DEX 整体加壳 | 函数抽取加壳 | VMP 加壳 | Dex2C 加壳 | Android 应用加固防护级别 )