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.
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.apkHooking 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
