Skip to main content

Your First JVM Hook

Hooking Java or Kotlin methods is just as easy as hooking native functions, thanks to a consistent and expressive DSL. The underlying mechanism is fundamentally different—Terminator directly manipulates the internal ArtMethod structures within the Android Runtime (ART)—but the API makes it feel familiar.

Our Goal​

Intercept calls to android.util.Log.d to inspect or modify log messages before they are written.

The signature for this method is:

public static int d(String tag, String msg);

Hooking with beforeCall​

The most common way to hook a method is to execute some logic before the original method runs. The beforeCall infix function is perfect for this.

import android.util.Log
import com.skiy.terminatorkt.beforeCall
import kotlin.reflect.full.functions

fun setupLogHook() {
val targetMethod = Log::class.functions.first {
it.name == "d" &&
it.parameters.size == 2 &&
it.parameters[0].type.classifier == String::class &&
it.parameters[1].type.classifier == String::class
}

targetMethod beforeCall { methodCall ->
val args = methodCall.arguments
val tag = args[0] as String
val msg = args[1] as String

Log.d("JVMFuncMonitor", "Intercepted Log.d: tag='$tag', msg='$msg'")
args[1] = "[HOOKED] $msg"
}
}

// Call `setupLogHook()` in your Application's onCreate()

After setting up this hook, all calls to Log.d from anywhere in your app will have their message prepended with [HOOKED].

In-Depth Breakdown​

Get a Method Reference​

  • Kotlin: Use Kotlin's reflection (::) or standard KClass APIs to find the target KFunction.
  • Java: Use standard Java reflection (.class.getMethod(...) or .class.getDeclaredMethod(...)).

beforeCall { ... }​

This is an infix extension function that attaches your hook logic. It:

  • Runs before the original method is executed.
  • Receives a MethodCall object — the context for this invocation.
  • Always calls the original method after the block finishes (unless intercepted otherwise).

The MethodCall Context Object​

  • methodCall.getArguments(): Returns an Object[] array of arguments.
  • Modify arguments in-place by assigning to the array: args[1] = newVal.
  • The framework uses the modified array for the actual method call.

beforeCall is ideal for inspecting or modifying arguments before they’re passed into the original implementation.


✅ What's Next?​

beforeCall is simple and effective. To gain full control over the method's execution—including running logic after the call, skipping the original, and replacing its return value—check out the next page.

➡️ Next: Advanced JVM Hooking