
Frida详解:动态插桩框架的完全指南
本文最后更新于 2025-09-23,文章内容可能已经过时。
1. Frida简介
Frida是一个强大的动态代码插桩框架,允许你将JavaScript代码注入到原生应用程序中。它支持Windows、macOS、Linux、iOS、Android等多个平台,广泛应用于:
移动应用安全测试
恶意软件分析
逆向工程
自动化测试
研究和教育
2. 核心架构
2.1 Frida组件组成
Frida核心架构:
客户端 (Python/Node.js等) ←通信→ Frida服务器 (目标设备)
↓
目标进程 (注入的JavaScript)
核心组件:
frida-core:核心C库
frida-python:Python绑定
frida-node:Node.js绑定
frida-server:目标设备上运行的服务端
frida-gadget:嵌入式模式使用的库
3. 安装与配置
3.1 客户端安装
Python环境安装:
# 安装frida-tools(包含命令行工具)
pip install frida-tools
# 或者只安装frida
pip install frida
# 验证安装
frida --version
# 安装开发版本(可选)
pip install frida --pre
Node.js环境安装:
npm install frida
其他语言绑定:
# C#/.NET
Install-Package Frida
# Swift
pod 'Frida'
3.2 服务端安装
Android设备安装:
# 1. 下载对应架构的frida-server
# 从 https://github.com/frida/frida/releases 下载
# 2. 推送到设备
adb push frida-server-16.0.8-android-arm64 /data/local/tmp/frida-server
# 3. 设置权限并运行
adb shell
su
cd /data/local/tmp
chmod 755 frida-server
./frida-server &
# 4. 端口转发(可选)
adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
iOS设备安装:
# 越狱设备通过Cydia安装
# 添加源:https://build.frida.re
# 搜索安装Frida
# 或者手动部署
scp frida-server root@ios-device:/usr/sbin/
ssh root@ios-device chmod 755 /usr/sbin/frida-server
/usr/sbin/frida-server &
桌面平台安装:
# Linux/macOS/Windows
# 直接运行frida-server二进制文件
./frida-server -l 0.0.0.0 # 监听所有接口
4. 基础用法
4.1 命令行工具使用
列出进程:
# 列出USB连接的Android设备进程
frida-ps -U
# 列出远程设备进程
frida-ps -H 192.168.1.100:27042
# 列出本地进程
frida-ps
# 列出应用程序(Android)
frida-ps -Uai
附加到运行中的进程:
# 附加到特定进程
frida -U -p 1234
# 附加到进程名
frida -U -n "com.example.app"
# 附加并执行脚本
frida -U -n "com.example.app" -l script.js
生成新进程:
# 启动应用并注入
frida -U -f com.example.app
# 启动应用并执行脚本
frida -U -f com.example.app -l script.js --no-pause
4.2 基本JavaScript API
// 脚本的基本结构
Java.perform(function () {
console.log("脚本开始执行");
// 你的代码在这里
});
5. Android应用Hook实战
5.1 Java层Hook
Hook构造函数和方法:
Java.perform(function () {
// Hook特定的类
var TargetClass = Java.use("com.example.app.TargetClass");
// Hook构造函数
TargetClass.$init.overload('java.lang.String').implementation = function(param) {
console.log("构造函数被调用,参数: " + param);
return this.$init(param);
};
// Hook普通方法
TargetClass.targetMethod.implementation = function() {
console.log("targetMethod被调用");
console.log("this.toString(): " + this.toString());
// 调用原方法
var result = this.targetMethod();
// 修改返回值
return "修改后的结果: " + result;
};
// Hook重载方法
TargetClass.overloadedMethod.overload('int').implementation = function(x) {
console.log("overloadedMethod(int) 被调用,x = " + x);
return this.overloadedMethod(x * 2); // 修改参数
};
TargetClass.overloadedMethod.overload('java.lang.String').implementation = function(s) {
console.log("overloadedMethod(String) 被调用,s = " + s);
return this.overloadedMethod(s + "_hooked");
};
});
Hook静态方法:
Java.perform(function () {
var Utils = Java.use("com.example.app.Utils");
Utils.staticMethod.implementation = function(input) {
console.log("静态方法被调用: " + input);
// 修改参数
var modifiedInput = "hooked_" + input;
// 调用原静态方法
var result = Utils.staticMethod(modifiedInput);
return result;
};
});
5.2 主动调用Java方法
Java.perform(function () {
// 获取类的引用
var System = Java.use("java.lang.System");
// 主动调用静态方法
var currentTime = System.currentTimeMillis();
console.log("当前时间戳: " + currentTime);
// 创建新实例并调用方法
var ArrayList = Java.use("java.util.ArrayList");
var list = ArrayList.$new();
list.add("test1");
list.add("test2");
console.log("列表大小: " + list.size());
// 枚举已加载的类
Java.enumerateLoadedClasses({
onMatch: function(className) {
if (className.includes("example")) {
console.log("找到相关类: " + className);
}
},
onComplete: function() {
console.log("枚举完成");
}
});
});
6. 原生库Hook(Android/Linux)
6.1 Native函数Hook
// Hook原生库函数
Interceptor.attach(Module.findExportByName("libc.so", "strcmp"), {
onEnter: function(args) {
// args[0] 和 args[1] 是strcmp的两个参数
var str1 = Memory.readUtf8String(args[0]);
var str2 = Memory.readUtf8String(args[1]);
console.log("strcmp被调用:");
console.log(" 参数1: " + str1);
console.log(" 参数2: " + str2);
// 保存参数以便在onLeave中使用
this.str1 = str1;
this.str2 = str2;
},
onLeave: function(retval) {
console.log("strcmp返回值: " + retval);
console.log("比较结果: " + (parseInt(retval) === 0 ? "相等" : "不相等"));
}
});
6.2 内存操作
// 内存读写操作
Java.perform(function () {
// 分配内存
var buffer = Memory.alloc(100);
// 写入字符串
buffer.writeUtf8String("Hello from Frida!");
// 读取字符串
var content = buffer.readUtf8String();
console.log("内存内容: " + content);
// 查找内存模式
var patterns = Memory.scanSync(Module.findBaseAddress("libtarget.so"),
Module.findBaseAddress("libtarget.so").size,
"41 42 43 ?? 45");
patterns.forEach(function(pattern) {
console.log("找到模式在: " + pattern.address);
});
});
7. iOS应用Hook实战
7.1 Objective-C Hook
// Hook Objective-C方法
if (ObjC.available) {
// 获取类引用
var NSString = ObjC.classes.NSString;
// Hook字符串初始化方法
Interceptor.attach(NSString["- stringWithString:"].implementation, {
onEnter: function(args) {
console.log("stringWithString: 被调用");
var originalString = new ObjC.Object(args[2]);
console.log("原始字符串: " + originalString.toString());
},
onLeave: function(retval) {
var resultString = new ObjC.Object(retval);
console.log("返回字符串: " + resultString.toString());
}
});
// Hook自定义类方法
var TargetClass = ObjC.classes.TargetClass;
if (TargetClass) {
TargetClass["- targetMethod:"].implementation = function(selector, arg) {
console.log("targetMethod: 被调用");
console.log("参数: " + new ObjC.Object(arg).toString());
// 调用原始实现
var result = this.targetMethod_(arg);
// 修改返回值
return NSString.stringWithString_("Hooked: " + result);
};
}
}
7.2 Swift应用Hook
// Swift类名需要加上模块名
var SwiftClass = ObjC.classes.MyApp_MySwiftClass;
if (SwiftClass) {
SwiftClass["- swiftMethod:"].implementation = function(selector, arg) {
console.log("Swift方法被调用");
// 调用原始方法
var result = this.swiftMethod_(arg);
return result;
};
}
8. Frida服务器详解
8.1 服务器启动选项
# 基本启动
frida-server -l 0.0.0.0:27042
# 后台运行
frida-server -D
# 指定监听地址和端口
frida-server -l 192.168.1.100:9999
# 启用认证(实验性)
frida-server --authentication
# 详细日志
frida-server --verbose
# Android启动脚本示例
#!/system/bin/sh
cd /data/local/tmp
./frida-server -l 0.0.0.0:27042 -D
8.2 服务器管理
检查服务器状态:
# 检查是否运行
adb shell ps | grep frida-server
# 检查端口监听
adb shell netstat -tulpn | grep 27042
# 杀死frida-server进程
adb shell pkill -9 frida-server
重启脚本:
#!/bin/bash
# frida-server管理脚本
ADB="adb"
SERVER_PATH="/data/local/tmp/frida-server"
restart_frida() {
echo "停止frida-server..."
$ADB shell su -c "pkill -9 frida-server"
sleep 2
echo "启动frida-server..."
$ADB shell su -c "cd /data/local/tmp && chmod 755 $SERVER_PATH && $SERVER_PATH -l 0.0.0.0:27042 -D"
echo "检查状态..."
$ADB shell ps | grep frida-server
}
restart_frida
9. Python脚本集成
9.1 基本Python控制
#!/usr/bin/env python3
import frida
import sys
def on_message(message, data):
if message['type'] == 'send':
print(f"[*] {message['payload']}")
else:
print(message)
# 连接到设备
device = frida.get_usb_device()
# 附加到进程
session = device.attach("com.example.app")
# 加载JavaScript代码
with open("hook.js", "r") as f:
jscode = f.read()
script = session.create_script(jscode)
script.on('message', on_message)
script.load()
# 保持脚本运行
print("脚本已加载,按Ctrl+C退出")
sys.stdin.read()
9.2 高级Python控制
import frida
import time
class FridaController:
def __init__(self, app_name):
self.device = frida.get_usb_device()
self.session = None
self.script = None
self.app_name = app_name
def spawn_and_attach(self):
"""启动应用并附加"""
pid = self.device.spawn([self.app_name])
self.session = self.device.attach(pid)
return pid
def load_script(self, script_path):
"""加载JavaScript脚本"""
with open(script_path, 'r', encoding='utf-8') as f:
jscode = f.read()
self.script = self.session.create_script(jscode)
def on_message(message, data):
print(f"[{message['type']}] {message}")
self.script.on('message', on_message)
self.script.load()
def resume(self, pid):
"""恢复应用执行"""
self.device.resume(pid)
def run(self, script_path):
"""完整流程"""
pid = self.spawn_and_attach()
self.load_script(script_path)
self.resume(pid)
# 保持运行
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("停止监控")
# 使用示例
if __name__ == "__main__":
controller = FridaController("com.example.app")
controller.run("hook.js")
10. 实用脚本示例
10.1 SSL证书绕过
// Android SSL证书验证绕过
Java.perform(function () {
// Hook证书验证相关类
var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
X509TrustManager.checkServerTrusted.implementation = function(chain, authType) {
console.log("SSL证书验证被绕过");
// 什么都不做,相当于信任所有证书
};
// Hook OkHttp的证书验证
var TrustManager = Java.use("com.android.org.conscrypt.TrustManagerImpl");
TrustManager.verifyChain.implementation = function() {
console.log("TrustManager验证被绕过");
return;
};
// Hook Android 7+ 的证书锁定
var NetworkSecurityTrustManager = Java.use("android.security.net.config.NetworkSecurityTrustManager");
NetworkSecurityTrustManager.checkServerTrusted.implementation = function() {
console.log("NetworkSecurityTrustManager验证被绕过");
};
});
10.2 root检测绕过
// 常见的root检测绕过
Java.perform(function () {
// 1. 检查Build.TAGS
var Build = Java.use("android.os.Build");
Build.TAGS.value = "test-keys";
// 2. 检查SuperUser.apk等文件
var File = Java.use("java.io.File");
File.$init.overload('java.lang.String').implementation = function(path) {
if (path.contains("su") || path.contains("Superuser") || path.contains("magisk")) {
console.log("Root检测文件访问: " + path);
// 返回不存在的路径
path = "/system/bin/false_path";
}
return this.$init(path);
};
// 3. 检查运行时命令执行
var Runtime = Java.use("java.lang.Runtime");
Runtime.exec.overload('java.lang.String').implementation = function(cmd) {
if (cmd.contains("su") || cmd.contains("which su")) {
console.log("Root检测命令: " + cmd);
// 抛出异常或返回空结果
throw new Java.use("java.io.IOException").$new("Command not found");
}
return this.exec(cmd);
};
});
10.3 方法调用追踪
// 方法调用追踪器
Java.perform(function () {
var targetClass = "com.example.app.MainActivity";
// 枚举类的所有方法
var hookAllMethods = function(className) {
var target = Java.use(className);
var methods = target.class.getDeclaredMethods();
methods.forEach(function(method) {
var methodName = method.getName();
var overloads = target[methodName].overloads;
overloads.forEach(function(overload) {
overload.implementation = function() {
var args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
console.log("=== 方法调用 ===");
console.log("类: " + className);
console.log("方法: " + methodName);
console.log("参数: " + JSON.stringify(args));
console.log("调用栈: " + Java.use("android.util.Log").getStackTraceString(
Java.use("java.lang.Exception").$new()));
try {
var result = this[methodName].apply(this, arguments);
console.log("返回值: " + result);
return result;
} catch (e) {
console.log("异常: " + e);
throw e;
}
};
});
});
};
hookAllMethods(targetClass);
});
11. 高级技巧与最佳实践
11.1 性能优化
// 性能优化技巧
Java.perform(function () {
// 1. 避免频繁的Java来回调用
var StringClass = Java.use("java.lang.String");
// 不好的做法:每次调用都创建新对象
// 好的做法:缓存引用
var cachedString = Java.use("java.lang.String");
// 2. 使用批量操作
var ArrayList = Java.use("java.util.ArrayList");
var list = ArrayList.$new();
// 批量添加而不是单个添加
var items = Java.array('Ljava/lang/Object;', ["item1", "item2", "item3"]);
list.addAll(items);
// 3. 避免在热路径上使用console.log
var debugMode = false;
function debugLog(message) {
if (debugMode) {
console.log(message);
}
}
});
11.2 错误处理
// 健壮的错误处理
Java.perform(function () {
try {
var targetClass = Java.use("com.example.app.TargetClass");
targetClass.sensitiveMethod.implementation = function() {
try {
console.log("方法开始执行");
// 业务逻辑
var result = this.sensitiveMethod();
console.log("方法执行成功");
return result;
} catch (e) {
console.log("方法执行出错: " + e);
// 可以选择重新抛出异常或返回默认值
return null;
}
};
} catch (e) {
console.log("Hook设置失败: " + e);
}
});
12. 故障排除
12.1 常见问题解决
连接问题:
# 检查设备连接
frida-ps -U # 应该列出设备进程
# 如果失败,检查adb连接
adb devices
adb forward tcp:27042 tcp:27042
# 检查frida-server是否运行
adb shell ps | grep frida-server
脚本加载失败:
// 添加错误处理
try {
Java.perform(function () {
// 你的代码
});
} catch (e) {
console.log("脚本执行错误: " + e);
}
权限问题:
# Android上确保有root权限
adb shell su -c "whoami" # 应该返回root
# 重新启动frida-server
adb shell su -c "pkill -9 frida-server"
adb shell su -c "/data/local/tmp/frida-server &"
13. 总结
Frida是一个功能极其强大的动态分析框架,通过本文的详细讲解,你应该已经掌握了:
Frida的基本架构和安装配置
Android和iOS应用的Hook技术
服务端的部署和管理
Python脚本集成
实用脚本开发和调试技巧
关键要点:
理解Frida的注入原理和通信机制
掌握Java/Objective-C/Native不同层面的Hook技术
学会编写健壮的生产环境脚本
了解性能优化和错误处理的最佳实践
Frida的学习曲线相对陡峭,但一旦掌握,你将拥有强大的动态分析能力。建议从简单的示例开始,逐步深入到复杂的使用场景。
延伸学习:
Frida官方文档:https://frida.re/docs/
Frida JavaScript API参考
各种应用场景的实际案例研究
Happy Frida-ing!