3

为了支持静态分析工具,我想以一种可以确定每个反射调用的方式检测或监视 Java 程序(如 Method.invoke(..)):

1.) 在哪个类 C 上调用这个方法,以及 2.) 哪个类加载器加载了这个 C 类。

理想情况下,我正在寻找不需要我静态修改 Java 运行时库的解决方案,即我正在寻找加载时解决方案。但是,该解决方案应该能够捕获所有反射调用,甚至是在 Java 运行时库本身中发生的此类调用。(我玩过 ClassFileTransformer 但这似乎只适用于 ClassFileTransformer 本身不依赖的类。特别是,ClassFileTransfomer 不适用于“类”类。)

谢谢!

4

2 回答 2

1

您是否正在寻找可以在生产中运行的东西?或者对在测试环境中运行的应用程序进行检测就足够了吗?如果是后者,可能需要考虑在分析工具下运行应用程序。我个人使用过并且会推荐JProfiler,它可以让您进行调用跟踪并设置触发器以在调用特定方法时执行诸如日志记录之类的操作。它不需要对托管程序进行任何修改,并且可以在 Java 运行时库上正常工作。也有开源工具,但我没有成功让这些工具发挥作用。

如果您需要可以在生产中运行的东西,您可能需要研究通过 Javassist 或 CGLib 实现您自己的自定义类加载器或字节码操作,也许使用 AspectJ (AOP)。这显然是一个更复杂的解决方案,我不确定它是否可以在没有编译时支持的情况下工作,所以希望分析工具对您的情况是可行的。

于 2009-08-30T23:31:41.547 回答
0

您可能追求的 API 是JVMTI。JVMTI 允许您为 JVM 中发生的大多数事件注册回调,包括 MethodEntry、MethodExit。您侦听这些事件并提取 Method.invoke 事件。有 API 调用来获取特定类的类加载器。但是,您必须使用 C 或 C++ 编写工具。

这是一个示例,它将过滤出 java.lang.reflect.Method.invoke 调用并将其打印出来。要获取有关已调用对象的详细信息,您可能需要查看堆栈帧。

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

static jvmtiEnv *jvmti = NULL;
static jvmtiCapabilities capa;

static jint check_jvmti_error(jvmtiEnv *jvmti,
                              jvmtiError errnum,
                              const char *str) {

    if (errnum != JVMTI_ERROR_NONE) {
        char *errnum_str;
        errnum_str = NULL;
        (void) (*jvmti)->GetErrorName(jvmti, errnum, &errnum_str);
        printf("ERROR: JVMTI: %d(%s): %s\n",
               errnum,
               (errnum_str == NULL ? "Unknown" : errnum_str),
               (str == NULL ? "" : str));
        return JNI_ERR;
    }
    return JNI_OK;
}

void JNICALL callbackMethodEntry(jvmtiEnv *jvmti_env,
                                 JNIEnv* jni_env,
                                 jthread thread,
                                 jmethodID method) {

    char* method_name;
    char* method_signature;
    char* generic_ptr_method;
    char* generic_ptr_class;
    char* class_name;
    jvmtiError error;
    jclass clazz;

    error = (*jvmti_env)->GetMethodName(jvmti_env,
                                        method,
                                        &method_name,
                                        &method_signature,
                                        &generic_ptr_method);
    if (check_jvmti_error(jvmti_env, error, "Failed to get method name")) {
        return;
    }

    if (strcmp("invoke", method_name) == 0) {

        error
                = (*jvmti_env)->GetMethodDeclaringClass(jvmti_env, method,
                        &clazz);
        if (check_jvmti_error(jvmti_env, error,
                "Failed to get class for method")) {
            (*jvmti_env)->Deallocate(jvmti_env, method_name);
            (*jvmti_env)->Deallocate(jvmti_env, method_signature);
            (*jvmti_env)->Deallocate(jvmti_env, generic_ptr_method);
            return;
        }

        error = (*jvmti_env)->GetClassSignature(jvmti_env, clazz, &class_name,
                &generic_ptr_class);
        if (check_jvmti_error(jvmti_env, error, "Failed to get class signature")) {
            (*jvmti_env)->Deallocate(jvmti_env, method_name);
            (*jvmti_env)->Deallocate(jvmti_env, method_signature);
            (*jvmti_env)->Deallocate(jvmti_env, generic_ptr_method);
            return;
        }

        if (strcmp("Ljava/lang/reflect/Method;", class_name) == 0) {
            printf("Method entered: %s.%s.%s\n", class_name, method_name,
                    method_signature);
        }
        (*jvmti_env)->Deallocate(jvmti_env, class_name);
        (*jvmti_env)->Deallocate(jvmti_env, generic_ptr_class);
    }

    (*jvmti_env)->Deallocate(jvmti_env, method_name);
    (*jvmti_env)->Deallocate(jvmti_env, method_signature);
    (*jvmti_env)->Deallocate(jvmti_env, generic_ptr_method);
}

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {

    jint result;
    jvmtiError error;
    jvmtiEventCallbacks callbacks;

    result = (*jvm)->GetEnv(jvm, (void**) &jvmti, JVMTI_VERSION_1_0);
    if (result != JNI_OK || jvmti == NULL) {
        printf("error\n");
        return JNI_ERR;
    } else {
        printf("loaded agent\n");
    }

    (void) memset(&capa, 0, sizeof(jvmtiCapabilities));
    capa.can_generate_method_entry_events = 1;

    error = (*jvmti)->AddCapabilities(jvmti, &capa);
    if (check_jvmti_error(jvmti, error, "Unable to set capabilities") != JNI_OK) {
        return JNI_ERR;
    }

    (void) memset(&callbacks, 0, sizeof(callbacks));
    callbacks.MethodEntry = &callbackMethodEntry;
    error = (*jvmti)->SetEventCallbacks(jvmti,
                                        &callbacks,
                                        (jint) sizeof(callbacks));
    if (check_jvmti_error(jvmti, error, "Unable to set callbacks") != JNI_OK) {
        return JNI_ERR;
    }

    error = (*jvmti)->SetEventNotificationMode(jvmti,
                                               JVMTI_ENABLE,
                                               JVMTI_EVENT_METHOD_ENTRY,
                                               (jthread) NULL);
    if (check_jvmti_error(jvmti, error,
            "Unable to set method entry notifications") != JNI_OK) {
        return JNI_ERR;
    }

    return JNI_OK;
}
于 2009-08-31T07:31:17.977 回答