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;
}
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);
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With