hacktricks/src/mobile-pentesting/ios-pentesting/frida-configuration-in-ios.md

342 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# iOS Frida 配置
{{#include ../../banners/hacktricks-training.md}}
## 安装 Frida
**在越狱设备上安装 Frida 的步骤:**
1. 打开 Cydia/Sileo 应用。
2. 导航到 管理 -> 源 -> 编辑 -> 添加。
3. 输入 "https://build.frida.re" 作为 URL。
4. 前往新添加的 Frida 源。
5. 安装 Frida 包。
如果您使用 **Corellium**,您需要从 [https://github.com/frida/frida/releases](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](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>`
<figure><img src="../../images/image (1159).png" alt=""><figcaption></figcaption></figure>
- 获取 **所有** 可用的 **类**(按字符串过滤)
```javascript:/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.")
}
```
- 获取 **类** 的 **所有** **方法**(按字符串过滤)
```javascript:/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](https://frida.re/docs/stalker/): Stalker 是 Frida 的代码 **跟踪引擎**。它允许线程被 **跟踪****捕获** 每个函数、**每个块**,甚至每个执行的指令。
您可以在 [https://github.com/poxyran/misc/blob/master/frida-stalker-example.py](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](https://github.com/ttdennis/fpicker)
[**fpicker**](https://github.com/ttdennis/fpicker) 是一个 **基于Frida的模糊测试套件**提供多种进程内模糊测试模式如AFL++模式或被动跟踪模式。它应该可以在所有Frida支持的平台上运行。
- [**安装fpicker**](https://github.com/ttdennis/fpicker#requirements-and-installation) **& radamsa**
```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`):
```javascript: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/](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 教程
{{#ref}}
../android-app-pentesting/frida-tutorial/
{{#endref}}
## 参考文献
- [https://www.briskinfosec.com/blogs/blogsdetail/Getting-Started-with-Frida](https://www.briskinfosec.com/blogs/blogsdetail/Getting-Started-with-Frida)
{{#include ../../banners/hacktricks-training.md}}