raptor-frida17-android-trace

by
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 Script

Then 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);
Share this script:
Twitter LinkedIn

Comments

Login or Sign up to leave a comment.
Loading comments...