Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Printing class names of all loaded classes using JVMTI

I am using JNI and JVMTI with C++ with the goal of printing all class names of a target java application. The problem I am experiencing is that it crashes when trying to call the getName() method from inside each Java class.

    static jvmtiEnv* jvmtiEnv;
    static JNIEnv* Env;

    // The following code is located inside a method
    jint class_count;
    jclass* classes_ptr;
    jvmtiEnv->GetLoadedClasses(&class_count, &classes_ptr);
    Logger::Log("LoadedClassCount: " + std::to_string(class_count));

    for (int i = 0; i < class_count; i++) {
        jclass clazz = classes_ptr[i];

        if (clazz) {
            //Logger::Log("Class Number: " + std::to_string(i));
            jmethodID mid_getName = Env->GetMethodID(clazz, "getName", "()Ljava/lang/String;");
            if (!mid_getName) continue; // This method is not always available.

            Logger::Log("Pass 2");
            jobject classNameObj = Env->CallObjectMethod(clazz, mid_getName); // Crashes here
            if (!classNameObj) continue;
            Logger::Log("Pass 3");

            // Printing fix suggested by @Someprogrammerdude
            std::string str = Env->GetStringUTFChars((jstring) classNameObj, 0);
            Logger::Log(("ClassName: " + str).c_str());  // or Logger::Log("ClassName: " + str)
        }
    }

Also yes, the fields jvmtiEnv, and Env are initialized and work because I have been able to call other methods but cant seem to figure out how to print class names. The following is my logging method. I also print log method calls to console that is actively attached to the target java application but since my code crashes this part is useless, so I rely on also printing to a txt file.

void Logger::Log(std::string message)
{
    if (!Logger::Initialized) Logger::Init();
    std::cout << "[ LOG ] :: " + message << std::endl;

    std::ofstream log("C:\\Users\\me\\debug.txt", std::ofstream::app | std::ofstream::out);
    log << message << std::endl;
}
like image 967
UnSure Avatar asked Dec 14 '25 06:12

UnSure


1 Answers

Let us say for the sake of demonstration that clazz is the Class instance that corresponds to the java.util.Vector class. In Java parlance this would be a Class<Vector>.

Thus, this line

jmethodID mid_getName = Env->GetMethodID(clazz, "getName", "()Ljava/lang/String;");

tries to look up the getName method in java.util.Vector. This fails because, indeed, Vectors do not have a getName method. In the unlikely even that this would have returned a value for a given class C, you are only allowed to call that method on instances of class C. Yet you call it on the class C itself. That is why the JVM rightfully crashes.

Instead, you want to look up the getName method of Class itself, or in Java parlance, Class<Class> (and everywhere else, a "metaclass") and call that on all instances of Class<Class>, which are the Class in your classes_ptr.

There are lots of ways to get at this, but the easiest is probably just the following:

jclass cls_Class = Env->FindClass("java/lang/Class");
jmethodID mid_Class_getName = Env->GetMethodID(cls_Class, "getName", "()Ljava/lang/String;");

for (int i = 0; i < class_count; i++) {
   jclass clazz = classes_ptr[i];
   jstring classNameObj = (jstring) Env->CallObjectMethod(clazz, mid_Class_getName);
   ...
}
like image 124
Botje Avatar answered Dec 16 '25 22:12

Botje



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!