Frida 入门
- 1 视频也可以含恶意代码吗
- 2 Frida 入门 本文
- 3 虚拟机保护
- 4 C#逆向入门
- 5 安卓刷机一点通
- 6 Z3-Solver教程
- 7 出题人的“恶意”
- 8 编译器为我做了什么
- 9 CSAPP Lab2 Note
- 10 Hash系列算法
- 11 程序脱壳
- 12 安卓逆向介绍
- 13 IDA类型修复
- 14 IDA疑难杂症
- 15 花指令
- 16 反动态调试技术与应对手段
- 17 AES加密算法
- 18 如何解决魔改后的加密算法
- 19 常见加密算法识别与变种分析
- 20 TEA 加密算法
- 21 RC4 加密算法
- 22 Base64 编码
- 23 IDA Dynamic Debugging
- 24 IDA Usage With Reverse Basic
Frida 是一个知名的 Hook 框架 接下来我们主要以 Android Hook 为主题进行讲解
环境配置
分别在 PC 和目标 Android 上配置 Frida:
# 我使用 UV 管理 Python,因此没有一个全局的 Python# uv venv --seed --clear venv# venv\Scripts\activate
pip install frida frida-tools记住 Frida 版本:
$ pip show frida
Name: fridaVersion: 17.5.1在 ADB shell 中查看 Android 设备架构:
$ uname -m
aarch64一般来讲,模拟器采用 x86-64,真机采用 arm64(即此处的 aarch64)
在 Releases · frida/frida 下载对应版本的 Frida-server,例如此处应下 frida-server-17.5.1-android-arm64.xz。
解压得到的文件传输到 Android 上:
adb push frida-server-17.5.1-android-arm64 /data/local/tmp/在 Android 上启动服务端:
adb shellsu # 需要在 Root 下运行,否则 SELinux 会报错cd /data/local/tmpchmod +x ./frida-server-17.5.1-android-arm64 # 赋予执行权限./frida-server-17.5.1-android-arm64 # 无报错即服务端正常运行现在就可以用 Frida 了 😊
Frida 调试基础
Android 每个 APP 都运行在自己独立的虚拟机和沙盒中,如果每个 APP 的运行时环境都不共享,那么资源消耗是很恐怖的。而 Android 提供了 Zygote 进程,所有 APP 都是由 Zygote fork 出来的:
init└── Zygote(已预加载所有 Framework 类) ├── system_server ├── com.tencent.mm └── com.example.yourapp ← 你要分析的 APPZygote 的作用是:提前把 Android Framework 所有的类加载好,每次 fork 新进程时直接继承,不用重新初始化。这样做可以实现极速启动和节省内存.
两种模式:Spawn(生成)和 Attach(附加)。Spawn 模式是从头启动应用并调试,Attach 模式是在应用运行时才开始调试。Spawn 模式下,Frida 利用 Zygote 控制进程创建,可以在业务代码运行前完成 Hook;Attach 模式下,APP 的 Application.onCreate 等初始化逻辑已经执行完,注入太晚。因此对抗加固的场景大多用 Spawn 模式。
但 Spawn 模式也可以被某些 APP 检测到。
Spawn 模式:
frida -U -f <TARGET> -l <SCRIPT>TARGET:APP 包名或可执行文件名SCRIPT:用于调试的 Python, JS 或 TS 脚本。自己写或者用已有的。如果不附带脚本,则进入交互式调试。
TARGET 需要的包名可以通过 frida-ps -Ua(运行中的应用)/frida-ps -Uai(安装的应用)得到。
Spawn 方式启动后,会立刻暂停运行。需要继续运行,需要手动输入:%resume
旧版本有一个 --no-pause 会自动运行,但新版本已不支持。
Attach 模式:
frida -U -N TARGET -l SCRIPT怎么写好一个调试脚本
脚本主要有三类内容:
Interceptor(拦截器):实现 Hook 函数、打印参数等调试操作。每次匹配到(对应函数),都会触发执行代码。ApiResolver(解析器):模糊查找相关函数。只有第一次(解析时)匹配到,才会执行。Stalker(跟踪器):跟踪代码的实际运行的过程。期间可以打印和查看对应的值,便于实现调试真正代码运行的逻辑。
对于 JS 脚本,一个简单的 CTF 示例如下:
// 所有 Java API 调用必须在 Java.perform() 回调中执行Java.perform(function () { // 获取目标类的引用 var Vault = Java.use("com.heroctf.freeda1.utils.Vault");
// 尝试调用目标方法并处理异常。延迟 1000ms 执行以减少 bug setTimeout(function () { try { var flag = Vault.get_flag(); console.log("[SUCCESS] Flag: " + flag); } catch (e) { console.log("[ERROR] " + e); } }, 1000);});这个的脚本的目的是直接获取 Vault 类下的 get_flag() 方法的输出,得到 flag。
一些常用方法:
| API名称 | 描述 |
|---|---|
Java.use(className) | 获取指定的 Java 类并使其在 JavaScript 代码中可用。 |
Java.perform(callback) | 确保回调函数在 Java 的主线程上执行。 |
Java.choose(className, callbacks) | 枚举指定类的所有实例。 |
Java.cast(obj, cls) | 将一个 Java 对象转换成另一个 Java 类的实例。 |
Java.enumerateLoadedClasses(callbacks) | 枚举进程中已经加载的所有 Java 类。 |
Java.enumerateClassLoaders(callbacks) | 枚举进程中存在的所有 Java 类加载器。 |
Java.enumerateMethods(targetClassMethod) | 枚举指定类的所有方法。 |
绕过检测
要用 Frida-server 调试的前提就是 Root,但显然大部分应用都不希望自己被 Frida 或者 Root 调戏,因此会做 Root 检测,一检测到特征就停止程序运行。
但是,代码总是有执行顺序的,只要我在检测前就把特征检测方法给 Hook 掉,你不就检查不出来了吗?另一种就是“打入内部”,将 Frida-gadget 植入程序,可以实现免 Root 的 Hook。
package com.heroctf.freeda2.utils;
import android.content.Context;import com.scottyab.rootbeer.RootBeer;
/* compiled from: r8-map-id-baa4c77f810b701c3077d4ce68a3d5b79ee91034c03e266e8ba1aed7e464c1a1 *//* loaded from: classes.dex */public final class Security { private Security() { }
public static boolean detectRoot(Context context) { return new RootBeer(context).isRooted(); }}Java.perform(function () { console.log("[+] Bypassing root detection...");
// 绕过 RootBeer var RootBeer = Java.use("com.scottyab.rootbeer.RootBeer"); RootBeer.isRooted.implementation = function () { console.log("[+] RootBeer.isRooted() bypassed"); return false; };
// 绕过 Vault 构造函数检测 var Vault = Java.use("com.heroctf.freeda2.utils.Vault"); Vault.$init.implementation = function () { console.log("[+] Vault constructor bypassed"); // Don't call original constructor };
setTimeout(function () { try { var flag = Vault.get_flag(); console.log("[SUCCESS] Flag: " + flag); } catch (e) { console.log("[ERROR] " + e); } }, 1000);});RootBeer 是一个简单的 Root 权限检查库。作者也在 README 中坦陈 RootBeer 可以被绕过(查看 Bypassing Android’s RootBeer Library — Part 1)
本题仅使用 RootBeer 的综合检测方法 isRooted() 来检测 Root,这倒是为我们省去了不少麻烦,因为我们只需要 Hook isRooted() 就行了。
Native Hook
与 Java 层不同,Native 层的函数是分散在 so 文件中的。在 Hook 前,Frida 需要找到目标函数,因为 so 是动态加载的。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!