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

17 KiB
Raw Blame History

iOS Frida配置

☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

安装Frida

在越狱设备的Cydia/Sileo应用中,通过进入Manage -> Sources -> Edit -> Add并输入https://build.frida.re 来添加Frida的存储库。这将在源列表中添加一个新的源。进入Frida ,现在你应该安装 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

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

无需越狱设备和无需修补应用程序的Frida

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

安装Frida客户端

安装frida工具

pip install frida-tools
pip install frida

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

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

Frida跟踪

Frida is a dynamic instrumentation toolkit that allows you to inject JavaScript code into running processes. It is commonly used for reverse engineering, debugging, and dynamic analysis of mobile applications. In this section, we will discuss how to configure Frida for iOS pentesting.

Frida是一个动态插桩工具包允许您将JavaScript代码注入到正在运行的进程中。它通常用于移动应用程序的逆向工程、调试和动态分析。在本节中我们将讨论如何配置Frida用于iOS渗透测试。

Setting up Frida Server on iOS

To use Frida on iOS, you need to set up Frida Server on the target device. Follow the steps below to configure Frida Server:

  1. Jailbreak the iOS device or use a jailbroken device.

  2. Install the Frida package from the Cydia repository.

  3. Open a terminal and run the following command to start Frida Server:

    frida-server -l 0.0.0.0
    

    This will start Frida Server and listen on all available network interfaces.

  4. Make sure the device and the machine running Frida have network connectivity.

Connecting to Frida Server from the Host Machine

Once Frida Server is running on the iOS device, you can connect to it from your host machine using the Frida command-line tools or the Frida Python bindings. Follow the steps below to connect to Frida Server:

  1. Install the Frida command-line tools or the Frida Python bindings on your host machine.

  2. Open a terminal and run the following command to connect to Frida Server:

    frida-ps -H <device_ip_address>
    

    Replace <device_ip_address> with the IP address of the iOS device running Frida Server.

  3. If the connection is successful, you will see a list of running processes on the iOS device.

Injecting JavaScript Code into a Process

Once you are connected to Frida Server, you can inject JavaScript code into a running process on the iOS device. Follow the steps below to inject JavaScript code:

  1. Identify the process ID (PID) of the target process using the frida-ps command.

  2. Open a terminal and run the following command to inject JavaScript code into the target process:

    frida -H <device_ip_address> -p <pid> -l <script.js>
    

    Replace <device_ip_address> with the IP address of the iOS device running Frida Server, <pid> with the process ID of the target process, and <script.js> with the path to the JavaScript code file.

  3. If the injection is successful, the JavaScript code will be executed in the target process.

Conclusion

Configuring Frida for iOS pentesting involves setting up Frida Server on the target device and connecting to it from the host machine. Once connected, you can inject JavaScript code into running processes on the iOS device for reverse engineering, debugging, and dynamic analysis. Frida is a powerful tool that can greatly enhance your iOS pentesting capabilities.

# 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>
  • 获取所有可用的(通过字符串过滤)

{% code title="/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.");
}

{% endcode %}

  • 获取一个类的所有方法(通过字符串过滤)

{% code title="/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.");
}

{% endcode %}

  • 调用函数
// 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模糊测试

Frida Stalker

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

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

这是另一个示例每次调用函数时都会附加Frida Stalker

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
}
});

{% hint style="danger" %} 这对于调试目的来说很有趣,但对于模糊测试来说,不断地使用 .follow().unfollow() 是非常低效的。 {% endhint %}

Fpicker

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

# 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
  • 准备文件系统:
# 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):

{% code title="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;

{% endcode %}

  • 编译模糊测试器:
# From inside fpicker clone
## Compile from "myfuzzer.js" to "harness.js"
frida-compile examples/wg-log/myfuzzer.js -o harness.js
  • 使用 radamsa 调用模糊测试工具 fpicker

{% code overflow="wrap" %}

# 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/

{% endcode %}

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

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

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

日志和崩溃

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

如果您不知道该怎么做:

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

{% hint style="warning" %} iOS仅存储同一应用的25个崩溃日志因此您需要清理它否则iOS将停止创建崩溃日志。 {% endhint %}

Frida Android教程

{% content-ref url="../android-app-pentesting/frida-tutorial/" %} frida-tutorial {% endcontent-ref %}

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥