Master Frida: Hook Android Apps with Advanced Techniques

This tutorial walks you through using Frida to hook Android applications, covering API basics, method interception, constructor and field manipulation, overload handling, dynamic class loading, and practical code snippets for both static and instance methods, all while emphasizing safe, educational use.

Python Crawling & Data Mining
Python Crawling & Data Mining
Python Crawling & Data Mining
Master Frida: Hook Android Apps with Advanced Techniques

Introduction

Welcome! This tutorial is for learning and discussion only; illegal use is prohibited. By reading you agree to these terms.

Official Documentation

Frida JavaScript API:

https://frida.re/docs/javascript-api/

Target Application

APK used for demonstration:

app-release.apk

Hooking Ordinary and Static Methods

Hooking works the same for both; distinction is needed only for explicit calls.

Code Example

// ordinary method
let money = Java.use("com.xiaojianbang.hook.Money");
money.getInfo.implementation = function() {
    console.log("money.getInfo called");
    return this.getInfo();
};
// static method
money.setFlag.overload('java.lang.String').implementation = function(str) {
    console.log("money.setFlag called");
    console.log("param str:", str);
    return this.setFlag(str);
};

Modifying Parameters and Return Values

let money = Java.use("com.xiaojianbang.hook.Money");
money.getInfo.implementation = function() {
    console.log("money.getInfo called");
    return "getInfo return value modified";
};

Hooking Constructors ($init)

let money = Java.use("com.xiaojianbang.hook.Money");
money.$init.implementation = function(str, i) {
    console.log(str, i);
    return this.$init("张三", 2000);
};

Object Construction and Modification ($new)

// construct a new Money object inside deposit
let wallet = Java.use("com.xiaojianbang.hook.Wallet");
let money = Java.use("com.xiaojianbang.hook.Money");
wallet.deposit.implementation = function(arg_money) {
    console.log("arg_money:", arg_money);
    return this.deposit(money.$new("张三", 10));
};
// modify passed Money object
wallet.deposit.implementation = function(a) {
    console.log("arg a:", a);
    a.setAmount(15);
    console.log(a.getAmount());
    return this.deposit(a);
};

Printing HashMap

function printMap(map) {
    var result = {};
    var keyset = map.keySet();
    var it = keyset.iterator();
    while (it.hasNext()) {
        var keystr = it.next().toString();
        var valuestr = map.get(keystr).toString();
        result[keystr] = valuestr;
    }
    return JSON.stringify(result);
}

Overload Handling

var utils = Java.use("com.xiaojianbang.hook.Utils");
utils.getCalc.overload('int','int').implementation = function(a,b){
    console.log(a,b);
    return this.getCalc(a,b);
};
utils.getCalc.overload('int','int','int').implementation = function(a,b,c){
    console.log(a,b,c);
    return this.getCalc(a,b,c);
};
utils.getCalc.overload('int','int','int','int').implementation = function(a,b,c,d){
    console.log(a,b,c,d);
    return this.getCalc(a,b,c,d);
};

Hook All Overloads Dynamically

var utils = Java.use("com.xiaojianbang.hook.Utils");
var overloadsArr = utils.getCalc.overloads;
for (var i=0;i<overloadsArr.length;i++) {
    overloadsArr[i].implementation = function(){
        var params = "";
        for (var j=0;j<arguments.length;j++) {
            params += arguments[j] + " ";
        }
        console.log("utils.getCalc is called! params is:", params);
        return this.getCalc.apply(this, arguments);
    };
}

Active Invocation

var money = Java.use("com.xiaojianbang.hook.Money");
money.setFlag("static method active call");
var moneyObj = money.$new("卢布", 1000);
console.log(moneyObj.getInfo());
Java.choose("com.xiaojianbang.hook.Money", {
    onMatch: function(obj){
        console.log(obj.getInfo(), obj.currency.value, obj._currency.value);
    },
    onComplete: function(){
        console.log("Memory search for Money objects completed");
    }
});

Accessing and Modifying Fields

let money = Java.use("com.xiaojianbang.hook.Money");
console.log(money.flag.value);
money.flag.value = "modified";
console.log(money.flag.value);
let moneyObj = money.$new("张三", 10);
console.log(moneyObj.currency.value);
moneyObj.currency.value = "field modified";
console.log(moneyObj.currency.value);

Hooking Inner and Anonymous Classes

Inner class: use the class name with $ suffix.

let wallet$InnerStructure = Java.use("com.xiaojianbang.hook.Wallet$InnerStructure");
console.log(wallet$InnerStructure);
Java.choose("com.xiaojianbang.hook.Wallet$InnerStructure", {
    onMatch: function(obj){
        console.log("Wallet$InnerStructure:", obj.bankCardsList.value);
    },
    onComplete: function(){}
});

Anonymous class: identified by $ followed by a number.

let money$1 = Java.use("com.xiaojianbang.app.MainActivity$1");
money$1.getInfo.implementation = function(){
    var result = this.getInfo();
    console.log("money.getInfo result:", result);
    return result;
};

Enumerating Loaded Classes

console.log(Java.enumerateLoadedClassesSync().join("
"));

Enumerating Class Members

let wallet = Java.use("com.xiaojianbang.hook.Wallet");
let methods = wallet.class.getDeclaredMethods();
for (let i=0;i<methods.length;i++) console.log(methods[i].getName());
let constructors = wallet.class.getDeclaredConstructors();
console.log("=== Constructors ===");
for (let i=0;i<constructors.length;i++) console.log(constructors[i].getName());
let fields = wallet.class.getDeclaredFields();
console.log("=== Fields ===");
for (let i=0;i<fields.length;i++) console.log(fields[i].getName());
let classes = wallet.class.getDeclaredClasses();
console.log("=== Inner Classes ===");
for (let i=0;i<classes.length;i++) console.log(classes[i].getName());

Hooking All Methods of a Class

function hookFunc(cls, methodName) {
    console.log(methodName);
    var overloadsArr = cls[methodName].overloads;
    for (var j=0;j<overloadsArr.length;j++) {
        overloadsArr[j].implementation = function(){
            var params = "";
            for (var k=0;k<arguments.length;k++) params += arguments[k] + " ";
            console.log("cls."+methodName+" is called! params is:", params);
            return this[methodName].apply(this, arguments);
        };
    }
}
var utils = Java.use("com.xiaojianbang.hook.Utils");
var methods = utils.class.getDeclaredMethods();
for (let i=0;i<methods.length;i++) {
    var methodName = methods[i].getName();
    hookFunc(utils, methodName);
}

Injecting a Class with registerClass

const MyWeirdTrustManager = Java.registerClass({
    name: 'com.xiaojianbang.app.MyRegisterClass',
    implements: [Java.use("com.xiaojianbang.app.TestRegisterClass")],
    fields: {description: 'java.lang.String', limit: 'int'},
    methods: {
        $init() { console.log('Constructor called'); },
        test1: [{returnType:'void',argumentTypes:[],implementation(){ console.log('test1 called'); }},
                {returnType:'void',argumentTypes:['java.lang.String','int'],implementation(str,num){ console.log('test1(str,num) called',str,num); }}],
        test2(str,num){ console.log('test2(str,num) called',str,num); return null; }
    }
});
var myObj = MyWeirdTrustManager.$new();
myObj.test1();
myObj.test1("xiaojianbang1", 100);
myObj.test2("xiaojianbang2", 200);
myObj.limit.value = 10000;
console.log(myObj.limit.value);

Loading External Dex Files

Java.openClassFile("/data/local/tmp/patch.dex").load();
var test = Java.use("com.xiaojianbang.myapplication.Test");
var utils = Java.use("com.xiaojianbang.hook.Utils");
utils.shufferMap.implementation = function(map){
    var result = test.print(map);
    console.log(result);
    return result;
};

Enumerating Classes with Java.choose

Java.choose("com.xiaojianbang.app.Season", {
    onMatch: function(obj){ console.log(obj.ordinal()); },
    onComplete: function(){}
});
console.log(Java.use("com.xiaojianbang.app.Season").values());

Writing Files via Frida

var ios = new File("/sdcard/xiaojianbang.txt", "w");
ios.write("xiaojianbang is very good!!!
");
ios.flush();
ios.close();

Getting Android Context

var current_application = Java.use('android.app.ActivityThread').currentApplication();
var context = current_application.getApplicationContext();

Upcasting with Java.cast

var utils = Java.use("com.xiaojianbang.hook.Utils");
utils.shufferMap2.implementation = function(map){
    console.log("map:", map);
    console.log("map (printMap):", printMap(map));
    console.log("map (printMap2):", printMap2(map));
    var result = Java.cast(map, Java.use("java.util.HashMap"));
    console.log("map (cast):", result);
    return this.shufferMap2(result);
};

Array Construction

console.log(utils.myPrint(["xiaojianbang","QQ:243757","VX:xia88","公众号"]));
var strarr = Java.array("Ljava.lang.String;", ["123","123","VX:123","123"]);
console.log(utils.myPrint(strarr));

Object Array Construction

var bankCard = Java.use("com.xiaojianbang.hook.BankCard");
var bankCardObj = bankCard.$new("xiaojianbang","123456789","CBDA",1,"15900000000");
var integer = Java.use("java.lang.Integer");
var boolean = Java.use("java.lang.Boolean");
console.log(utils.myPrint(["xiaojianbang", integer.$new(30), boolean.$new(true), bankCardObj]));

ArrayList Active Invocation

let arrayList = Java.use("java.util.ArrayList").$new();
let integer = Java.use("java.lang.Integer");
let boolean = Java.use("java.lang.Boolean");
let bankCard = Java.use("com.xiaojianbang.hook.BankCard");
let bankCardObj = bankCard.$new("xiaojianbang","123456789","CBDA",1,"15900000000");
arrayList.add("xiaojianbang");
arrayList.add(integer.$new(30));
arrayList.add(boolean.$new(true));
arrayList.add(bankCardObj);
var utils = Java.use("com.xiaojianbang.hook.Utils");
console.log(utils.myPrint(arrayList));

Hooking Dynamically Loaded Dex (ClassLoaders)

Java.enumerateClassLoaders({
    onMatch: function(loader){
        try {
            Java.classFactory.loader = loader;
            var dynamic = Java.use("com.xiaojianbang.app.Dynamic");
            console.log("dynamic:", dynamic);
            dynamic.sayHello.implementation = function(){
                console.log("hook dynamic.sayHello is run!");
                return "xiaojianbang";
            };
        } catch(e){ console.log("e:", loader); }
    },
    onComplete: function(){}
});

Hooking DexClassLoader

var dexClassLoader = Java.use("dalvik.system.DexClassLoader");
dexClassLoader.loadClass.overload('java.lang.String').implementation = function(className){
    var result = this.loadClass(className);
    if ("com.xiaojianbang.app.Dynamic" === className){
        Java.classFactory.loader = this;
        var dynamic = Java.use("com.xiaojianbang.app.Dynamic");
        console.log("dynamic:", dynamic);
        dynamic.sayHello.implementation = function(){
            console.log("dynamic.sayHello is called");
            return "xiaojianbang";
        };
        console.log(dynamic.$new().sayHello());
    }
    return result;
};

Scoped Hooking Example

var mainActivity = Java.use("com.xiaojianbang.app.MainActivity");
var stringBuilder = Java.use('java.lang.StringBuilder');
mainActivity.generateAESKey.implementation = function(){
    console.log("mainActivity.generateAESKey is called!");
    stringBuilder.toString.implementation = function(){
        var result = this.toString();
        console.log(result);
        return result;
    };
    var result = this.generateAESKey.apply(this, arguments);
    stringBuilder.toString.implementation = null; // remove hook
    return result;
};

Clicking a Login Button via Frida

function call_login_btn(){
    Java.perform(function(){
        Java.choose("com.dodonew.online.ui.LoginActivity", {
            onMatch: function(objectWrapper){
                var current_application = Java.use('android.app.ActivityThread').currentApplication();
                var context = current_application.getApplicationContext();
                var view = Java.use("android.view.View");
                var viewObj = view.$new(context);
                viewObj.setId(2131558593);
                objectWrapper.onClick(viewObj);
            },
            onComplete: function(){}
        });
    });
}

Conclusion

The article compiles essential Frida hooking techniques, from basic method interception to dynamic dex loading and scoped hooks, providing a practical reference for Android reverse‑engineering and security research.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaJavaScriptAndroidinformation securityreverse engineeringHookingFrida
Python Crawling & Data Mining
Written by

Python Crawling & Data Mining

Life's short, I code in Python. This channel shares Python web crawling, data mining, analysis, processing, visualization, automated testing, DevOps, big data, AI, cloud computing, machine learning tools, resources, news, technical articles, tutorial videos and learning materials. Join us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.