目录
一、Hook 基本原理(一句话懂)
二、环境准备(Windows/macOS + 安卓真机 / 模拟器)
1. 电脑端(安装 Frida)
2. 手机 / 模拟器端(frida-server)
3. 必备工具
三、实战目标(准备一个测试 APK)
四、第一步:用 jadx 定位目标类和方法
五、第二步:编写 Frida Hook 脚本(encrypt_hook.js)
六、第三步:注入脚本并运行
1. 查看手机进程
2. 注入脚本
3. 操作 APP,触发 encrypt 方法
七、进阶:Hook 重载方法 / 多参数
八、常见问题 & 避坑
九、扩展:Native 层 Hook(so 库)
十、总结
下面我用一个完整、可复现、手把手的 APK Hook 实战,从原理、环境、工具、样本分析、脚本编写、注入、调试到常见坑,全部讲透。本次实战选用Frida(最主流、上手快、免 root 也能玩),目标是:Hook 一个 APK 的 Java 加密方法,篡改参数、返回值并打印调用栈。
一、Hook 基本原理(一句话懂)
Hook = 在不修改原 APK 的前提下,拦截目标方法的执行,先跑你的代码,再决定要不要跑原方法。
- Java 层:Hook 类的方法(如登录、加密、支付)
- Native 层:Hook so 里的函数(如 JNI 加密、签名校验) 主流框架:Frida(推荐)、Xposed/LSPosed、Magisk + 模块。
二、环境准备(Windows/macOS + 安卓真机 / 模拟器)
1. 电脑端(安装 Frida)
# 安装 Frida 核心
pip install frida-tools
# 验证
frida –version
2. 手机 / 模拟器端(frida-server)
adb push frida-server-15.2.2-android-arm64 /data/local/tmp/
adb shell
cd /data/local/tmp
chmod 755 frida-server-*
./frida-server-* & # 后台运行
3. 必备工具
- jadx-gui:反编译 APK,找类和方法
- Android Studio / 模拟器:调试用
- adb:连接手机
三、实战目标(准备一个测试 APK)
我用一个简单 Demo APK(你可以自己写一个,或用任意带加密方法的 APK):
// 包名:com.example.crypto
// 类名:MainActivity
// 方法:encrypt(String data) —— 对字符串做简单加密
package com.example.crypto;
public class MainActivity {
private String encrypt(String data) {
// 原逻辑:return Base64.encode(data)
return "ENCRYPTED:" + data;
}
}
我们要:
四、第一步:用 jadx 定位目标类和方法
- 全类名:com.example.crypto.MainActivity
- 方法名:encrypt
- 参数类型:String
- 返回值类型:String
五、第二步:编写 Frida Hook 脚本(encrypt_hook.js)
Java.perform(function () {
console.log("[*] 开始 Hook 加密方法");
// 1. 获取目标类
var MainActivity=Java.use("com.example.crypto.MainActivity");
// 2. Hook 目标方法(参数类型必须匹配)
MainActivity.encrypt.implementation=function (data) {
// ———- 进入方法前:打印参数 ———-
console.log("\\n[+] encrypt 被调用");
console.log("[+] 传入参数:" + data);
// ———- 打印调用栈(关键!) ———-
console.log("[+] 调用栈:");
console.log(Java.backtrace().join("\\n"));
// ———- 调用原方法,获取原返回值 ———-
var originalResult=this.encrypt(data);
console.log("[+] 原方法返回:" + originalResult);
// ———- 篡改返回值 ———-
var fakeResult = "HACKED_DATA";
console.log("[+] 返回篡改值:" + fakeResult);
// 返回篡改后的值
return fakeResult;
};
});
保存为 encrypt_hook.js。
六、第三步:注入脚本并运行
1. 查看手机进程
adb shell ps | grep crypto # 找到包名对应的进程ID
# 或
frida-ps -U # -U 表示USB设备,直接显示进程名
2. 注入脚本
frida -U -n com.example.crypto -l encrypt_hook.js
# -U:USB设备
# -n:进程名
# -l:加载脚本
3. 操作 APP,触发 encrypt 方法
打开 APP → 点击加密按钮 → 观察电脑终端输出:
[*] 开始 Hook 加密方法
[+] encrypt 被调用
[+] 传入参数:test123
[+] 调用栈:
com.example.crypto.MainActivity.encrypt(Native Method)
com.example.crypto.MainActivity.onCreate(MainActivity.java:45)
…
[+] 原方法返回:ENCRYPTED:test123
[+] 返回篡改值:HACKED_DATA
APP 界面显示的加密结果变成了 HACKED_DATA,Hook 成功!
七、进阶:Hook 重载方法 / 多参数
如果方法是重载的(多个同名不同参数),要指定参数签名:
// 例:Hook encrypt(String, int)
MainActivity.encrypt.overload("java.lang.String", "int").implementation=function (data, num) {
console.log("参数1:" + data + " 参数2:" + num);
return "HACK";
};
八、常见问题 & 避坑
- 包名 / 类名写错(jadx 里复制全类名)
- 类在其他 dex(多 dex)→ 用 Java.enumerateClasses() 遍历
- 方法是 private/protected → Frida 无视修饰符,直接 use
- 参数类型不匹配(如 int 写成 Integer)→ 严格按 jadx 签名
- 手机未开 USB 调试
- frida-server 没运行
- 电脑 / 手机 Frida 版本不匹配
- 检测到 Frida → 用 frida-obfuscator 加密脚本
- 检测 root → 用 Magisk 隐藏 root
九、扩展:Native 层 Hook(so 库)
如果加密在 libxxx.so 里,用 Frida 直接 Hook 原生函数:
// Hook libnative.so 里的 Java_com_example_crypto_encrypt
var lib=Module.load("libnative.so");
var encrypt=lib.findExportByName("Java_com_example_crypto_encrypt");
Interceptor.attach(encrypt, {
onEnter: function (args) {
console.log("Native 加密参数:" + args[0].readCString());
},
onLeave: function (retval) {
console.log("Native 返回:" + retval.readCString());
retval.writeUtf8String("NATIVE_HACK");
}
});
十、总结
这次实战完整走了 Frida Hook Java 方法 的全流程:
