本文最后更新于 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是一个功能极其强大的动态分析框架,通过本文的详细讲解,你应该已经掌握了:

  1. Frida的基本架构和安装配置

  2. Android和iOS应用的Hook技术

  3. 服务端的部署和管理

  4. Python脚本集成

  5. 实用脚本开发和调试技巧

关键要点:

  • 理解Frida的注入原理和通信机制

  • 掌握Java/Objective-C/Native不同层面的Hook技术

  • 学会编写健壮的生产环境脚本

  • 了解性能优化和错误处理的最佳实践

Frida的学习曲线相对陡峭,但一旦掌握,你将拥有强大的动态分析能力。建议从简单的示例开始,逐步深入到复杂的使用场景。

延伸学习:

Happy Frida-ing!