0x00 什么是Frida
它是原生应用的Greasemonkey,或者用更专业的术语来说,它是一个动态代码插桩工具包。它允许你将 JavaScript 代码片段或你自己的库注入到 Windows、macOS、GNU/Linux、iOS、watchOS、tvOS、Android、FreeBSD 和 QNX 上的原生应用中。Frida 还提供了基于 Frida API 构建的一些简单工具。这些工具可以直接使用,也可以根据你的需求进行调整,或者作为 API 使用示例。
Frida原理及重要组件
frida注入的原理就是找到目标进程,使用ptrace跟踪目标进程获取mmap,dlpoen,dlsym等函数库的偏移获取mmap在目标进程申请一段内存空间将在目标进程中找到存放frida-agent-32/64.so的空间启动执行各种操作由agent去实现
| 组件名称 | 功能描述 |
|---|---|
| frida-gum | 提供了inline-hook的核心实现,还包含了代码跟踪模块Stalker,用于内存访问监控的MemoryAccessMonitor,以及符号查找、栈回溯实现、内存扫描、动态代码生成和重定位等功能 |
| frida-core | fridahook的核心,具有进程注入、进程间通信、会话管理、脚本生命周期管理等功能,屏蔽部分底层的实现细节并给最终用户提供开箱即用的操作接口。包含了frida-server、frida-gadget、frida-agent、frida-helper、frida-inject等关键模块和组件,以及之间的互相通信底座 |
| frida-gadget | 本身是一个动态库,可以通过重打包修改动态库的依赖或者修改smali代码去实现向三方应用注入gadget,从而实现Frida的持久化或免root |
| frida-server | 本质上是一个二进制文件,类似于前面学习到的android_server,需要在目标设备上运行并转发端口,在Frida hook中起到关键作用 |
Frida与Xposed的对比
| 工具 | 优点 | 缺点 |
|---|---|---|
| Xposed | 直接编写Java代码,Java层hook方便,可打包模块持久化hook | 环境配置繁琐,兼容性较差,难以Hook底层代码。 |
| Frida | 配置简单,免重启hook。支持Java层和Native层的hook操作 | 持久化hook相对麻烦 |
Frida环境配置
- 推荐用python的虚拟环境env
- Frida安装以及多版本处理
|
|
push Frida-server- 注意要下载对应版本
- 不知道手机基础框架(ABI)的,可以使用这个指令:
adb shell getprop ro.product.cpu.abi arm64-v8a→ 64 位 ARM(主流新机)armeabi-v7a→ 32 位 ARM(老机型)x86/x86_64→ Intel 架构(模拟器或极少数真机)
FIrda基础知识
基础命令
-
1.
frida-ps -U查看当前手机运行的进程
-
2.
frida-ps --help查看help指令
|
|
-
3.操作模式 CLI模式:通过命令行直接将JavaScript脚本注入进程中。 RPC模式:使用Python进行JavaScript脚本的注入,适合复杂数据处理。
-
4.注入模式与启动命令
- Spawn模式
将启动App的权利交由Frida来控制,即使目标App已经启动,在使用Frida注入程序时还是会重新启动App
当需要监控App从启动开始的所有行为时使用
frida -U -f 进程名 -l hook.js进程名可以通过frida-ps -U来查看- Attach模式
在目标App已经启动的情况下,Frida通过ptrace注入程序从而执行Hook的操作
frida -U 进程名 -l hook.js在App已经启动,或者我们只关心特定时刻或特定功能的行为时使用 -
5.frida_server自定义端口
|
|
-
6.基础语法
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)枚举指定类的所有方法。 -
7.日志输出
日志方法 描述 区别 console.log()使用JavaScript直接进行日志打印 多用于在CLI模式中, console.log()直接输出到命令行界面,使用户可以实时查看。在RPC模式中,console.log()同样输出在命令行,但可能被Python脚本的输出内容掩盖。send()Frida的专有方法,用于发送数据或日志到外部Python脚本 多用于RPC模式中,它允许JavaScript脚本发送数据到Python脚本,Python脚本可以进一步处理或记录这些数据。 -
8.日志捕获 D:表示级别 “zj2595"这个是标签

-
9.Hook框架模板
1 2 3 4 5 6 7 8 9 10 11// 定义主函数 function main() { // Frida提供的Java环境操作API,确保在Java虚拟机上下文执行 Java.perform(function() { // 调用自定义的Hook逻辑函数(需要自己实现) hookTest1(); }); } // 立即执行main函数(Frida脚本常用的启动方式) setImmediate(main);-
Java.perform(callback)Frida 的核心 API 之一,用于在目标进程的Java 虚拟机(JVM)上下文中执行代码。因为 Hook Java 方法需要访问 JVM 环境,Java.perform会确保回调函数中的代码在 JVM 就绪后执行,避免因环境未初始化导致的错误。 -
**
hookTest1()**这是一个自定义函数(需要你自己实现具体逻辑),用于编写实际的 Hook 操作,例如:- 拦截某个 Java 方法的调用
- 修改方法的参数或返回值
- 打印方法调用栈信息等
-
**
setImmediate(main)**用于启动整个 Hook 流程。setImmediate是 JavaScript 的异步 API,会将main函数放入事件循环中立即执行(类似setTimeout(fn, 0))。在 Frida 脚本中,这是启动 Hook 逻辑的标准方式,确保代码在进程初始化完成后运行。
-
Frida常用API
1. Hook普通方法
|
|
此案例中,类名修改为com.zj.wuaipojie.Demo,将参数和方法名都修改。

最新的jadx中已经可以将方法复制成frida片段,如下:
|
|
使用frida-ps -U 查看当前手机运行的进程得到 1821 wuaipojie

然后使用frida -U 进程名 -l hook.js启动命令,下图是另一个终端窗口中logcat |grep "D.zj2595"进行的日志捕获

可以看到,终端输出了方法传入的参数和输出的返回值

在let result = this["a"](str);这条语句中即可修改方法的返回值,但需注意原方法返回值的类型,若原方法返回int,应该返回数字22(而不是字符串"22")
2. Hook重载参数
|
|

再看一个自定义参数类型的方法案例

当overload()中没有填入参数类型或者你不知道该参数的类型时候可以尝试运行在报错的提示中可以找到参数类型,也可以从Java文件定位到对应的smali语句,也可以看到相应的参数类型

有时候文件运行后,没有hook成功,可以多尝试几次
3. Hook构造函数
|
|

4. Hook字段
|
|
在第一次的使用过程中,发现obj.privateInt.value = 9999;并没有成功,可能的原因是
private修饰:Java 中private字段只能在当前类内部访问,外部(包括 Frida 直接访问)默认没有权限修改;final修饰:final字段初始化后不允许重新赋值(即使是类内部也不行),Java 的语法规则会阻止修改。

翻论坛评论的时候发现了这个帖子

尝试了一下,确实可以,真的要和别人多交流学习啊(感慨)

附上代码:
|
|
5.Hook内部类
|
|

6. 枚举所有类与方法
|
|

7.枚举所有方法
|
|

8.主动调用
- 静态方法
|
|

下图就能显示出修改后的加密结果

- 非静态方法
|
|

0x01 objection
什么是objection
objection是基于frida的命令行hook集合工具, 可以让你不写代码, 敲几句命令就可以对java函数的高颗粒度hook, 还支持RPC调用。可以实现诸如内存搜索、类和模块搜索、方法hook打印参数返回值调用栈等常用功能,是一个非常方便的,逆向必备、内存漫游神器。
-
由于objection在2021就已经停止更新,所以需要使用较低版本的frida和frida_tools,建议重新设置一个虚拟环境,因为之前虚拟环境用的是最新版本的,不太支持objection,配置一个新的环境也方便管理 我这里配置的是
1 2 3 4 5objection 1.11.0 frida 16.2.1 frida-tools 13.2.0 -
简单测试一下,出现如下图所示就是成功了

objection 命令注释
- 1.help命令注释
|
|
- 2.注入命令
|
|
启动前就hook
|
|
-
3.objection基础api
memory list modules-查看内存中加载的库
1 2 3 4 5 6 7 8 9 10 11 12memory list modules Save the output by adding `--json modules.json` to this command Name Base Size Path ---------------------------------------------------------------- ------------ ------------------- ------------------------------------------------------------------------------ app_process64 0x57867c9000 40960 (40.0 KiB) /system/bin/app_process64 linker64 0x72e326a000 229376 (224.0 KiB) /system/bin/linker64 libandroid_runtime.so 0x72e164e000 2113536 (2.0 MiB) /system/lib64/libandroid_runtime.so libbase.so 0x72dfa67000 81920 (80.0 KiB) /system/lib64/libbase.so libbinder.so 0x72dec1c000 643072 (628.0 KiB) /system/lib64/libbinder.so libcutils.so 0x72de269000 86016 (84.0 KiB) /system/lib64/libcutils.so libhidlbase.so 0x72df4cc000 692224 (676.0 KiB) /system/lib64/libhidlbase.so liblog.so 0x72e0be1000 98304 (96.0 KiB) /system/lib64/liblogmemory list exports so名称- 查看库的导出函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16memory list exports liblog.so Save the output by adding `--json exports.json` to this command Type Name Address -------- ------------------------------------ ------------ function android_log_write_int32 0x72e0be77c8 function android_log_write_list_begin 0x72e0be76f0 function __android_log_bswrite 0x72e0be9bd8 function __android_log_security 0x72e0bf2144 function __android_log_bwrite 0x72e0be9a18 function android_log_reset 0x72e0be75ec function android_log_write_string8 0x72e0be7a38 function android_logger_list_free 0x72e0be8c04 function __android_log_print 0x72e0be9728 function __android_logger_property_get_bool 0x72e0bf2248 function android_logger_get_id 0x72e0be8270 function android_logger_set_prune_list 0x72e0be8948android hooking list activities-查看内存中加载的activity/android hooking list services-查看内存中加载的servicesandroid intent launch_activity类名 -启动activity或service(可以用于一些没有验证的activity,在一些简单的ctf中有时候可以出奇效)- 关闭ssl校验
android sslpinning disable - 关闭root检测
android root disable
-
4.objection内存漫游
- 内存搜刮类实例
1 2 3 4 5android heap search instances 类名(命令) Class instance enumeration complete for com.zj.wuaipojie.Demo Hashcode Class toString() --------- --------------------- ----------------------------- 215120583 com.zj.wuaipojie.Demo com.zj.wuaipojie.Demo@cd27ac7- 调用实例的方法
1 2 3 4 5android heap execute <handle> getPublicInt(实例的hashcode+方法名) 如果是带参数的方法,则需要进入编辑器环境 android heap evaluate <handle> console.log(clazz.a("吾爱破解")); 按住esc+enter触发- android hooking list classes -列出内存中所有的类(结果比静态分析的更准确)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17android hooking list classes tw.idv.palatis.xappdebug.MainApplication tw.idv.palatis.xappdebug.xposed.HookMain tw.idv.palatis.xappdebug.xposed.HookMain$a tw.idv.palatis.xappdebug.xposed.HookMain$b tw.idv.palatis.xappdebug.xposed.HookMain$c tw.idv.palatis.xappdebug.xposed.HookMain$d tw.idv.palatis.xappdebug.xposed.HookSelf u v void w xposed.dummy.XResourcesSuperClass xposed.dummy.XTypedArraySuperClass Found 10798 classes- android hooking search classes 关键类名 -在内存中所有已加载的类中搜索包含特定关键词的类
1 2 3 4 5 6 7 8 9 10 11 12android hooking search classes wuaipojie Note that Java classes are only loaded when they are used, so if the expected class has not been found, it might not have been loaded yet. com.zj.wuaipojie.Demo com.zj.wuaipojie.Demo$Animal com.zj.wuaipojie.Demo$Companion com.zj.wuaipojie.Demo$InnerClass com.zj.wuaipojie.Demo$test$1 com.zj.wuaipojie.MainApplication com.zj.wuaipojie.databinding.ActivityMainBinding ... Found 38 classes- android hooking search methods 关键方法名 -在内存中所有已加载的类的方法中搜索包含特定关键词的方法(一般不建议使用,特别耗时,还可能崩溃)
- android hooking list class_methods 类名 -内存漫游类中的所有方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15android hooking list class_methods com.zj.wuaipojie.ui.ChallengeSixth private static final void com.zj.wuaipojie.ui.ChallengeSixth.onCreate$lambda-0(com.zj.wuaipojie.ui.ChallengeSixth,android.view.View) private static final void com.zj.wuaipojie.ui.ChallengeSixth.onCreate$lambda-1(com.zj.wuaipojie.ui.ChallengeSixth,android.view.View) private static final void com.zj.wuaipojie.ui.ChallengeSixth.onCreate$lambda-2(com.zj.wuaipojie.ui.ChallengeSixth,android.view.View) private static final void com.zj.wuaipojie.ui.ChallengeSixth.onCreate$lambda-3(com.zj.wuaipojie.ui.ChallengeSixth,android.view.View) protected void com.zj.wuaipojie.ui.ChallengeSixth.onCreate(android.os.Bundle) public final java.lang.String com.zj.wuaipojie.ui.ChallengeSixth.hexToString(java.lang.String) public final java.lang.String com.zj.wuaipojie.ui.ChallengeSixth.unicodeToString(java.lang.String) public final void com.zj.wuaipojie.ui.ChallengeSixth.toastPrint(java.lang.String) public static void com.zj.wuaipojie.ui.ChallengeSixth.$r8$lambda$1lrkrgiCEFWXZDHzLRibYURG1h8(com.zj.wuaipojie.ui.ChallengeSixth,android.view.View) public static void com.zj.wuaipojie.ui.ChallengeSixth.$r8$lambda$IUqwMqbTKaOGiTaeOmvy_GjNBso(com.zj.wuaipojie.ui.ChallengeSixth,android.view.View) public static void com.zj.wuaipojie.ui.ChallengeSixth.$r8$lambda$Kc_cRYZjjhjsTl6GYNHbgD-i6sE(com.zj.wuaipojie.ui.ChallengeSixth,android.view.View) public static void com.zj.wuaipojie.ui.ChallengeSixth.$r8$lambda$PDKm2AfziZQo6Lv1HEFkJWkUsoE(com.zj.wuaipojie.ui.ChallengeSixth,android.view.View) Found 12 method(s) -
5.objectionHook
hook类的所有方法
|
|
hook方法的参数、返回值和调用栈
|
|
hook 类的构造方法
|
|
hook 方法的所有重载
|
|
参考:
实用FRIDA进阶:内存漫游、hook anywhere、抓包
使用objection对吾爱破解2023春节安卓初级题进行快速解题
trace实战java控制流混淆
用到的实战题目:吾爱破解2023春节红包题
用到的混淆项目:DEX控制流混淆 · BlackObfuscator
项目效果展示:
- 混淆前

- 混淆后


对抗方法:
AKA:精简版 objection + Wallbreaker
0x02 Process、Module、Memory基础
1.Process
Process 对象代表当前被Hook的进程,能获取进程的信息,枚举模块,枚举范围等
| API | 含义 |
|---|---|
Process.id |
返回附加目标进程的 PID |
Process.isDebuggerAttached() |
检测当前是否对目标程序已经附加 |
Process.enumerateModules() |
枚举当前加载的模块,返回模块对象的数组 |
Process.enumerateThreads() |
枚举当前所有的线程,返回包含 id, state, context 等属性的对象数组 |
|
|
2.Module
Module 对象代表一个加载到进程的模块(例如,在 Windows 上的 DLL,或在 Linux/Android 上的 .so 文件),能查询模块的信息,如模块的基址、名称、导入/导出的函数等
| API | 含义 |
|---|---|
Module.load() |
加载指定so文件,返回一个Module对象 |
enumerateImports() |
枚举所有Import库函数,返回Module数组对象 |
enumerateExports() |
枚举所有Export库函数,返回Module数组对象 |
enumerateSymbols() |
枚举所有Symbol库函数,返回Module数组对象 |
Module.findExportByName(exportName)、Module.getExportByName(exportName) |
寻找指定so中export库中的函数地址 |
Module.findBaseAddress(name)、Module.getBaseAddress(name) |
返回so的基地址 |
|
|
3.Memory
Memory是一个工具对象,提供直接读取和修改进程内存的功能,能够读取特定地址的值、写入数据、分配内存等
| 方法 | 功能 |
|---|---|
Memory.copy() |
复制内存 |
Memory.scan() |
搜索内存中特定模式的数据 |
Memory.scanSync() |
同上,但返回多个匹配的数据 |
Memory.alloc() |
在目标进程的堆上申请指定大小的内存,返回一个NativePointer |
Memory.writeByteArray() |
将字节数组写入一个指定内存 |
Memory.readByteArray |
读取内存 |
|
|
0x03 枚举导入导出表
- 导出表(Export Table):列出了库中可以被其他程序或库访问的所有公开函数和符号的名称。
- 导入表(Import Table):列出了库需要从其他库中调用的函数和符号的名称。
简而言之,导出表告诉其他程序:“这些是我提供的功能。”,而导入表则表示:“这些是我需要的功能。”。
|
|
0x04 Native函数的基础Hook打印
1.整数型、布尔值类型、char类型Hook
|
|
2.字符串类型Hook
|
|
0x05 Native函数的基础Hook修改
1.整数型修改
|
|
2.字符串类型修改
|
|
0x06 SO基址的获取方式
|
|
0x07 Hook未导出函数与函数地址计算
|
|
函数地址计算
-
1.安卓里一般32 位的 so 中都是
thumb指令,64 位的 so 中都是arm指令 -
2.通过IDA里的opcode bytes来判断,arm 指令为 4 个字节(options -> general -> Number of opcode bytes (non-graph) 输入4)
-
3.thumb 指令,函数地址计算方式: so 基址 + 函数在 so 中的偏移 + 1 arm 指令,函数地址计算方式: so 基址 + 函数在 so 中的偏移
0x08 Hook_dlopen
dlopen/android_dlopen_ext 是 Android 加载 SO 的系统函数,Hook 后可捕获 SO 加载时机,实现延迟 Hook(避免 SO 未加载时 Hook 失败)。
|
|
参考文档: