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.
- Kotlin (Recommended)
- Java
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()
import android.util.Log;
import static com.skiy.terminatorkt.TerminatorJVMExtensions.beforeCall;
import com.skiy.terminator.hooks.jvm.MethodCall;
import java.lang.reflect.Method;
public class MyJvmHooks {
public static void setupLogHook() {
try {
Method targetMethod = Log.class.getMethod("d", String.class, String.class);
beforeCall(targetMethod, (methodCall) -> {
Object[] args = methodCall.getArguments();
String tag = (String) args[0];
String msg = (String) args[1];
Log.d("JVMFuncMonitor", "Intercepted Log.d (from Java)");
args[1] = "[HOOKED] " + msg;
return null;
});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
// Call MyJvmHooks.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 targetKFunction
. - 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 anObject[]
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