ART TI

在 Android 8.0 及更高版本中,ART 工具接口 (ART TI) 公开了某些运行时内部组件,并使分析器和调试器能够影响应用的运行时行为。这可以用于实现最先进的性能工具,这些工具旨在为在其他平台上实现原生代理提供支持。

运行时内部机制会暴露给已加载到运行时进程中的代理。这些代理通过直接调用和回调与 ART 通信。运行时支持多个代理,以便可以将不同的正交分析关注点分隔开来。代理可以在运行时启动时提供(当调用 dalvikvmapp_process 时),也可以附加到已运行的进程。

由于检测和修改应用和运行时行为的能力非常强大,因此 ART TI 中集成了两项安全措施

  • 首先,公开代理接口 JVMTI 的代码是作为运行时插件实现的,而不是运行时的核心组件。插件加载可能会受到限制,因此可以阻止代理找到任何接口点。
  • 其次,ActivityManager 类和运行时进程都只允许代理附加到可调试应用。可调试应用已由其开发者签名认可进行分析和检测,并且不会分发给最终用户。Google Play 商店不允许分发可调试应用。这可确保普通应用(包括核心组件)无法被检测或操纵。

设计

检测应用中的通用流程和互连如图 1 所示。

Flow and interconnection in an instrumented app
图 1. 检测应用的流程和互连

ART 插件 libopenjdkjvmti 公开了 ART TI,该 TI 旨在满足平台的需求和约束

  • 类重定义基于 Dex 文件,其中仅包含单个类定义,而不是类文件。
  • 用于检测和重定义的 Java 语言 API 未公开。

ART TI 还支持 Android Studio 性能分析器。

加载或附加代理

要在运行时启动时附加代理,请使用以下命令加载 JVMTI 插件和给定的代理

dalvikvm -Xplugin:libopenjdkjvmti.so -agentpath:/path/to/agent/libagent.so …

当代理在运行时启动时加载时,没有安全措施到位,因此请记住,手动启动的运行时允许完全修改而无需安全措施。(这允许 ART 测试。)

注意:这不适用于设备上的普通应用(包括系统服务器)。应用是从已运行的 zygote 分叉出来的,并且 zygote 进程不允许加载代理。

要将代理附加到已运行的应用,请使用以下命令

adb shell cmd activity attach-agent [process]
/path/to/agent/libagent.so[=agent-options]

如果尚未加载 JVMTI 插件,则附加代理会同时加载插件和代理库。

代理可能只能附加到标记为可调试的正在运行的应用(应用清单的一部分,应用节点上的属性 android:debuggable 设置为 true)。在允许附加代理之前,ActivityManager 类和 ART 都会执行检查。ActivityManager 类检查当前应用信息(从 PackageManager 类数据派生)的可调试状态,运行时检查其当前状态,该状态在应用启动时设置。

代理位置

运行时需要将代理加载到当前进程中,以便代理可以直接绑定到运行时并与之通信。ART 本身与代理的特定来源位置无关。该字符串用于 dlopen 调用。文件系统权限和 SELinux 策略限制了实际加载。

要交付可由可调试应用运行的代理,请执行以下操作

  • 将代理嵌入到应用 APK 的库目录中。
  • 使用 run-as 将代理复制到应用的数据目录中。

API

以下方法已添加到 android.os.Debug

/**
     * Attach a library as a jvmti agent to the current runtime, with the given classloader
     * determining the library search path.
     * Note: agents may only be attached to debuggable apps. Otherwise, this function will
     * throw a SecurityException.
     *
     * @param library the library containing the agent.
     * @param options the options passed to the agent.
     * @param classLoader the classloader determining the library search path.
     *
     * @throws IOException if the agent could not be attached.
     * @throws a SecurityException if the app is not debuggable.
     */
    public static void attachJvmtiAgent(@NonNull String library, @Nullable String options,
            @Nullable ClassLoader classLoader) throws IOException {

其他 Android API

attach-agent 命令是公开可见的。此命令将 JVMTI 代理附加到正在运行的进程

adb shell 'am attach-agent com.example.android.displayingbitmaps
\'/data/data/com.example.android.displayingbitmaps/code_cache/libfieldnulls.so=Ljava/lang/Class;.name:Ljava/lang/String;\''

am start -Pam start-profiler/stop-profiler 命令类似于 attach-agent 命令。

JVMTI

此功能向代理(本机代码)公开 JVMTI API。重要功能包括

  • 重新定义类。
  • 跟踪对象分配和垃圾回收。
  • 迭代堆中的所有对象,跟踪对象的引用树。
  • 检查 Java 调用堆栈。
  • 暂停(和恢复)所有线程。

不同版本的 Android 上可能提供不同的功能。

兼容性

此功能需要核心运行时支持,该支持仅在 Android 8.0 及更高版本中可用。设备制造商无需进行任何更改即可实现此功能。它是 AOSP 的一部分。

验证

CTS 在 Android 8 及更高版本上测试以下内容

  • 测试代理附加到可调试应用,并且无法附加到不可调试应用。
  • 测试所有已实现的 JVMTI API
  • 测试代理的二进制接口是否稳定

其他测试已添加到 Android 9 及更高版本,并包含在这些版本的 CTS 测试中。