The Java Native Interface, or JNI, allows developers to access and implement Java classes and functions from native C code. Part of this functionality involves an API RegisterNatives to attach native code to a loaded Java class, after which point it defines an ABI for bridging back/forth to native logic.

However, this API requires the class to have marked these specific methods with the native keyword, which both allows their implementation to be abstract and makes it clear to the VM that they will be bridged at runtime to native code, registered using one of a few mechanism (including RegisterNatives).

Substrate provides a more powerful replacement for this API, which not only allows developers to replace methods that were not marked native with JNI implementations, but also to reclaim an identifier to the original method for purposes of "calling through" to the previous implementation.

Note: JNI uses two different calling conventions: when calling from Java to C, the standard C calling convention is used; however, when calling from C to Java, you instead use the modified calling convention used by variadic functions. This API is implemented based on this same premise.

Note: For Objective-C developers used to MSHookMessageEx, it should be noted at this API currently does not allow for hooking "below" a method at a different point in the inheritance hierarchy. It is intended that this will be fixed in a future release of Substrate.

void MSJavaHookMethod(JNIEnv *jni, jclass _class, jmethodID method, void *hook, void **old);
Parameter Description
jni JNI environment used to interpret _class argument.
_class JNI class for which method will be instrumented.
method JNI method ID of the method to instrument on _class
hook Replacement JNI implementation to register for this method. This function should use the standard Java JNI ABI.
old A pointer to a function pointer with variadic argument calling convention that will be filled in with a stub which may be used to call the original implementation. This can be NULL if you do not proceed to the original.
// this function pointer is purposely variadic
static void (*oldCode)(JNIEnv *, jobject, ...);

static void newCode(
    JNIEnv *jni, jobject this,
    jobject host, jint port
) {
    if (port == 6667)
        port = 7001;
    (*oldCode)(jni, this, host, port);
}

JNIEnv *jni = /* ... */;
jclass _class = jni->FindClass("java/net/InetSocketAddress");

jmethodID method = jni->GetMethodID(_class,
    "<init>", // JNI name of constructor
    "(Ljava/lang/String;I)V" // void (String, int)
);

MSJavaHookMethod(jni, _class, method, &newCode, &oldCode);