在 Android 8.0 及更高版本中,ART 工具接口 (ART TI) 公开了某些运行时内部组件,并使分析器和调试器能够影响应用的运行时行为。这可以用于实现最先进的性能工具,这些工具旨在为在其他平台上实现原生代理提供支持。
运行时内部机制会暴露给已加载到运行时进程中的代理。这些代理通过直接调用和回调与 ART 通信。运行时支持多个代理,以便可以将不同的正交分析关注点分隔开来。代理可以在运行时启动时提供(当调用 dalvikvm
或 app_process
时),也可以附加到已运行的进程。
由于检测和修改应用和运行时行为的能力非常强大,因此 ART TI 中集成了两项安全措施
- 首先,公开代理接口 JVMTI 的代码是作为运行时插件实现的,而不是运行时的核心组件。插件加载可能会受到限制,因此可以阻止代理找到任何接口点。
- 其次,
ActivityManager
类和运行时进程都只允许代理附加到可调试应用。可调试应用已由其开发者签名认可进行分析和检测,并且不会分发给最终用户。Google Play 商店不允许分发可调试应用。这可确保普通应用(包括核心组件)无法被检测或操纵。
设计
检测应用中的通用流程和互连如图 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 -P
和 am start-profiler/stop-profiler
命令类似于 attach-agent 命令。
JVMTI
此功能向代理(本机代码)公开 JVMTI API。重要功能包括
- 重新定义类。
- 跟踪对象分配和垃圾回收。
- 迭代堆中的所有对象,跟踪对象的引用树。
- 检查 Java 调用堆栈。
- 暂停(和恢复)所有线程。
不同版本的 Android 上可能提供不同的功能。
兼容性
此功能需要核心运行时支持,该支持仅在 Android 8.0 及更高版本中可用。设备制造商无需进行任何更改即可实现此功能。它是 AOSP 的一部分。
验证
CTS 在 Android 8 及更高版本上测试以下内容
- 测试代理附加到可调试应用,并且无法附加到不可调试应用。
- 测试所有已实现的 JVMTI API
- 测试代理的二进制接口是否稳定
其他测试已添加到 Android 9 及更高版本,并包含在这些版本的 CTS 测试中。