raptor-frida17-android-trace
4 views
9d950aed...
Description
Full-featured Java and Module tracer for Android (Frida 17 API).
How to Use
Download the script and run it with Frida CLI:
Download ScriptThen run with Frida:
frida -U -f YOUR_PACKAGE_NAME -l raptor-frida17-android-trace.js
Replace YOUR_PACKAGE_NAME with the target app's package name.
Source Code
JavaScript
/*
* raptor_frida_android_trace.js - Java/Module tracer for Android
* Copyright (c) 2017-2025 Marco Ivaldi <raptor@0xdeadbeef.info>
*
* "Life is not like water. Things in life don't necessarily
* flow over the shortest possible route."
* -- Haruki Murakami, 1Q84
*
* Frida.re JS code to trace arbitrary Java methods and Module functions calls
* in an Android app for debugging and reverse engineering. See https://www.frida.re/
* and https://codeshare.frida.re/ for further information on this world-class
* dynamic instrumentation toolkit.
*
* Example usage:
* $ pipx install frida-tools
* $ frida -U -f com.target.app -l raptor_frida_android_trace.js
*
* Tested with:
* Frida 17.3.2 on macOS 15.6.1 with Redmi Note 10S (Android 11)
*
* Thanks:
* @inode-, @federicodotta, @leonjza, @dankluev
*
* Get the latest version at:
* https://github.com/0xdea/frida-scripts/
*/
// Generic trace
function trace(pattern) {
var type = (pattern.toString().indexOf("!") === -1) ? "java" : "module";
if (type === "module") {
// trace Module
var res = new ApiResolver("module");
var matches = res.enumerateMatches(pattern);
var targets = uniqBy(matches, JSON.stringify);
targets.forEach(function(target) {
traceModule(target.address, target.name);
});
} else if (type === "java") {
// Trace Java class
var found = false;
Java.enumerateLoadedClasses({
onMatch: function(aClass) {
if (aClass.match(pattern)) {
found = true;
try {
var className = aClass.match(/[L](.*);/)[1].replace(/\//g, ".");
} catch (err) {
return;
} // Avoid TypeError: cannot read property 1 of null
traceClass(className);
}
},
onComplete: function() {}
});
// Trace Java method
if (!found) {
try {
traceMethod(pattern);
} catch (err) { // Catch non-existing classes/methods
console.error(err);
}
}
}
}
// Find and trace all methods declared in a Java class
function traceClass(targetClass) {
var hook = Java.use(targetClass);
var methods = hook.class.getDeclaredMethods();
hook.$dispose;
var parsedMethods = [];
methods.forEach(function(method) {
parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
});
var targets = uniqBy(parsedMethods, JSON.stringify);
targets.forEach(function(targetMethod) {
traceMethod(targetClass + "." + targetMethod);
});
}
// Trace a specific Java method
function traceMethod(targetClassMethod) {
var delim = targetClassMethod.lastIndexOf(".");
if (delim === -1) return;
var targetClass = targetClassMethod.slice(0, delim)
var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length)
var hook = Java.use(targetClass);
var overloadCount = hook[targetMethod].overloads.length;
console.log("Tracing " + targetClassMethod + " [" + overloadCount + " overload(s)]");
for (var i = 0; i < overloadCount; i++) {
hook[targetMethod].overloads[i].implementation = function() {
console.warn("\n*** entered " + targetClassMethod);
// Print backtrace
// Java.perform(function() {
// var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
// console.log("\nBacktrace:\n" + bt);
// });
// Print args
// TODO: double check this works without the occasional crash
if (arguments.length) console.log();
for (var j = 0; j < arguments.length; j++) {
console.log("arg[" + j + "]: " + arguments[j]);
}
// Print retval
var retval = this[targetMethod].apply(this, arguments); // Rare crash (Frida bug?)
console.log("\nretval: " + retval);
console.warn("\n*** exiting " + targetClassMethod);
return retval;
}
}
}
// Trace Module functions
function traceModule(impl, name) {
console.log("Tracing " + name);
Interceptor.attach(impl, {
onEnter: function(args) {
// Trace only intended calls
this.flag = false;
// var filename = args[0].readCString();
// if (filename.indexOf("XYZ") === -1 && filename.indexOf("ZYX") === -1) // exclusion list
// if (filename.indexOf("my.interesting.file") !== -1) // inclusion list
this.flag = true;
if (this.flag) {
console.warn("\n*** entered " + name);
// Print backtrace
// TODO: understand why this crashes sometimes
// console.log("\nBacktrace:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE)
// .map(DebugSymbol.fromAddress).join("\n"));
}
},
onLeave: function(retval) {
if (this.flag) {
// Print retval
console.log("\nretval: " + retval);
console.warn("\n*** exiting " + name);
}
}
});
}
// Remove duplicates from array
function uniqBy(array, key) {
var seen = {};
return array.filter(function(item) {
var k = key(item);
return seen.hasOwnProperty(k) ? false : (seen[k] = true);
});
}
// Usage examples
setTimeout(function() { // Avoid java.lang.ClassNotFoundException
Java.perform(function() {
// trace("com.target.utils.CryptoUtils.decrypt");
// trace("android.os.storage.StorageVolume");
// trace("com.target.utils.CryptoUtils");
// trace("CryptoUtils");
// trace(/crypto/i);
// trace("exports:*!open*");
});
}, 0);
Comments