打造基于 ART 的 Android 函数抽取壳:原理剖析与完整源码实战

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/

ART 下类加载流程

1. 类加载的时机

1、隐式加载:

  • 创建类的实例

  • 访问类的静态变量,或者为静态变量赋值

  • 调用类的静态方法

  • 使用反射方式来强制创建某个类或接口对应的 java.lang.Class 对象

  • 初始化某个类的子类

2、显示加载:

  • 使用 loadClass() 加载

  • 使用 forName() 加载

两者又有所不同。

2. 类加载的流程

loadClass 和 forName 也是有区别的;

loadClass 只完成了第一步,就是加载;

Class.forName 是完成了3步,加载、链接和初始化。

阶段 发生了什么
加载 字节码文件 -> Class 对象
验证 检查字节码合法性
准备 静态变量分配内存,默认值
解析 符号引用 → 真实引用
初始化 调用 <clinit> 函数,static 代码块执行,静态变量赋值

一个类从“加载” 到 “可以使用” 的完整生命周期:加载(Loading)→ 连接(Linking)→ 初始化(Initialization)。

加载(Loading)
  ↓
连接(Linking)
    → 验证(Verification)
    → 准备(Preparation)
    → 解析(Resolution)
  ↓
初始化(Initialization)
  ↓
使用(Use)
  ↓
卸载(Unload)

相关文章:Android 下的 ClassLoader 与 双亲委派机制

ClassLinker::LoadMethod

ClassLinker::LoadMethod 是 ART 中负责将 dex 文件中的方法信息解析并填充到 ArtMethod(Java 函数的 native 表示) 结构体中的关键函数,是抽取壳的实现基础。

从 ClassLoader.loadClass() 开始,逐步分析 ART 下类加载的完整流程。

word/media/image1.png
https://cs.android.com/android/platform/superproject/main/+/main:libcore/ojluni/src/main/java/java/lang/ClassLoader.java;l=557

ClassLoader 的 loadClass(String name, boolean resolve) 方法的核心实现,它正是 双亲委派机制(Parent Delegation Model) 的体现。

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
        // 1️⃣ 首先检查类是否已经加载过
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false); // 2️⃣ 向父类加载器请求加载
                } else {
                    c = findBootstrapClassOrNull(name); // 3️⃣ 否则用 BootstrapClassLoader
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // 4️⃣ 父加载器找不到,当前类加载器自己尝试加载
                c = findClass(name);
            }
        }
        return c;
}

https://cs.android.com/android/platform/superproject/main/+/main:libcore/ojluni/src/main/java/java/lang/ClassLoader.java;l=617

https://cs.android.com/android/platform/superproject/main/+/main:libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java;l=245

ClassLoader.loadClass → ClassLinker::LoadMethod 调用路径

ClassLoader.loadClass(name)
  ↓
BaseDexClassLoader.findClass(name)
  ↓
DexPathList.findClass(name)
  ↓
Element.findClass(name)
  ↓
DexFile.loadClassBinaryName(name)
  ↓
DexFile.defineClass(name)
  ↓
DexFile.defineClassNative(name)
  ↓
→ ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash, Handle<mirror::ClassLoader> class_loader, const DexFile& dex_file, const dex::ClassDef& dex_class_def)
    → ClassLinker::LoadClass(Thread* self, const DexFile& dex_file, const dex::ClassDef& dex_class_def, Handle<mirror::Class> klass)
        → LoadField(field, klass, ...)
        → LoadMethod(dex_file, method, klass, art_method)  ←🎯目标函数

https://cs.android.com/android/platform/superproject/+/android-10.0.0_r47:art/runtime/native/dalvik_system_DexFile.cc;l=456

https://cs.android.com/android/platform/superproject/+/android-10.0.0_r47:art/runtime/class_linker.cc;l=3094

https://cs.android.com/android/platform/superproject/+/android-10.0.0_r47:art/runtime/class_linker.cc;l=3732

ClassLinker::LoadMethod 真正进入到对类中 Java 函数对应的 ArtMethod 对象的初始化,ArtMethod 包含了当前指向内存中 CodeItem 的偏移

调用 SetCodeItemOffset 方法设置 ArtMethod 中 CodeItem 的偏移

word/media/image2.png
https://cs.android.com/android/platform/superproject/+/android-10.0.0_r47:art/runtime/class_linker.cc;l=3743

如果拿到了 CodeItemOffset 我们是不是就可以通过打补丁的方式恢复被抽取的 CodeItem 了。

hook execve 函数,禁用 dex2oat

ART 下实现抽取壳的另一个难点:dex2oat 编译流程。

如果 dex2oat 对抽取的 dex 进行编译生成了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值