damn the jvm again(2)

发布于:2025-08-13 ⋅ 阅读:(12) ⋅ 点赞:(0)

都想看jvm是吧

class_monitor_agent.c

#include <jni.h>
#include <jvmti.h>
#include <stdio.h>
#include <string.h>

// JVM TI环境指针
static jvmtiEnv* jvmti = NULL;

// 类加载事件回调函数
void JNICALL ClassFileLoadHook(jvmtiEnv* jvmti, JNIEnv* jni,
                              jclass class_being_redefined, jobject loader,
                              const char* name, jobject protection_domain,
                              jint class_data_len, const unsigned char* class_data,
                              jint* new_class_data_len, unsigned char** new_class_data) {
    if (name != NULL) {
        printf("类加载: %s\n", name);
    } else {
        printf("类加载: 匿名类\n");
    }
}

// 代理初始化函数 - 修正参数和调用方式
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
    jint result;
    jvmtiCapabilities capabilities;
    jvmtiEventCallbacks callbacks;
    
    // 修正GetEnv调用参数和类型转换
    result = (*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_2);
    if (result != JNI_OK || jvmti == NULL) {
        printf("无法获取JVM TI环境,错误码: %d\n", result);
        return JNI_ERR;
    }

    // 初始化能力结构体
    memset(&capabilities, 0, sizeof(capabilities));
    capabilities.can_retransform_classes = 1;
    
    // 修正能力字段名称
    capabilities.can_generate_all_class_hook_events = 1;
    
    // 设置能力
    result = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    if (result != JVMTI_ERROR_NONE) {
        printf("无法设置JVM TI能力,错误码: %d\n", result);
        return JNI_ERR;
    }

    // 初始化回调结构体
    memset(&callbacks, 0, sizeof(callbacks));
    callbacks.ClassFileLoadHook = &ClassFileLoadHook;
    
    // 设置回调
    result = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
    if (result != JVMTI_ERROR_NONE) {
        printf("无法设置JVM TI回调,错误码: %d\n", result);
        return JNI_ERR;
    }

    // 启用类加载事件监控
    result = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 
                                               JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
    if (result != JVMTI_ERROR_NONE) {
        printf("无法启用类加载事件,错误码: %d\n", result);
        return JNI_ERR;
    }

    printf("JVM TI代理初始化成功\n");
    return JNI_OK;
}

// JVM退出时的处理
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* vm) {
    if (jvmti != NULL) {
        // 禁用事件监控
        (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_DISABLE,
                                          JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL);
    }
    printf("JVM TI代理卸载\n");
}

thread_trace_agent.c

#include <jni.h>
#include <jvmti.h>
#include <stdio.h>
#include <string.h>

static jvmtiEnv* jvmti = NULL;

// 线程启动回调
void JNICALL ThreadStart(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
    // 尝试通过JNI获取线程名称(不依赖JVM TI特定能力)
    jclass thread_class = (*jni)->FindClass(jni, "java/lang/Thread");
    jmethodID get_name = (*jni)->GetMethodID(jni, thread_class, "getName", "()Ljava/lang/String;");
    jstring name_str = (jstring)(*jni)->CallObjectMethod(jni, thread, get_name);
    const char* name = name_str ? (*jni)->GetStringUTFChars(jni, name_str, NULL) : "无名线程";
    
    printf("[THREAD START] 名称: %s, 地址: %p\n", name, (void*)thread);
    
    // 释放资源
    if (name_str) {
        (*jni)->ReleaseStringUTFChars(jni, name_str, name);
    }
}

// 线程结束回调
void JNICALL ThreadEnd(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
    // 尝试通过JNI获取线程名称
    jclass thread_class = (*jni)->FindClass(jni, "java/lang/Thread");
    jmethodID get_name = (*jni)->GetMethodID(jni, thread_class, "getName", "()Ljava/lang/String;");
    jstring name_str = (jstring)(*jni)->CallObjectMethod(jni, thread, get_name);
    const char* name = name_str ? (*jni)->GetStringUTFChars(jni, name_str, NULL) : "无名线程";
    
    printf("[THREAD END] 名称: %s, 地址: %p\n", name, (void*)thread);
    
    // 释放资源
    if (name_str) {
        (*jni)->ReleaseStringUTFChars(jni, name_str, name);
    }
}

// 代理初始化函数
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
    jint result;
    jvmtiCapabilities capabilities;
    jvmtiEventCallbacks callbacks;

    // 获取JVM TI环境
    result = (*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_2);
    if (result != JNI_OK || jvmti == NULL) {
        printf("无法获取JVM TI环境,错误码: %d\n", result);
        return JNI_ERR;
    }

    // 初始化能力结构体(移除Java SDK 24不支持的字段)
    memset(&capabilities, 0, sizeof(capabilities));
    // 注意:不设置can_get_thread_info和can_generate_thread_events(SDK 24无这些字段)
    
    // 尝试设置能力(忽略不支持字段导致的错误)
    result = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    if (result != JVMTI_ERROR_NONE) {
        printf("警告: 能力设置部分失败,错误码: %d\n", result);
    }

    // 设置线程事件回调
    memset(&callbacks, 0, sizeof(callbacks));
    callbacks.ThreadStart = &ThreadStart;
    callbacks.ThreadEnd = &ThreadEnd;
    result = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
    if (result != JVMTI_ERROR_NONE) {
        printf("无法设置回调,错误码: %d\n", result);
        return JNI_ERR;
    }

    // 启用线程启动事件
    result = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_START, NULL);
    if (result != JVMTI_ERROR_NONE) {
        printf("无法启用线程启动事件,错误码: %d\n", result);
        return JNI_ERR;
    }
    
    // 启用线程结束事件
    result = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_END, NULL);
    if (result != JVMTI_ERROR_NONE) {
        printf("无法启用线程结束事件,错误码: %d\n", result);
        return JNI_ERR;
    }

    printf("线程跟踪代理初始化成功\n");
    return JNI_OK;
}

// 代理卸载函数
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* vm) {
    if (jvmti != NULL) {
        (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_DISABLE, JVMTI_EVENT_THREAD_START, NULL);
        (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_DISABLE, JVMTI_EVENT_THREAD_END, NULL);
    }
}

exception_trace_agent.c

#include <jni.h>
#include <jvmti.h>
#include <stdio.h>
#include <string.h>

static jvmtiEnv* jvmti = NULL;

// 异常捕获回调(适配Java SDK 24的事件类型)
void JNICALL ExceptionCatch(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, 
                           jmethodID method, jlocation location, jthrowable exception) {
    jclass ex_class = (*jni)->GetObjectClass(jni, exception);
    char* ex_class_name;
    
    // 获取异常类名
    (*jvmti)->GetClassSignature(jvmti, ex_class, &ex_class_name, NULL);
    
    // 获取异常消息
    jmethodID get_message = (*jni)->GetMethodID(jni, ex_class, "getMessage", "()Ljava/lang/String;");
    jstring message_str = (jstring)(*jni)->CallObjectMethod(jni, exception, get_message);
    const char* message = message_str ? (*jni)->GetStringUTFChars(jni, message_str, NULL) : "无消息";

    printf("[EXCEPTION CAUGHT] 类型: %s, 消息: %s\n", ex_class_name, message);

    // 释放资源
    (*jvmti)->Deallocate(jvmti, (unsigned char*)ex_class_name);
    if (message_str) {
        (*jni)->ReleaseStringUTFChars(jni, message_str, message);
    }
}

// 初始化代理
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
    jint result;
    jvmtiCapabilities capabilities;
    jvmtiEventCallbacks callbacks;

    result = (*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_2);
    if (result != JNI_OK || jvmti == NULL) return JNI_ERR;

    // 启用异常监控能力
    memset(&capabilities, 0, sizeof(capabilities));
    capabilities.can_generate_exception_events = 1;
    result = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    if (result != JVMTI_ERROR_NONE) {
        printf("无法设置异常监控能力,错误码: %d\n", result);
        return JNI_ERR;
    }

    // 设置异常回调(使用ExceptionCatch而非ExceptionThrown)
    memset(&callbacks, 0, sizeof(callbacks));
    callbacks.ExceptionCatch = &ExceptionCatch;
    result = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
    if (result != JVMTI_ERROR_NONE) return JNI_ERR;

    // 启用异常捕获事件(适配Java SDK 24)
    (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION_CATCH, NULL);

    printf("异常跟踪代理初始化成功\n");
    return JNI_OK;
}

JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* vm) {
    if (jvmti != NULL) {
        (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_DISABLE, JVMTI_EVENT_EXCEPTION_CATCH, NULL);
    }
}

object_trace_agent.c

#include <jni.h>
#include <jvmti.h>
#include <stdio.h>
#include <string.h>

static jvmtiEnv* jvmti = NULL;

// 对象分配事件回调
void JNICALL VMObjectAlloc(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread,
                          jobject obj, jclass cls, jlong size) {
    char* class_name;
    // 获取类名
    (*jvmti)->GetClassSignature(jvmti, cls, &class_name, NULL);
    if (class_name) {
        printf("[ALLOC] 类: %s, 大小: %ld bytes\n", class_name, size);
        // 释放字符串(JVM TI分配的内存需手动释放)
        (*jvmti)->Deallocate(jvmti, (unsigned char*)class_name);
    }
}

// 代理初始化
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
    jint result;
    jvmtiCapabilities capabilities;
    jvmtiEventCallbacks callbacks;

    // 获取JVM TI环境
    result = (*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_2);
    if (result != JNI_OK || jvmti == NULL) {
        printf("无法获取JVM TI环境\n");
        return JNI_ERR;
    }

    // 启用对象分配监控能力
    memset(&capabilities, 0, sizeof(capabilities));
    capabilities.can_generate_vm_object_alloc_events = 1;
    result = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    if (result != JVMTI_ERROR_NONE) {
        printf("无法设置能力\n");
        return JNI_ERR;
    }

    // 设置回调
    memset(&callbacks, 0, sizeof(callbacks));
    callbacks.VMObjectAlloc = &VMObjectAlloc;
    result = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks));
    if (result != JVMTI_ERROR_NONE) {
        printf("无法设置回调\n");
        return JNI_ERR;
    }

    // 启用对象分配事件
    result = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
                                              JVMTI_EVENT_VM_OBJECT_ALLOC, NULL);
    if (result != JVMTI_ERROR_NONE) {
        printf("无法启用事件\n");
        return JNI_ERR;
    }

    printf("对象跟踪代理初始化成功\n");
    return JNI_OK;
}

JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* vm) {
    if (jvmti != NULL) {
        (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_DISABLE,
                                          JVMTI_EVENT_VM_OBJECT_ALLOC, NULL);
    }
}

gbuild.sh

# Usage: bash gbuild.sh what.c Jxxx.java
set -xe

export JAVA_INCLUDE_DIR=/etc/alternatives/java_sdk_24


gcc -shared -fPIC -shared -I$JAVA_INCLUDE_DIR/include -I$JAVA_INCLUDE_DIR/include/linux $1 -o lib$1.so

java -agentpath:./lib$1.so $2

usage

bash gbuild.sh <what agent.c> <what test java>

JVMTITest.java

public class JVMTITest {
    // 内部类,用于展示更多类加载情况
    static class InnerClass {
        public void doSomething() {
            System.out.println("内部类方法被调用");
        }
    }

    public static void main(String[] args) {
        System.out.println("主程序开始运行");
        
        // 创建对象,触发类加载
        InnerClass inner = new InnerClass();
        inner.doSomething();
        
        // 尝试加载一个标准库类
        try {
            Class<?> stringClass = Class.forName("java.lang.String");
            System.out.println("加载了标准类: " + stringClass.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
        System.out.println("主程序运行结束");

	try {
		throw new Exception("damn");
	} catch (Exception e) {
		System.out.println(e.getMessage());
	}
    }
}

sample.log

  • https://gitee.com/EEPPEE_admin/jvm-backend-example/blob/jni-and-hack-jvm/hackjvm_example/sample.log