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 局部引用及时释放,避免内存泄漏

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器