使用 Choicy 來管理 Frida Gadget

通常来说,我们在越狱后的 iOS 平台都会直接使用 Frida 官方提供的 frida-server 来使用 frida。但是在实际使用中 frida-server 会有一些缺陷。frida-gadget 会是一个更好的选择。使用 opa334 的 Choicy 插件可以很方便的管理 Frida Gadget。

Frida Server 的缺陷

在我日常使用中,发现Frida Server 有以下几个缺陷:

  1. 迷之稳定性。在我的 iPhone 11/iOS 14.1 设备上,通过 Taurine 越狱后,使用frida时几乎必现内核 panic,设备直接重启。通过unc0ver 或 checkra1n 越狱则没有这个问题。这使得我完全无法在 Taurine 越狱后的设备上使用 frida,不得不改为用 unc0ver,但 unc0ver 还是老旧了。frida 的仓库中有一些 issue 提及这个问题,但都没找到具体的原因:
  2. 易被检测。有些 app 会在检测到安装官方 frida-server 后直接闪退。

与之相对应的,Frida Gadget 很稳定,至少没遇到过直接把内核搞崩的情况。Gadget 的隐蔽性也比 Server 更好。

Frida Gadget

注入 Frida Gadget 非常简单,只需要将 frida-gadget.dylib 和 配置文件 frida-gadget.config ,以及 Substrate 的配置文件 frida-gadget.plist 放入 Substrate 的模块目录 /Library/MobileSubstrate/DynamicLibraries 即可。修改 plist 中的 Filter 来手动指定需要注入的进程。可以通过指定 Bundles 为 com.apple.Security 来注入所有进程。

需要注意,最好不要直接向所有进程注入 frida-gadget.dylib,下面这个配置要配合后面的 Choicy 一起使用,否则可能会产生一些问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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>Filter</key>
<dict>
<key>Bundles</key>
<array>
<string>com.apple.Security</string>
</array>
</dict>
</dict>
</plist>

编辑 frida-gadget.config 来配置 gadget 的行为(可参考 frida-gadget 文档)。推荐配置如下:

1
2
3
4
5
6
7
8
9
{
"interaction": {
"type": "listen",
"address": "127.0.0.1",
"port": 27052,
"on_port_conflict": "pick-next",
"on_load": "resume"
}
}

这样会在 com.app.Security 加载的时候,注入 frida-gadget.dylib。而所有 app 都会加载 com.app.Security 这个 bundle,也就是说所有 app 都会被注入 frida-gadget.dylib。跟进 frida-gadget 的配置文件,进程将会在 127.0.0.1:27052 开放 frida 服务。如果该端口冲突,则会选用下一个端口。注入 frida-gadget.dylib 后,会自动恢复进程的执行。

使用 Choicy

我们可以手动编辑 frida-gadget.plist 文件来配置进程过滤规则,不过这样每次都要手动编辑。使用 Choicy 的好处在于可以提供一个手机上的 gui 界面,手动选择那些进程需要注入 frida-gadget。

添加 opa334 的源 https://opa334.github.io/,下载 Choicy 插件安装后,即可在设置中找到 Choicy 的设置界面。

首先可以配置全局插件配置,选择那些插件全局打开或关闭。

在进程配置一节中,可以针对 SpringBoard、每个应用、每个守护进程或额外的可执行文件单独配置。

在我们的场景下,可以使用“白名单”和“黑名单”两种配置方法:

  • 白名单模式:所有 app 都不会被注入 frida-gadget,仅有我们手动开启的app才会被注入
  • 黑名单模式:所有 app 都会被注入 frida-gadget,仅有我们手动关闭的app才会被注入

实际应用中应该只有少数 app 需要使用 frida-gadget。白名单配置如下:

  1. frida-gadget.plist 中确保 FilterBundlescom.apple.Security 或其他所有 app 都加载的 bundle。
  2. 在 Choicy 的Global Tweak Configuration中,关闭 frida-gadget 的开关。
  3. 进入 Choicy 的 Applications 页面,选择进入你需要注入 firda-gadget 的应用,打开 Overwrite Global Tweak Configuration打开 Custom Tweak Configuration 选项,在 Allow 标签页中打开 frida-gadget 的开关。

如果真的有什么需求需要用到黑名单模式,只需要打开 frida-gadget 的全局配置,然后在需要关闭的app中的 Deny 页面里打开 frida-gadget 的开关即可。

连接 Frida Gadget

当某个进程注入 frida-gadget.dylib 后,会从 27052 开始依次选择可用的端口创建 frida 服务。如果只给一个进程配白了,那么这个进程肯定在 27052 上创建(除非该端口已被别的什么服务占用)。可以在 iOS 中使用 lsof -t -i tcp:27052 获取占用该端口的进程 pid,再用 ps 确认进程名。合起来就是 ps -p $(lsof -t -i tcp:27052)

如果使用 usb 连接 iOS 设备的话,接下来在 host 端通过 iproxy 27052:27052 转发端口,然后使用 frida -H localhost:27052 gadget 即可连接到 frida-gadget。

如果想使用 wifi 连接 iOS 设备,则需要修改 frida-gadget.config,将 interaction 中的 address 改为 0.0.0.0。此时无需使用 iproxy 转发端口,直接用 frida -H <device_ip>:27052 gadget 即可连接到 frida-gadget。

如果有需求需要同时注入多个进程并连接 frida,需要手动确定每个进程的 frida-gadget 开放在哪个端口,然后分别(转发端口并)连接即可。

ps1: 也可以改到 27042 端口,这样 frida -H 连接的时候可以不用指定端口号。

ps2: 使用 frida-ps 看到的进程名为 Gadget

下面这个脚本可以获取所有注入了 frida-gadget 的进程、pid、以及对应的端口号:

1
2
3
4
5
6
7
8
9
10
11
frida-port() {
port=${1:-27052}
pid=$(lsof -t -i tcp:$port)
while [ "$pid" != "" ]
do
comm=$(basename $(ps -p $pid -o comm=))
echo "$pid\t$port\t$comm"
port=$(($port+1))
pid=$(lsof -t -i tcp:$port)
done
}

总结

本文只是抛砖引玉,实际使用中可以按照自己的个人习惯修改 plist,config 等。除了 Choicy 之外,也可用 libhooker configuratorSubstrate substitute

使用 Choicy 管理 frida-gadget 时,在多 app 场景下需要手动确认每个 app 的 frida-gadget 开启在哪个端口上。另外无法自定义配置每个 app 所需要的配置文件。我的想法是单独写一个插件来管理 frida-gadget,为每个需要注入 frida-gadget 的 app 单独做一份配置。目前已经新建文件夹了,后面写的差不多了再放出来 XDDDD。

参考