iOS Frida 配置

Reading time: 11 minutes

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)

支持 HackTricks

安装 Frida

在越狱设备上安装 Frida 的步骤:

  1. 打开 Cydia/Sileo 应用。
  2. 导航到 管理 -> 源 -> 编辑 -> 添加。
  3. 输入 "https://build.frida.re" 作为 URL。
  4. 前往新添加的 Frida 源。
  5. 安装 Frida 包。

如果您使用 Corellium,您需要从 https://github.com/frida/frida/releases 下载 Frida 版本 (frida-gadget-[yourversion]-ios-universal.dylib.gz),并解压并复制到 Frida 要求的 dylib 位置,例如:/Users/[youruser]/.cache/frida/gadget-ios.dylib

安装后,您可以在您的 PC 上使用命令 frida-ls-devices 并检查设备是否出现(您的 PC 需要能够访问它)。
还可以执行 frida-ps -Uia 来检查手机的运行进程。

在未越狱设备上使用 Frida & 无需修补应用

查看这篇博客文章,了解如何在未越狱设备上使用 Frida 而无需修补应用:https://mrbypass.medium.com/unlocking-potential-exploring-frida-objection-on-non-jailbroken-devices-without-application-ed0367a84f07

Frida 客户端安装

安装 frida tools

bash
pip install frida-tools
pip install frida

安装了 Frida 服务器并且设备正在运行和连接,检查客户端是否正常工作

bash
frida-ls-devices  # List devices
frida-ps -Uia     # Get running processes

Frida Trace

bash
# Functions
## Trace all functions with the word "log" in their name
frida-trace -U <program> -i "*log*"
frida-trace -U <program> -i "*log*" | swift demangle # Demangle names

# Objective-C
## Trace all methods of all classes
frida-trace -U <program> -m "*[* *]"

## Trace all methods with the word "authentication" from classes that start with "NE"
frida-trace -U <program> -m "*[NE* *authentication*]"

# Plug-In
## To hook a plugin that is momentarely executed prepare Frida indicating the ID of the Plugin binary
frida-trace -U -W <if-plugin-bin> -m '*[* *]'

获取所有类和方法

  • 自动完成:只需执行 frida -U <program>
  • 获取 所有 可用的 (按字符串过滤)
/tmp/script.js
// frida -U <program> -l /tmp/script.js

var filterClass = "filterstring"

if (ObjC.available) {
for (var className in ObjC.classes) {
if (ObjC.classes.hasOwnProperty(className)) {
if (!filterClass || className.includes(filterClass)) {
console.log(className)
}
}
}
} else {
console.log("Objective-C runtime is not available.")
}
  • 获取 所有 方法(按字符串过滤)
/tmp/script.js
// frida -U <program> -l /tmp/script.js

var specificClass = "YourClassName"
var filterMethod = "filtermethod"

if (ObjC.available) {
if (ObjC.classes.hasOwnProperty(specificClass)) {
var methods = ObjC.classes[specificClass].$ownMethods
for (var i = 0; i < methods.length; i++) {
if (!filterMethod || methods[i].includes(filterClass)) {
console.log(specificClass + ": " + methods[i])
}
}
} else {
console.log("Class not found.")
}
} else {
console.log("Objective-C runtime is not available.")
}
  • 调用一个函数
javascript
// Find the address of the function to call
const func_addr = Module.findExportByName("<Prog Name>", "<Func Name>")
// Declare the function to call
const func = new NativeFunction(
func_addr,
"void",
["pointer", "pointer", "pointer"],
{}
)

var arg0 = null

// In this case to call this function we need to intercept a call to it to copy arg0
Interceptor.attach(wg_log_addr, {
onEnter: function (args) {
arg0 = new NativePointer(args[0])
},
})

// Wait untill a call to the func occurs
while (!arg0) {
Thread.sleep(1)
console.log("waiting for ptr")
}

var arg1 = Memory.allocUtf8String("arg1")
var txt = Memory.allocUtf8String("Some text for arg2")
wg_log(arg0, arg1, txt)

console.log("loaded")

Frida Fuzzing

Frida Stalker

From the docs: Stalker 是 Frida 的代码 跟踪引擎。它允许线程被 跟踪捕获 每个函数、每个块,甚至每个执行的指令。

您可以在 https://github.com/poxyran/misc/blob/master/frida-stalker-example.py 找到一个实现 Frida Stalker 的示例。

这是另一个示例,用于在每次调用函数时附加 Frida Stalker:

javascript
console.log("loading")
const wg_log_addr = Module.findExportByName("<Program>", "<function_name>")
const wg_log = new NativeFunction(
wg_log_addr,
"void",
["pointer", "pointer", "pointer"],
{}
)

Interceptor.attach(wg_log_addr, {
onEnter: function (args) {
console.log(`logging the following message: ${args[2].readCString()}`)

Stalker.follow({
events: {
// only collect coverage for newly encountered blocks
compile: true,
},
onReceive: function (events) {
const bbs = Stalker.parse(events, {
stringify: false,
annotate: false,
})
console.log(
"Stalker trace of write_msg_to_log: \n" +
bbs.flat().map(DebugSymbol.fromAddress).join("\n")
)
},
})
},
onLeave: function (retval) {
Stalker.unfollow()
Stalker.flush() // this is important to get all events
},
})

caution

从调试的角度来看,这很有趣,但对于模糊测试来说,不断地 .follow().unfollow() 是非常低效的。

Fpicker

fpicker 是一个 基于Frida的模糊测试套件,提供多种进程内模糊测试模式,如AFL++模式或被动跟踪模式。它应该可以在所有Frida支持的平台上运行。

bash
# Get fpicker
git clone https://github.com/ttdennis/fpicker
cd fpicker

# Get Frida core devkit and prepare fpicker
wget https://github.com/frida/frida/releases/download/16.1.4/frida-core-devkit-16.1.4-[yourOS]-[yourarchitecture].tar.xz
# e.g. https://github.com/frida/frida/releases/download/16.1.4/frida-core-devkit-16.1.4-macos-arm64.tar.xz
tar -xf ./*tar.xz
cp libfrida-core.a libfrida-core-[yourOS].a #libfrida-core-macos.a

# Install fpicker
make fpicker-[yourOS] # fpicker-macos
# This generates ./fpicker

# Install radamsa (fuzzer generator)
brew install radamsa
  • 准备文件系统:
bash
# From inside fpicker clone
mkdir -p examples/wg-log # Where the fuzzing script will be
mkdir -p examples/wg-log/out # For code coverage and crashes
mkdir -p examples/wg-log/in # For starting inputs

# Create at least 1 input for the fuzzer
echo Hello World > examples/wg-log/in/0
  • 模糊测试脚本 (examples/wg-log/myfuzzer.js):
examples/wg-log/myfuzzer.js
// Import the fuzzer base class
import { Fuzzer } from "../../harness/fuzzer.js"

class WGLogFuzzer extends Fuzzer {
constructor() {
console.log("WGLogFuzzer constructor called")

// Get and declare the function we are going to fuzz
var wg_log_addr = Module.findExportByName(
"<Program name>",
"<func name to fuzz>"
)
var wg_log_func = new NativeFunction(
wg_log_addr,
"void",
["pointer", "pointer", "pointer"],
{}
)

// Initialize the object
super("<Program nane>", wg_log_addr, wg_log_func)
this.wg_log_addr = wg_log_addr // We cannot use "this" before calling "super"

console.log("WGLogFuzzer in the middle")

// Prepare the second argument to pass to the fuzz function
this.tag = Memory.allocUtf8String("arg2")

// Get the first argument we need to pass from a call to the functino we want to fuzz
var wg_log_global_ptr = null
console.log(this.wg_log_addr)
Interceptor.attach(this.wg_log_addr, {
onEnter: function (args) {
console.log("Entering in the function to get the first argument")
wg_log_global_ptr = new NativePointer(args[0])
},
})

while (!wg_log_global_ptr) {
Thread.sleep(1)
}
this.wg_log_global_ptr = wg_log_global_ptr
console.log("WGLogFuzzer prepare ended")
}

// This function is called by the fuzzer with the first argument being a pointer into memory
// where the payload is stored and the second the length of the input.
fuzz(payload, len) {
// Get a pointer to payload being a valid C string (with a null byte at the end)
var payload_cstring = payload.readCString(len)
this.payload = Memory.allocUtf8String(payload_cstring)

// Debug and fuzz
this.debug_log(this.payload, len)
// Pass the 2 first arguments we know the function needs and finally the payload to fuzz
this.target_function(this.wg_log_global_ptr, this.tag, this.payload)
}
}

const f = new WGLogFuzzer()
rpc.exports.fuzzer = f
  • 编译模糊测试器:
bash
# From inside fpicker clone
## Compile from "myfuzzer.js" to "harness.js"
frida-compile examples/wg-log/myfuzzer.js -o harness.js
  • 使用 radamsa 调用模糊测试工具 fpicker
bash
# Indicate fpicker to fuzz a program with the harness.js script and which folders to use
fpicker -v --fuzzer-mode active -e attach -p <Program to fuzz> -D usb -o examples/wg-log/out/ -i examples/wg-log/in/ -f harness.js --standalone-mutator cmd --mutator-command "radamsa"
# You can find code coverage and crashes in examples/wg-log/out/

caution

在这种情况下,我们不会在每个有效负载后重启应用程序或恢复状态。因此,如果 Frida 发现崩溃,那么在该有效负载之后的下一个输入也可能会崩溃应用程序(因为应用程序处于不稳定状态),即使输入不应该崩溃应用程序。

此外,Frida 将钩住 iOS 的异常信号,因此当Frida 发现崩溃时,可能不会生成iOS 崩溃报告

为了防止这种情况,例如,我们可以在每次 Frida 崩溃后重启应用程序。

日志与崩溃

您可以检查macOS 控制台或**log** cli 来查看 macOS 日志。
您还可以使用**idevicesyslog检查 iOS 的日志。
一些日志将省略信息,添加
<private>**。要显示所有信息,您需要从 https://developer.apple.com/bug-reporting/profiles-and-logs/ 安装一些配置文件以启用该私密信息。

如果您不知道该怎么办:

sh
vim /Library/Preferences/Logging/com.apple.system.logging.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Enable-Private-Data</key>
<true/>
</dict>
</plist>

killall -9 logd

您可以检查崩溃记录:

  • iOS
  • 设置 → 隐私 → 分析与改进 → 分析数据
  • /private/var/mobile/Library/Logs/CrashReporter/
  • macOS
  • /Library/Logs/DiagnosticReports/
  • ~/Library/Logs/DiagnosticReports

warning

iOS 仅存储同一应用的 25 个崩溃记录,因此您需要清理这些记录,否则 iOS 将停止生成崩溃记录。

Frida Android 教程

Frida Tutorial

参考文献

tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)

支持 HackTricks