Master Frida Hooking: Advanced Android Reverse‑Engineering Techniques

This comprehensive tutorial walks through using Frida to hook Android applications, covering official API references, loading APKs, hooking normal and static methods, modifying parameters and return values, handling constructors, inner and anonymous classes, enumerating loaded classes and methods, dynamic dex loading, selective hooking, and practical examples such as programmatically clicking a login button, all illustrated with clear code snippets and explanations.

Python Crawling & Data Mining
Python Crawling & Data Mining
Python Crawling & Data Mining
Master Frida Hooking: Advanced Android Reverse‑Engineering Techniques

Preface

In the previous lesson we learned how to hook key code locations; this lesson continues to explore more Frida APIs.

Official Documentation

Frida's official JavaScript API:

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

Target App

app-release.apk

Hooking Normal and Static Methods

Normal and static methods are hooked in the same way; no distinction is required.

If the call is active , you need to differentiate static methods from normal methods.

Code

//普通方法
let money = Java.use("com.xiaojianbang.hook.Money");
money.getInfo.implementation = function () {
    console.log("money.getInfo 被调用");
    return this.getInfo();
};
//静态方法
money.setFlag.overload('java.lang.String').implementation = function (str) {
    console.log("money.getInfo 被调用");
    console.log("参数 str:", str);
    return this.setFlag(str);
};

Modifying Function Parameters and Return Values

let money = Java.use("com.xiaojianbang.hook.Money");
money.getInfo.implementation = function () {
    console.log("money.getInfo 被调用");
    return "getInfo 返回值被修改了";
};

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);
};

Constructing and Modifying Object Parameters ($new)

//构造
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 arg_money:", arg_money);
    //deposit needs a Money object, create it with $new
    return this.deposit(money.$new("张三", 10));
};
//修改
wallet.deposit.implementation = function (a) {
    console.log("arg a:", a);
    //修改传过来Money对象的值
    a.setAmount(15);
    console.log(a.getAmount());
    return this.deposit(a);
};

Printing a 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);
}

Through type casting

function printMap2(map) {
    return Java.cast(map, Java.use("java.util.HashMap"));
}

Overloaded Methods Using overload

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

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 () {
        showStack();
        var params = "";
        for (var j = 0; j < arguments.length; j++) {
            params += arguments[j] + " ";
        }
        console.log("utils.getCalc is called! params is: ", params);
        console.log(this);
        return this.getCalc.apply(this, arguments);
    };
}

Active Invocation

var money = Java.use("com.xiaojianbang.hook.Money");
money.setFlag("静态方法主动调用");
// 普通方法 实例化+对象.方法()
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("内存中的Money对象搜索完毕");
    }
});

Getting and Modifying Class Fields

let money = Java.use("com.xiaojianbang.hook.Money");
//静态字段需要通过value才能获取具体值
console.log(money.flag.value);
//设置值直接设置即可
money.flag.value = "fuck";
console.log(money.flag.value);
//普通字段
let moneyObj = money.$new("张三", 10);
console.log(moneyObj.currency.value);
moneyObj.currency.value = "普通字段修改";
console.log(moneyObj.currency.value);

Hooking Inner and Anonymous Classes

Inner Class

Inner classes can be accessed by appending $ to the class name.

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("Java.choose Wallet$InnerStructure: ", obj.bankCardsList.value);
    },
    onComplete: function () {}
});

Anonymous Class

Anonymous classes usually appear as $ 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 All Loaded Classes

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

Enumerating All Methods of a Class

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("============================");
for (let i = 0; i < constructors.length; i++) {
    console.log(constructors[i].getName());
}
let fields = wallet.class.getDeclaredFields();
console.log("============================");
for (let i = 0; i < fields.length; i++) {
    console.log(fields[i].getName());
}
let classes = wallet.class.getDeclaredClasses();
console.log("============================");
for (let i = 0; i < classes.length; i++) {
    console.log(classes[i].getName());
    var Wallet$InnerStructure = classes[i].getDeclaredFields();
    for (let j = 0; j < Wallet$InnerStructure.length; j++) {
        console.log(Wallet$InnerStructure[j].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 (var i = 0; i < methods.length; i++) {
    var methodName = methods[i].getName();
    hookFunc(utils, methodName);
}

Registering a Class into the App

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);

Injecting a Dex File with Frida

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;
};

Hooking Dynamically Loaded Dex via enumerateClassLoaders

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 Dynamically Loaded Dex via 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;
};

Hook Only Within a Specific Function

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;
};

Simple Example: Programmatically Click a Login Button

//主动点击登录按钮
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); // button id
                objectWrapper.onClick(viewObj);
            },
            onComplete: function () {}
        });
    });
}

Conclusion

This tutorial consolidates essential Frida hooking techniques, covering static and instance method interception, constructor hooking, field manipulation, class enumeration, dynamic dex loading, and practical active invocations such as auto‑clicking UI elements. Mastering these patterns equips developers and security researchers with powerful tools for Android reverse engineering.

JavaAndroidReverse 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.