[FIXED] How to hook Java method with JNI during runtime?

Issue

Other questions I saw about this topic were not about while the Java program is running or were unclear to me, so I’m asking a separate question

I’d like to know if there’s a way to hook/override methods with JNI during runtime

It would work as Mixins (like in Fabric API), which "injects" code at the beginning or at the end of the method but with C++ and JNI

For example: there’s a getter method that returns a field of a class but I want to change the return value to something I want instead of the expected value

I’ve ran into some topics that said about JVM Handles but I have no idea where to start searching it

Solution

Thank you all for the answers but I found exactly what I wanted (not sure if it’s what everyone is looking for but this was my goal with this question)

Basically I found out that jmethodIDs are pointers, which I thought of actually being the address of the method on the stack

To check if this would work I debugged the jvm and found out that it is actually it!
With some testing I also found out that some methods have specific offsets in order to be hooked… you could get this with trial and error but also debugging the jvm (its mostly 0x40)

With the address of the function we can hook it with simple hooking as we would hook any other process method

Here’s an example using MinHook and jni (obviously) that actually works with Minecraft with the onTick method (used to keep track of when a tick on the game happens):

typedef void(__cdecl* runTick) (void**, void**);

runTick pRunTick;
runTick pRunTickTarget;

void __cdecl tickHook(void** p1, void** p2) {
    sdk::instance->hasTick = true;
    return pRunTick(p1, p2);
}

bool  cheat::hooks::jhooks::apply_jtickhook(bool create) {
    jclass mc_class = machip::instance->get_env()->FindClass("ave");
    jmethodID method = machip::instance->get_env()->GetMethodID(mc_class, "s", "()V");
    pRunTickTarget = reinterpret_cast<runTick>(*(unsigned __int64*)(*(unsigned __int64*)method + 0x40));

    if (create)
    {
        MH_STATUS status = MH_CreateHook(reinterpret_cast<void**>(pRunTickTarget), &tickHook, reinterpret_cast<void**>(&pRunTick));
        if (status != MH_OK) {
            wrapper::output("failed to hook onTick");
            wrapper::output(MH_StatusToString(status));
            return false;
        }
    }
    

    if (MH_EnableHook(reinterpret_cast<void**>(pRunTickTarget)) != MH_OK) {
        wrapper::output("failed to enable onTick hook");
        return false;
    }

    machip::instance->get_env()->DeleteLocalRef(mc_class);

    wrapper::output("onTick hooked");

    return true;
}

For more about hooking/detour I saw this video to understand better and also this one

Answered By – kpp

Answer Checked By – Timothy Miller (Easybugfix Admin)

Leave a Reply

(*) Required, Your email will not be published