Android 逆向ART 函数抽取加壳 ⑥ ( 函数抽取后续操作 “ 还原被抽取的函数 “ | LoadClass 类加载 | LoadClassMembers 类成员加载 )
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 逆向ART 函数抽取加壳 ⑥ ( 函数抽取后续操作 “ 还原被抽取的函数 “ | LoadClass 类加载 | LoadClassMembers 类成员加载 )相关的知识,希望对你有一定的参考价值。
文章目录
在
- 【Android 逆向】ART 函数抽取加壳 ① ( ART 下的函数抽取恢复时机 | 禁用 dex2oat 机制源码分析 )
- 【Android 逆向】ART 函数抽取加壳 ② ( 禁用 dex2oat 简介 | TurboDex 中禁用 dex2oat 参考示例 )
两篇博客中 , 简单介绍了 禁用 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 应用加固防护级别 )