Wockstar

Interacting with Java applications through C/C++.

Wockhardt Wizard (github) April 11th 2024 • 12 minute read • Java/C++ (Advanced)

Introduction

Today I'll be covering how to interact with applications running under the JVM (Java Virtual Machine).

There is a library called JNI, which is short for Java Native Interface or Java Native Injection, which we will be using to access the JVM from the comfort of C/C++. We will be executing as a DLL (Dynamic Link Library), as the library requires us to be executing under the same process as the Java application.

Beginning

Okay, so first lets cover on how to get the library, make sure you have a version of JDK (Java Development Kit) and go to the installation path. Copy over the files in the following folders: include, include/win32 into your project, and then finally go into the folder lib and copy jvm.lib into your project. Remember to link jvm.lib in your project and include jni.h in your source.

Now that we can implement this into our project, we can start to use the library. In the code below, I show an example on how to get the JavaVM and JNIEnv.

JniDll.cpp

JavaVM* vm;
JNI_GetCreatedJavaVMs(&vm, 1L, 0);

JNIEnv* env;
jint status = vm->GetEnv((void**)&env, JNI_VERSION_1_8); // Or any other JNI version

if (status == JNI_EDETACHED)
{
    vm->AttachCurrentThreadAsDaemon((void**)&env, 0);
}

The reason we attach the thread as 'Daemon' is so that it so the thread doesn't prevent the JVM from exiting. This way the application isn't reliant on our thread being closed.

Generic Types

If you are getting a field or a method, and in the signature you need to include types such as an instance (covered lower in the post), or native types such as floats or integers, there are aliases.

Type Aliases

Boolean - "Z"
Integer - "I"
Float - "F"
Double - "D"
Void - "V"
Byte - "B"
Char - "C"
Short - "S"
Long - "J
Array - Prefix With

Prefixing arrays may be confusing, so here is an example of a char array: "[C".

Fields

Fields, in the context of object oriented programming, are just variables within a class, and in this section I'll explain how to to find and access/modify these fields in the JVM.

JniDll.cpp

jclass my_class = env->FindClass("net/my_class");
jobject my_instance; // Obtaining instances is covered lower down in the post.
jfieldID my_field = env->GetFieldID(my_class, "my_field", "F");
float value = env->GetFloatField(my_instance, my_field);

Methods

Methods, are functions associated with an object or a class (for object oriented programming). In this section, I will now show you how to call methods in the JVM and find them.

JniDll.cpp

jclass my_class = env->FindClass("net/my_class");
jobject my_instance; // Obtaining instances is covered lower down in the post.
jmethodID my_method = env->GetMethodID(my_class, "my_func", "()F");
float value = env->CallFloatMethod(my_instance, my_method);

Static Fields & Methods

If a field or method is static, you shall call it with the class instead of the object, and using the dedicated static callers. In this example, I get a static float field, and get the value of it:

JniDll.cpp

jclass my_class = env->FindClass("net/my_class");
jfieldID my_field = env->GetStaticFieldID(my_class, "my_field", "F");
float value = env->GetStaticFloatField(my_class, my_field);

Class Instances (Objects)

So instead of, for example, '()Z' or 'Z' for the signature if you're getting a method or field, you'd swap the 'Z' in either of those statements by 'L' + [the class path] + ';'. Example here for getting a field that has the instance of a class, or a method that returns it.

JniDll.cpp

jclass my_class = env->FindClass("net/my_class");
jfieldID my_field = env->GetStaticFieldID(my_class, "my_o", "Lnet/my_class;");
jobject object_from_field = env->GetStaticObjectField(my_class, my_field);
jmethodID my_method = env->GetMethodID(my_class, "my_o_func", "()Lnet/my_class;");
jobject object_from_method = env->CallObjectMethod(object_from_field, my_method);

By the way, they don't have to be static or non-static, just in my example I use both.

Memory Management

Freeing references to Java objects a good practice, and do this stop stop memory leaks in our program. Local references are freed by the JVM automatically, but it can be useful to free them manually, as there is a limited amount of local references available. You can free references as such:

JniDll.cpp

// Assuming instance is a valid object
jobject instance; // Can also be done with a jclass (cast to jobject)
env->DeleteLocalRef(instance); // Or DeleteGlobalRef if its a global reference

Make sure to not use the object after its reference has been terminated, as it'll lead to your JVM throwing an exception. If you programming in C++, you may use classes to your advantages, and make a class that wraps over a jobject, and in the destructor cleans up the reference.

Also ensure that you are getting classes, fields, and methods efficiently, with only getting them once at startup of your module (and caching them), or at least times as possible, as if you are constantly re-getting these methods it will lead to unnecessary JNI calls, thus making your code inefficient. The only exception to this is if you're 'injecting' classes at runtime, whether you're patching a currently existing class or adding your own, you will need to wait for that to be done before finding classes/methods/fields.

Conclusion

And that's all the info for now! You now have a solid idea on how to interact with Java applications through C++!

If you found this post helpful or learned something new, consider supporting me. Your donations help me continue to create and share content like this. Every contribution, no matter how big or small, is appreciated. Thank you for your support!

Bitcoin: 1NULLXYYLMy77Np8DMfCqUcNPDhVP3K9QJ
Litecoin: LWihbiZd7F2cRcSAbC8ptJJJu3V2ZD844Q
Etherium: 0x65bB26e9Ea91A4D958e47F9dd86999aeCD65815D
Monero: 46rgFADhcTbUrgG9FGq2RyTj1M69mq8qq9QC6EnSoNoN34vGcPqDxUYQpwZdkT3kxCMJRoeVTCyohUAhVq8auut7HDR2GNt
Dogecoin: DEMcjLL3idCygT9bp4ebfqFnu3dah6gpfD
Tether USDT: 0x65bB26e9Ea91A4D958e47F9dd86999aeCD65815D
Solana: 139iYdXNNm8gotaKE5kvUo2nEfZYBr2SZVGhzPMkV6Nf