Frida 介绍以及功能
本文档中的信息仅用于学术研究和交流学习之目的,不涉及任何商业或非法用途。我们已对可能涉及的敏感信息,如抓包数据、特定网址、数据接口等,进行了必要的脱敏处理,以确保信息安全。任何未经授权的转载或修改后的二次传播均被严格禁止。
若擅自使用本文所述技术而引发的任何不良后果或意外,本文作者不承担任何责任。若认为本文内容涉及侵权,请通过公众号【小马哥逆向】与我们取得联系,我们将立即进行处理。感谢您的理解和配合。
Frida 介绍
Frida 是一款开源的动态代码检测工具,可以用于分析和修改移动应用程序的行为。它跨平台兼容,可以在 Android、iOS 以及桌面应用程序上使用。
功能
-
API 监控: 可以监控应用程序内部的各种 API 调用,例如网络请求、文件 I/O 操作以及敏感数据访问等,帮助分析应用程序的行为和潜在的安全/隐私问题。
import frida # 连接设备并获取目标进程 session = frida.get_remote_device().attach("com.example.myapp") # 创建 JavaScript 脚本 script = session.create_script(""" Interceptor.attach(Module.findExportByName("libcurl.so", "curl_easy_perform"), { onEnter: function(args) { console.log("curl_easy_perform called with URL: " + Memory.readCString(args[0].add(offsetof("CURL", "url")))); } }); """) # 加载并运行脚本 script.load() session.detach()
这个例子演示了如何使用 Frida 来监控 Android 应用程序中 libcurl.so 库的 curl_easy_perform 函数的调用情况,并打印出请求的 URL。
-
函数 Hook: 可以动态 hook 应用程序内部的函数,能够检查、修改或者替换函数的行为,用于绕过应用程序的安全检查、改变应用程序的逻辑或注入自定义功能。
import frida # 连接设备并获取目标进程 session = frida.get_remote_device().attach("com.example.myapp") # 创建 JavaScript 脚本 script = session.create_script(""" Interceptor.attach(Module.findExportByName("libcurl.so", "curl_easy_perform"), { onEnter: function(args) { console.log("curl_easy_perform called with URL: " + Memory.readCString(args[0].add(offsetof("CURL", "url")))); } }); """) # 加载并运行脚本 script.load() session.detach()
这个例子演示了如何使用 Frida 来监控 Android 应用程序中 libcurl.so 库的 curl_easy_perform 函数的调用情况,并打印出请求的 URL。
-
应用程序检测和仪表化: 可以对应用程序进行动态检测和分析,添加日志记录、跟踪或性能分析等功能,用于调试、性能分析和复杂行为的调查。
-
运行时补丁: 可以在不重新编译应用程序的情况下,对其进行动态补丁和修改,用于快速修复漏洞或临时改变应用程序的行为。
-
动态代码注入: 可以在运行时动态地加载和执行自定义代码,用于扩展应用程序功能、注入安全控制或实现动态分析技术。
import frida # 连接设备并获取目标进程 session = frida.get_remote_device().attach("com.example.myapp") # 创建 JavaScript 脚本 script = session.create_script(""" // 添加性能监控 Interceptor.attach(Module.findExportByName(null, "malloc"), { onEnter: function() { this.start = Process.getCurrentThreadId(); }, onLeave: function() { var duration = Process.getCurrentThreadId() - this.start; console.log("malloc took " + duration + " ms"); } }); """) # 加载并运行脚本 script.load() session.detach() 这个例子演示了如何使用 Frida 在运行时动态地加载和执行自定义 Java 代码,在这里我们重写了 com.example.myapp.MyClass 类的 myMethod 方法,添加了一个日志输出。
-
跨平台支持: Frida 可以在 Android、iOS 和桌面应用程序上使用,为分析和修改不同平台的应用程序提供了统一的解决方案。
-
自动化和脚本化: Frida 提供了强大的脚本引擎,可以用于自动化复杂的任务和工作流,如大规模的应用程序分析、模糊测试和安全测试。
import frida import time # 连接设备并获取目标进程 session = frida.get_remote_device().attach("com.example.myapp") # 创建 JavaScript 脚本 script = session.create_script(""" // 自动执行登录流程 Java.perform(function() { var MainActivity = Java.use("com.example.myapp.MainActivity"); MainActivity.login.implementation = function() { this.setUsername("testuser"); this.setPassword("testpassword"); this.doLogin(); }; }); """) # 加载并运行脚本 script.load() time.sleep(5) session.detach() 这个例子演示了如何使用 Frida 编写自动化脚本,在这里我们 hook 了应用程序的登录流程,自动填充用户名和密码,并执行登录操作。这种方式可以用于大规模的应用程序测试、安全评估或其他自动化任务。
Hook Java 函数
以下是一个 Hook Java 函数的例子:
setImmediate(function () {
Java.perform(function () {
var MainActivity = Java.use('com.example.MainActivity');
// Hook Java 方法
MainActivity.foo.implementation = function (str) {
// 调用方法
var ret = this.foo(str);
console.log('Return value:', ret);
// 返回
return ret;
};
})
})
这段代码是在 Frida 中 hook Java 方法的一个很好的示例。让我逐步解释一下:
setImmediate(function () { ... }) 是为了确保 Java 环境准备就绪后再执行后续的 JavaScript 代码。这是因为 Frida 会在单独的线程中执行 Java 代码,所以需要等待 Java 环境初始化完成。
在 Java.perform(function () { ... }) 中,我们获取了 com.example.MainActivity 类的引用。这个类是我们要 hook 的目标。
然后,我们 hook 了 MainActivity 类的 foo 方法。我们用自定义的实现替换了原始方法的实现。
在自定义的方法实现中:
首先调用了原始的 foo 方法,并获取返回值。
然后打印出返回值。
最后返回了原始方法的返回值。
这段代码演示了 Frida 如何 hook Java 方法并在执行前后添加自定义逻辑。这种能力非常强大,可以用于各种应用程序分析和修改的场景,例如:
监控和记录方法调用情况
修改方法参数或返回值
控制方法的执行流程
绕过应用程序的安全检查
修复应用程序中的漏洞
Hook Native 函数
以下是一个 Hook Native 函数的例子:
setImmediate(function () {
Java.perform(function () {
const nativeFuncAddr = Module.findExportByName(null, 'nativeFunc');
// Frida Gadget
Interceptor.attach(nativeFuncAddr, {
onEnter: function(args) {
// 输出函数的参数
console.log('nativeFunc(' + args[0] + ', ' + args[1] + ')');
// 修改参数
args[0] = ptr('0x1234');
args[1] = ptr('0x5678');
},
onLeave: function(retval) {
// 输出函数的返回值
console.log('returned: ' + retval);
}
});
})
这个代码示例展示了如何使用 Frida 来 hook 原生 C/C++ 函数。让我们逐步分析一下:
setImmediate(function () { ... }) 是为了确保 Java 环境准备就绪后再执行后续的 JavaScript 代码。这是因为 Frida 会在单独的线程中执行 Java 代码,所以需要等待 Java 环境初始化完成。
在 Java.perform(function () { ... }) 中, Frida 会在 Java 环境中执行后续的代码。
Module.findExportByName(null, 'nativeFunc') 用于获取名为 nativeFunc 的原生函数的地址。这个函数是我们要 hook 的目标。
接下来,我们使用 Interceptor.attach(nativeFuncAddr, {...}) 来 hook 这个原生函数。这个函数有两个回调函数:
onEnter: 在函数执行前调用,可以在这里修改参数值。
onLeave: 在函数执行后调用,可以在这里获取返回值。
在 onEnter 回调中:
我们首先打印出了函数的参数值。
然后,我们修改了参数 args[0] 和 args[1] 的值。
在 onLeave 回调中:
我们打印出了函数的返回值 retval。
这个示例代码演示了如何使用 Frida 的 Interceptor 模块来 hook 原生函数。通过这种方式,我们可以:
监控函数的参数和返回值
修改函数的参数和返回值
控制函数的执行流程
这种能力在应用程序分析和修改方面非常强大,可以用于各种场景,例如:
绕过原生库中的安全检查
修复原生库中的漏洞
动态分析原生库的行为
优化原生库的性能
Frida 启动的两种模式及区别
Frida 启动有两种方式,分别为 Spawn 模式和 Attach 模式:
- Spawn 模式可以完全控制目标进程的生命周期,而 Attach 模式只能依附到已经运行的进程。
- Spawn 模式更适合分析和干预应用程序的启动阶段,而 Attach 模式更适合分析和干预应用程序的运行时行为。
Spawn 模式启动方式: frida -U {package} -l xxx.js
Attach 模式启动方式: frida -U -l xxx.js -f {package}
Frida Hook 原理
- 动态代码注入:
- Frida 使用动态代码注入技术将自己的代码注入到目标进程中。
- 在 Android 和 iOS 上,Frida 会使用 ptrace 系统调用将自己的代码附加到目标进程。
- 在 Windows 上,Frida 使用 CreateRemoteThread 函数在目标进程中创建一个新线程并执行其代码。
- 函数钩子:
- Frida 使用函数钩子技术来拦截目标函数的执行。
- 在 Java 环境中,Frida 会使用 Java.use 和 Java.implementation 来替换目标类和方法的实现。
- 在原生环境中,Frida 会使用 Interceptor.attach 来拦截目标函数的执行,并提供 onEnter 和 onLeave 回调函数。
- 内存操作:
- Frida 可以读取和修改目标进程的内存,这可以用于修改函数参数、返回值和执行流程。
- 在 Java 环境中,Frida 提供了 Java.cast 和 Java.array 等 API 来操作对象和数组。
- 在原生环境中,Frida 提供了诸如 Memory.readPointer 和 Memory.writePointer 等 API 来读写内存。
- 跨平台兼容:
- Frida 是一个跨平台的工具,可以运行在 Android、iOS、macOS、Windows 和 Linux 等多种操作系统上。
- 为了实现跨平台兼容,Frida 在每个平台上使用不同的底层机制来实现动态代码注入和函数钩子。