JVMTI使用例子
一、JVMTI Agent 示例代码(C++,支持回调 Java 层)
写一个 JVMTI Agent,它在 OnLoad 时列出所有类名,并通过 JNI 调用 Java 静态方法 AgentCallback.onClassFound(String className) 打印输出。
jvmti_agent.cpp
#include <jni.h>
#include <jvmti.h>
#include <android/log.h>
#define LOG_TAG "JVMTI_AGENT"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
static jvmtiEnv* jvmti = nullptr;
static JavaVM* g_vm = nullptr;
extern "C" {
jint Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
LOGI("Agent_OnLoad called");
g_vm = vm;
if (vm->GetEnv((void**)&jvmti, JVMTI_VERSION_1_0) != JNI_OK || jvmti == nullptr) {
LOGE("Failed to get JVMTI env");
return JNI_ERR;
}
JNIEnv* env = nullptr;
if (vm->AttachCurrentThread(&env, nullptr) != JNI_OK || env == nullptr) {
LOGE("Failed to attach current thread");
return JNI_ERR;
}
// 替换成你的实际包名
jclass callbackCls = env->FindClass("your/package/name/AgentCallback");
if (callbackCls == nullptr) {
LOGE("Cannot find AgentCallback class");
return JNI_ERR;
}
jmethodID method = env->GetStaticMethodID(callbackCls, "onClassFound", "(Ljava/lang/String;)V");
if (method == nullptr) {
LOGE("Cannot find onClassFound method");
return JNI_ERR;
}
jint class_count = 0;
jclass* classes = nullptr;
if (jvmti->GetLoadedClasses(&class_count, &classes) != JVMTI_ERROR_NONE) {
LOGE("GetLoadedClasses failed");
return JNI_ERR;
}
for (int i = 0; i < class_count; ++i) {
char* signature = nullptr;
if (jvmti->GetClassSignature(classes[i], &signature, nullptr) == JVMTI_ERROR_NONE && signature != nullptr) {
jstring className = env->NewStringUTF(signature);
env->CallStaticVoidMethod(callbackCls, method, className);
env->DeleteLocalRef(className);
jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
}
}
jvmti->Deallocate(reinterpret_cast<unsigned char*>(classes));
return JNI_OK;
}
}
二、如何在 Android App 中加载并使用 JVMTI Agent(含 Java 回调)
本节将展示如何在 Android App 中:
加载编译好的 .so JVMTI Agent,并通过 JNI 回调 Java 层打印已加载类名。
1. Java 层准备:创建回调类
在你的 App 项目中创建 AgentCallback 类:
package your.package.name;
import android.util.Log;
public class AgentCallback {
public static void onClassFound(String className) {
Log.i("AgentCallback", "Class loaded: " + className);
}
}
2. 修改 AndroidManifest.xml
确保你的 App 是可调试的,并允许提取 native 库:
<application
android:debuggable="true"
android:extractNativeLibs="true"
... />
3. 在代码中加载 JVMTI Agent
建议放在 Application#onCreate 中:
if (Build.VERSION.SDK_INT >= 28) {
String agentPath = getApplicationInfo().nativeLibraryDir + "/libjvmti_agent.so";
try {
Debug.attachJvmtiAgent(agentPath, null, getClassLoader());
Log.i("AGENT", "JVMTI agent attached successfully");
} catch (IOException e) {
Log.e("AGENT", "Failed to attach JVMTI agent", e);
}
}
4. 编译并部署 native.so库
使用 CMake 构建 libjvmti_agent.so,确保它打包进 APK 中的 /lib/armeabi-v7a/ 或 /lib/arm64-v8a/ 目录下(与你设备 ABI 匹配)。
示例 CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
add_library(jvmti_agent SHARED jvmti_agent.cpp)
target_include_directories(jvmti_agent PRIVATE
${CMAKE_SOURCE_DIR}/include
${ANDROID_NDK}/sources/android/cpufeatures
)
target_link_libraries(jvmti_agent log)
5. 验证是否成功加载
在 adb logcat 中查看:
I/JVMTI_AGENT: Agent_OnLoad called
I/AgentCallback: Class loaded: Ljava/lang/String;
I/AgentCallback: Class loaded: Landroid/app/Activity;
...
6. 限制条件与注意事项
条目 | 要求 |
---|---|
Android版本 | 仅 Android 9(API 28)及以上支持 attachJvmtiAgent() |
应用类型 | 必须是 debuggable=true |
Java 回调类 | 要确保 AgentCallback 类已经加载(可放在 Application 中主动引用) |
多次 attach | 同一 Agent 不能重复 attach(否则会失败) |
线程安全 | Native 回调时注意 JNI 局部引用及时释放,避免内存泄漏 |