Master Frida: Essential Hook Scripts for Android App Reverse Engineering
This guide walks through using Frida to hook common Android methods, print stack traces, and extract runtime data, providing ready-to-use code snippets for functions like HashMap.put, ArrayList.add, TextUtils.isEmpty, Base64 encoding, and UI event handling.
Preface
Sometimes apps encrypt strings or obfuscate variable names, making the code hard to read. System-level functions such as Toast cannot be obfuscated, so tracing them can reveal decryption points.
Frida Common Commands
frida
-U // connect USB device
-F // attach to the frontmost app
-l // load JS script
-o // output to file
-f // restart app
--no-pause // execute immediately without pausePrint Stack Trace
The essence of printing a stack trace is throwing an exception and reading its output from bottom to top.
Code
function printStacks() {
console.log(
Java.use("android.util.Log")
.getStackTraceString(Java.use("java.lang.Throwable").$new())
);
}Common Hook Functions
Hook HashMap.put
Hooking HashMap.put can reveal key-value pairs, but be careful to avoid crashes.
var hashMap = Java.use("java.util.HashMap");
hashMap.put.implementation = function(a, b) {
// a == "username" usually works
if (a == "username") {
console.log("hashMap.put: ", a, b);
printStacks();
}
return this.put(a, b);
};Hook ArrayList.add
var arrayList = Java.use("java.util.ArrayList");
arrayList.add.overload('java.lang.Object').implementation = function(a) {
if (a == "username=18903916120") {
console.log("arrayList.add: ", a);
printStacks();
}
return this.add(a);
};
arrayList.add.overload('int', 'java.lang.Object').implementation = function(a, b) {
console.log("arrayList.add: ", a, b);
printStacks();
return this.add(a, b);
};Hook TextUtils.isEmpty
var textUtils = Java.use("android.text.TextUtils");
textUtils.isEmpty.implementation = function(a) {
if (a == "TURJNk1EQTZNREE2TURBNk1EQTZNREE9") {
console.log("textUtils.isEmpty: ", a);
printStacks();
}
return this.isEmpty(a);
};Hook String.trim
var str = Java.use("java.lang.String");
str.trim.implementation = function() {
console.log("str.trim: ", this);
printStacks();
return this.trim();
};Hook Log.w
var log = Java.use("android.util.Log");
log.w.overload('java.lang.String', 'java.lang.String').implementation = function(tag, message) {
console.log("log.w: ", tag, message);
printStacks();
return this.w(tag, message);
};Hook EditText.getText
var editText = Java.use("android.widget.EditText");
editText.getText.overload().implementation = function() {
var result = this.getText();
result = Java.cast(result, Java.use("java.lang.CharSequence"));
console.log("editText.getText: ", result.toString());
printStacks();
return result;
};Hook Collections.sort
var collections = Java.use("java.util.Collections");
collections.sort.overload('java.util.List').implementation = function(a) {
var result = Java.cast(a, Java.use("java.util.ArrayList"));
console.log("collections.sort List: ", result.toString());
printStacks();
return this.sort(a);
};
collections.sort.overload('java.util.List', 'java.util.Comparator').implementation = function(a, b) {
var result = Java.cast(a, Java.use("java.util.ArrayList"));
console.log("collections.sort List Comparator: ", result.toString());
printStacks();
return this.sort(a, b);
};Hook JSONObject.put / getString
var jSONObject = Java.use("org.json.JSONObject");
jSONObject.put.overload('java.lang.String', 'java.lang.Object').implementation = function(a, b) {
console.log("jSONObject.put: ", a, b);
printStacks();
return this.put(a, b);
};
jSONObject.getString.implementation = function(a) {
console.log("jSONObject.getString: ", a);
var result = this.getString(a);
console.log("jSONObject.getString result: ", result);
printStacks();
return result;
};Hook Toast.show
var toast = Java.use("android.widget.Toast");
toast.show.implementation = function() {
console.log("toast.show: ");
printStacks();
return this.show();
};Hook Base64.encodeToString
var base64 = Java.use("android.util.Base64");
base64.encodeToString.overload('[B', 'int').implementation = function(a, b) {
console.log("base64.encodeToString: ", JSON.stringify(a));
var result = this.encodeToString(a, b);
console.log("base64.encodeToString result: ", result);
printStacks();
return result;
};Find View by ID
Hooking UI elements often requires the view ID. Use uiautomatorviewer.bat to inspect IDs, then hook the click listener.
var btn_login_id = Java.use("com.dodonew.online.R$id").btn_login.value;
var view = Java.use("android.view.View");
view.setOnClickListener.implementation = function(a) {
if (this.getId() == btn_login_id) {
console.log("view.id: " + this.getId());
console.log("view.setOnClickListener is called");
printStacks();
}
return this.setOnClickListener(a);
};Simple Usage
In the "DuduNiu" app, DES encryption is used and the result is displayed via Base64. Hooking the Base64 method reveals the encrypted content.
Hook Result
If the output looks like Base64, MD5, or HEX, try the corresponding hook code and print the stack trace to analyze the app.
Conclusion
Although presented as key code location, these snippets are common hooks used in Android reverse engineering. The more experience you gain, the richer your hook library becomes.
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.
