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