Frida + Android Hook 完整指南
-
- [Frida 原理与架构](#Frida 原理与架构)
- 环境搭建
- 手机端准备(adb+frida-server)
- App脱壳
- [Java 层 Hook(最常用)](#Java 层 Hook(最常用))
-
- [1. 静态分析找目标(jadx)](#1. 静态分析找目标(jadx))
- [2. 编写 Frida JS 脚本(hook_java.js)](#2. 编写 Frida JS 脚本(hook_java.js))
- [3. 运行脚本](#3. 运行脚本)
- [Native 层 Hook(so 库)](#Native 层 Hook(so 库))
-
- [1. 场景](#1. 场景)
- [2. 脚本模板(hook_native.js)](#2. 脚本模板(hook_native.js))
- [常用 Hook 脚本模板](#常用 Hook 脚本模板)
-
- [1. 打印方法调用栈](#1. 打印方法调用栈)
- [2. Hook 构造方法](#2. Hook 构造方法)
- [3. 枚举所有类 / 方法](#3. 枚举所有类 / 方法)
- 总结
Frida 原理与架构
包含:环境搭建、Java 层 Hook、Native 层 Hook、常用脚本模板、常见坑与排查。内容偏实操,直接能用
Frida 是动态插桩框架,可以在不修改 APK、不重启设备的情况下,向运行中的 App 注入 JS 脚本,Hook Java 或 Native 函数。
架构:PC 客户端(frida) + 手机端服务端(frida-server)
语言:PC 端 Python,注入脚本 JavaScript
支持:Android、iOS、Windows、macOS、Linux
环境搭建
bash
# 安装frida 16.x 稳定兼容版
pip install frida==16.2.1 frida-tools==12.3.0
# 验证:
frida --version
# 列出手机进程
frida-ps -U
frida-ps -U 之前需启动模拟器

手机端准备(adb+frida-server)
必须 Root(真机或模拟器,推荐雷电 / Genymotion)
如果连 adb.exe 都没有,需要先下载 Android SDK Platform Tools:
adb-SDK下载
把压缩包解压到一个固定目录,比如 D:\platform-tools
把这个目录添加到系统环境变量 Path 里
重启 CMD,输入 adb --version 验证是否成功

进入adb shell 查看架构,根据架构下载对应的firda-server,这里x86_64
bash
getprop ro.product.cpu.abi

frida-server下载:https://github.com/frida/frida/releases/
这里的server版本跟之前的frida版本保持一致,防止出错
在 Assets 列表里,往下翻找到 frida-server-16.2.1-android-x86_64.xz
直达地址:https://github.com/frida/frida/releases/download/16.2.1/frida-server-16.2.1-android-x86_64.xz
(注意:不是 frida-core-devkit,文件名里必须有 frida-server)
解压 .xz 文件,得到 frida-server-16.2.1-android-x86_64;

将 frida-server推送到模拟器里:
bash
# 推送到手机或模拟器(选择/data/local/tmp 目录,因为其它目录有的不可写,可能会写入失败)
adb push frida-server-17.9.10-android-x86_64 /data/local/tmp/
# 进入adb shell
adb shell
# 管理员
su
# 加执行权限
chmod 755 /data/local/tmp/frida-server-17.9.10-android-x86_64
# 启动server
/data/local/tmp/frida-server-17.9.10-android-x86_64 &


App脱壳
逆向 hook之前先查看app基本信息,是否加壳,如果有安装firda-dexdump进行脱壳导出.dex文件

bash
# 安装对应的版本2.0.1
pip install frida-dexdump==2.0.1
frida-dexdump 2.0.1 是 2024 年 4 月发布,实测与 Frida 16.x(含 16.2.1)完全兼容。
它不强制绑定特定 Frida 小版本,只要 Frida ≥12 基本都能用,但16.2.1 + 2.0.1 是最稳组合。
配套的 frida-server 也要用 16.2.1(与客户端一致)
安装完成后进adb shell,启动frida-server,然后运frida-dexdump -U -f com.app.pkgname导出指定包名的应用的dex文件

导出后得到dex包,对他们使用d2j-dex2jar.bat转换为jar包,然后用jadx-gui查看源码


至于未加固的apk可直接拖入jadx-jui中查看
Java 层 Hook(最常用)
1. 静态分析找目标(jadx)
用 jadx-gui 打开 APK(.dex文件),找到要 Hook 的类和方法,例如:
java
package com.example.crypto;
public class MainActivity {
private String encrypt(String data) { ... }
}
- 类名:com.example.crypto.MainActivity
- 方法:encrypt(String)
2. 编写 Frida JS 脚本(hook_java.js)
js
Java.perform(function () {
console.log("[*] 开始 Hook Java 方法");
// 1. 获取目标类
var MainActivity = Java.use("com.example.crypto.MainActivity");
// 2. Hook 方法(参数类型要匹配)
MainActivity.encrypt.implementation = function (data) {
console.log("[+] 原方法被调用,参数:" + data);
// 调用原方法
var result = this.encrypt(data);
console.log("[+] 原方法返回值:" + result);
// 可篡改返回值
// return "fake_data";
return result;
};
});
3. 运行脚本
将frida-server端口转发到本地
bash
# frida-server 默认端口为 27042
adb forward tcp:27042 tcp:27042

用脚本去frida app
bash
# 方式1:spaw模式注入App(app未启动时注入,自动启动app)
frida -U -f <包名> -l hook_java.js
# 方式2:启动App后并注入
frida -U -p <pid> -l hook_java.js
jadx 反编译 APK,定位目标类 / 方法

选中相应的函数复制为frida代码片段

构造的hook 代码

Frida 注入脚本,观察日志 / 篡改返回值
bash
frida -U -f com.example.crypto -l hook_java.js

有时候,-f(spawn 模式)会在 App 刚启动时注入,容易触发崩溃,改用附加模式:
- 先在模拟器里手动打开 App,让它运行起来
- 用 frida-ps -U 查看进程 PID
- 用附加模式注入脚本:
bash
frida -U -p <pid> -l hook.js

Native 层 Hook(so 库)
1. 场景
App 用 so 库(libxxx.so)做加密 / 校验,需要 Hook JNI 函数或导出函数。
2. 脚本模板(hook_native.js)
js
setImmediate(function () {
Java.perform(function () {
console.log("[*] 等待 libxxx.so 加载...");
// 监听 so 加载
var System = Java.use("java.lang.System");
System.loadLibrary.implementation = function (libname) {
this.loadLibrary(libname);
if (libname === "xxx") { // libxxx.so
console.log("[+] libxxx.so 已加载,开始 Hook");
hookNativeFunc();
}
};
});
});
function hookNativeFunc() {
// 1. 找导出函数地址
var funcPtr = Module.findExportByName("libxxx.so", "Java_com_example_crypto_MainActivity_nativeEncrypt");
if (!funcPtr) {
console.log("[-] 未找到函数");
return;
}
console.log("[*] 函数地址:" + funcPtr);
// 2. Hook
Interceptor.attach(funcPtr, {
onEnter: function (args) {
console.log("[+] Native 方法进入");
// args[0] = JNIEnv*, args[1] = jobject, args[2] = 参数1
// 读取字符串参数
var data = Java.vm.getEnv().getStringUtfChars(args[2], null);
console.log("[+] 参数:" + data.readCString());
},
onLeave: function (retval) {
console.log("[+] Native 返回值:" + retval);
// 篡改返回值
// retval.replace(0);
}
});
}
常用 Hook 脚本模板
1. 打印方法调用栈
js
Java.perform(function () {
var stackTrace = Java.use("android.util.Log").getStackTraceString;
Java.use("java.lang.Throwable").$init.implementation = function () {
this.$init();
console.log("[+] 调用栈:\n" + stackTrace(this));
};
});
2. Hook 构造方法
js
Java.perform(function () {
var Clazz = Java.use("com.example.crypto.CoinMoney");
Clazz.$init.overload("java.lang.String", "int").implementation = function (a, b) {
console.log("[+] 构造方法参数:" + a + ", " + b);
return this.$init(a, b);
};
});
3. 枚举所有类 / 方法
javascript
Java.perform(function () {
var classFactory = Java.classFactory;
var classes = classFactory.enumerateClasses();
for (var i = 0; i < classes.length; i++) {
console.log(classes[i].getName());
}
});
总结
- 装 Frida PC 端 + 手机端 frida-server
- adx 反编译 APK,定位目标类 / 方法
- 写 JS 脚本 Hook Java/Native 方法
- Frida 注入脚本,观察日志 / 篡改返回值
- 处理反调试 / Frida 检测