Frida 在我的工作中最常常用来做 Hook 使用,什么是 Hook?是指 App 执行到指定方法时,会把我们插入的方法代码也执行,从而改变原有方法内容。不管怎样总是先执行我们的代码,有点像是中间人攻击。

Android 常见的 Hook 框架:Xposed 类(Xposed、TaiChi、dexposed、LSPosed)、Frida、Cydia Substrate、AndFix、Sohix、AndroidMethodHook、Legend、SandHook、虚拟大师、riru、Magisk、EPIC、YAHFA

iOS 常见的 Hook 框架:Frida

目录

1 安装 Frida

Frida 分为客户端和服务端,服务端安装在移动设备上,客户端发送指令让服务端执行操作。

1.1 创建 Python 虚拟环境

使用 Python 自带的 venv 虚拟环境,单独创建目录隔离库依赖问题。

创建虚拟环境。会自动创建对应目录。

python -m venv c:\path\to\myenv

进入虚拟环境。

c:\path\to\myenv\Scripts\activate.bat

退出虚拟环境。

(ven_frida) C:\Users\gbb> deactivate
C:\Users\gbb>

不想用了可以直接删除虚拟环境目录。

Linux 下创建过程还是一样的,只是需要用 source 命令进入虚拟环境。

┌──(root㉿raingray)-[~/Desktop]
└─# source  myenv/bin/activate

┌──(myenv)(root㉿raingray)-[~/Desktop]
└─#

deactivate 退出虚拟环境。

1.2 pip 安装 Frida 客户端

先安装 Python3,运行 pip install frida-tools 安装客户端。这里我用的 Python 3.12.3 安装 Frida-Tools。

(ven_frida) C:\Users\gbb>pip install frida-tools
Collecting frida-tools
  Using cached frida-tools-12.4.2.tar.gz (345 kB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  Installing backend dependencies ... done
  Preparing metadata (pyproject.toml) ... done
Collecting colorama<1.0.0,>=0.2.7 (from frida-tools)
  Using cached colorama-0.4.6-py2.py3-none-any.whl.metadata (17 kB)
Collecting frida<17.0.0,>=16.2.2 (from frida-tools)
  Using cached frida-16.2.4-cp37-abi3-win_amd64.whl.metadata (2.1 kB)
Collecting prompt-toolkit<4.0.0,>=2.0.0 (from frida-tools)
  Using cached prompt_toolkit-3.0.43-py3-none-any.whl.metadata (6.5 kB)
Collecting pygments<3.0.0,>=2.0.2 (from frida-tools)
  Using cached pygments-2.18.0-py3-none-any.whl.metadata (2.5 kB)
Collecting wcwidth (from prompt-toolkit<4.0.0,>=2.0.0->frida-tools)
  Downloading wcwidth-0.2.13-py2.py3-none-any.whl.metadata (14 kB)
Using cached colorama-0.4.6-py2.py3-none-any.whl (25 kB)
Using cached frida-16.2.4-cp37-abi3-win_amd64.whl (33.0 MB)
Using cached prompt_toolkit-3.0.43-py3-none-any.whl (386 kB)
Using cached pygments-2.18.0-py3-none-any.whl (1.2 MB)
Downloading wcwidth-0.2.13-py2.py3-none-any.whl (34 kB)
Building wheels for collected packages: frida-tools
  Building wheel for frida-tools (pyproject.toml) ... done
  Created wheel for frida-tools: filename=frida_tools-12.4.2-py3-none-any.whl size=209691 sha256=33494db27c38735aa763eb7d9971ae0ac8c4e33e85f30af93dc6f293743c5803
  Stored in directory: c:\users\gbb\appdata\local\pip\cache\wheels\e6\5e\05\44f1774260ebadd8fc813b1e1b7dbdb96d8a61e25340224e2d
Successfully built frida-tools
Installing collected packages: wcwidth, pygments, prompt-toolkit, frida, colorama, frida-tools
Successfully installed colorama-0.4.6 frida-16.2.4 frida-tools-12.4.2 prompt-toolkit-3.0.43 pygments-2.18.0 wcwidth-0.2.13

会自动安装 Frida 和对应的小工具。

(ven_frida) C:\Users\gbb>pip list
Package        Version
-------------- -------
colorama       0.4.6
frida          16.2.4
frida-tools    12.4.2
pip            24.0
prompt-toolkit 3.0.43
Pygments       2.18.0
wcwidth        0.2.13

如果是需要升级可以用指定版本升。

(ven_frida) PS E:\Desktop\MobileAppHook> frida --version
16.2.4
(ven_frida) PS E:\Desktop\MobileAppHook> pip install frida==16.5.7 -i https://pypi.mirrors.ustc.edu.cn/simple 
Looking in indexes: https://pypi.mirrors.ustc.edu.cn/simple
Collecting frida==16.5.7
  Downloading https://mirrors.ustc.edu.cn/pypi/packages/55/a7/36125c590f51cc7dfba9f0abc3bec68518652352e1646c82bfc2b3f746cb/frida-16.5.7-cp37-abi3-win_amd64.whl (33.5 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 33.5/33.5 MB 7.9 MB/s eta 0:00:00
Installing collected packages: frida
  Attempting uninstall: frida
    Found existing installation: frida 16.2.4
    Uninstalling frida-16.2.4:
      Successfully uninstalled frida-16.2.4
Successfully installed frida-16.5.7

[notice] A new release of pip is available: 24.0 -> 24.3.1
[notice] To update, run: python.exe -m pip install --upgrade pip
(ven_frida) PS E:\Desktop\MobileAppHook> frida --version
16.5.7

非要安装指定版本的 frida,可以看看 frida 和 frida-tools 的对应关系,这在官网没有明确写出来,但是 frida-tools 明显比 frida 发行的慢些,找它两最接近的位置就好。

pip install frida=16.2.1 -i https://pypi.mirrors.ustc.edu.cn/simple
pip install frida-tools==12.3.0 -i https://pypi.mirrors.ustc.edu.cn/simple

安装完成可以通过运行版本查看命令来确定两个版本对应上了,否则会报下面错误。

(ven_frida) PS E:\Desktop\MobileAppHook> frida --version
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "D:\raingray\Project\xx\20240521-xx\TestFile\ven_frida\Scripts\frida.exe\__main__.py", line 4, in <module>
  File "D:\raingray\Project\xx\20240521-xx\TestFile\ven_frida\Lib\site-packages\frida_tools\repl.py", line 31, in <module>       
    from frida_tools.application import ConsoleApplication
  File "D:\raingray\Project\xx\20240521-xx\TestFile\ven_frida\Lib\site-packages\frida_tools\application.py", line 22, in <module>
    import frida._frida as _frida
ModuleNotFoundError: No module named 'frida._frida'

1.3 安装 Frida 服务端

1.3.1 iOS 安装

iPhone 因为每个机器环境都不相同,但是大致流程都是先越狱,再安装软件商店,通过商店安装 SSH/NewTerm。

这里为了方便描述,就以我这边测试机环境进行过程叙述,越狱我习惯用爱思助手自动化操作,iOS 14.3 版本默认选择 unc0ver 越狱(这个软件重启就能恢复未越狱状态,要是检测文件需要在设置里启动 System 选项在重新去软件主节点按钮恢复)。

PS:iOS 15 可以用爱思助手安装 Dopamine,软件商店选 Sileo。从软件商店安装完 SSH 后,在渠道 Dopamine 设置中重置 mobile 用户密码方便后续 SSH 登录。

越狱完成通过 Cydia 安装 Sam Bingner 写的 OpenSSH,用 root/alpine 登录查询 CPU 架构。

xxxdeiPhone:~ mobile% arch
arm

根据 CPU 架构下载与客户端对应版本号的服务端程序,这里是 frida_16.2.4_iphoneos-arm.deb 通过 SSH 上传到临时目录。

scp frida_16.2.4_iphoneos-arm.deb root@<IP>:/private/var/tmp

执行安装操作,安装成功后运行 frida-server 命令即可启动服务端。其中 frida-server -l 0.0.0.0& 则是监听在 TCP 0.0.0.0:27042 方便通过 IP 远程连接,默认情况下还会监听在 TCP 127.0.0.1:27042 上用于 USB 连接。

ssh root@<IP> "dpkg -i /private/var/tmp/frida_16.2.4_iphoneos-arm.deb; frida-server -l 0.0.0.0 > /dev/null 2>&1 & ps aux | grep frida"

要想重启 Frida-Server 可以直接杀掉进程重新运行。

iPhone:~ root# ps aux | grep frida
root            4522   0.0  0.2 407926976   6320 s000  S+    7:21PM   0:00.02 grep frida
root            4508   0.0  0.3 407960176   9728   ??  Ss    7:20PM   0:00.03 /usr/sbin/frida-server -l 0.0.0.0
iPhone:~ root# kill -9 4508
iPhone:~ root# ps aux | grep frida
root            4524   0.1  0.3 407951488   9680   ??  Ss    7:21PM   0:00.04 /usr/sbin/frida-server

root            4526   0.0  0.2 407926976   6320 s000  S+    7:21PM   0:00.02 grep frida

或者卸载也可以杀死进程。

iPhone:~ root# dpkg --purge re.frida.server
(Reading database ... 4114 files and directories currently installed.)
Removing re.frida.server (16.2.4) ...
iPhone:~ root# ps aux | grep frida
root            4533   0.0  0.2 407928000   6352 s000  S+    7:22PM   0:00.02 grep frida

最简单的安装方式是在 Cydia/Sileo 应用商店里添加软件源 https://build.frida.re,最后搜索 Frida 安装。

1.3.2 Android 安装

服务端安装需要先查架构。

PS D:\raingray\Tools\MobileAppication\adb> ./adb shell getprop ro.product.cpu.abi
arm64-v8a

去 https://github.com/frida/frida/releases 下载 Android 对应版本 frida-server-xx.x.x-android-arm64.xz。

将 frida-server-xx.x.x-android-arm64 解压 push 到设备上。

PS D:\raingray\Tools\MobileAppication\adb> ./adb push frida-server-16.2.4-android-arm64 /data/local/tmp
frida-server-16.2.4-android-arm64: 1 file pushed, 0 skipped. 38.1 MB/s (56458432 bytes in 1.412s)
PS D:\raingray\Tools\MobileAppication\adb> ./adb shell su -c chmod +x /data/local/tmp/frida-server-16.2.4-android-arm64
PS D:\raingray\Tools\MobileAppication\adb>

无法 push 时就传到设备 Download 目录手动 move。

PS D:\adb> ./adb shell
haydn:/ $ su
haydn:/ # mv /sdcard/Download/frida-server-16.2.4-android-arm64 /data/local/tmp/
haydn:/ # chmod +x /data/local/tmp/frida-server-16.2.4-android-arm64
haydn:/ #

上传完成后,权限也给了,可以放入后台运行服务端。此时终端阻塞了没有返回任何内容,可以直接关掉,因为进程已经后台运行只是没有返回值。

PS D:\adb> ./adb shell su -c "/data/local/tmp/frida-server-16.2.4-android-arm64 &"

如果 frida-server 有问题想要杀掉进程,可以先查询 PID 再根据 PID 杀。

PS C:\Users\gbb> ./adb.exe shell su -c "ps | grep frida"
root         14522   677  132632  55812 poll_schedule_timeout 0 S frida-server-16.2.4-android-arm64
PS C:\Users\gbb> ./adb.exe shell su -c "kill -9 14522"
PS C:\Users\gbb> ./adb.exe shell su -c "ps | grep frida"
PS C:\Users\gbb>

或者根据名字杀。

PS D:\adb\ > ./adb shell su -c "pkill -9 frida-server-16.2.4-android-arm64"

2 frida-tools

2.1 frida-ps

frida-ps -U,使用 USB 连接设备,自动把手机上运行的所有进程 ID 和进程名称打印出来。要是没有连接 USB 也可以用 -H ip 来连接。

(ven_frida) E:\Desktop>frida-ps -U
 PID  Name
----  --------------------------------------------------------
 636  ACCHWComponentAuthService
1219  ASPCarryLog
1091  AppSSODaemon
 575  AppleCredentialManagerDaemon
 664  BlueTool
 718  CAReportingService
1106  设置
......

frida-ps -U -a,使用 -a 把已安装的应用中正在运行的打印出来

(ven_frida) E:\Desktop>frida-ps -U -a
 PID  Name   Identifier
----  -----  ---------------------
 769  Cydia  com.saurik.Cydia
1106  设置     com.apple.Preferences

frida-ps -U -a -i,使用 -i 把所有安装 iOS 应用显示名称和包名打印出来,通过 PID 还能看到那个应用正在运行。速记还可以想成 iua

(ven_frida) E:\Desktop>frida-ps -U -a -i
 PID  Name          Identifier
----  ------------  --------------------------------
 769  Cydia         com.saurik.Cydia
1106  设置            com.apple.Preferences
   -  App Store     com.apple.AppStore
   -  FaceTime通话    com.apple.facetime
   -  Safari浏览器     com.apple.mobilesafari
   -  Substitute    com.ex.substitute.settings
   -  Taurine       org.coolstar.taurine.92S7TKDPS8
   -  TestFlight    com.apple.TestFlight
   -  Watch         com.apple.Bridge
   -  iTunes Store  com.apple.MobileStore
   -  unc0ver       science.xnu.undecimus.92S7TKDPS8
   -  钱包            com.apple.Passbook
   ......

2.2 frida-ls-devices

(ven_frida) C:\Users\gbb>frida-ls-devices
Id                         Type    Name             OS
-------------------------  ------  ---------------  ------------------
local                      local   RAINGRAY         Windows 10.0.22621
00008101-000B38983652001E  usb     iPhone           iPhone OS 14.3
barebone                   remote  GDB Remote Stub
socket                     remote  Local Socket

这个设备 ID 还是很有用的,后续如果有多个设备的情况下可以指定 ID 进行连接。

2.3 frida-trace

使用的 Frida Interceptor API 编写的工具。

frida-trace <PID|process_name>

  • --runtime,执行运行脚本的 rumtime,v8 会错误信息更多
  • -i,用来搜索方法名,frida-trace -i "CreateFile*"
  • -I,用来搜索模块
  • -x,排除搜索的方法
  • -X,排除搜索的模块
  • --deubg,开启调试控制台

定位加密位置也有用 IDA 搜索 request、response 等关键词,找到具体方法或者类。另一种方法就是运行应用手动点击功能。

Frida 基础知识 - Frida 手册 --- Frida basics - Frida HandBook (learnfrida.info)
移动APP测试中关键代码定位 (qq.com)
iOS逆向指南:动态分析 | 🐼黑超熊猫zuik's blog (zuikyo.github.io)
典型使用方式 · 逆向调试利器:Frida
需要程序运行后才能执行搜索,不能 spawn。

2.3.1 iOS

搜索 Object-C 方法。

# -m 是搜索 Object 方法,* 是代表实例和类方法都搜索,方括号中第一个星号是方法名称,星号是搜索所有方法名
# 第二个 fileExistsAtPath 是参数名称,在参数名称后面使用了星号,表示任意参数的个数。
frida-trace -U -f com.xx.xx -m "*[* fileExistsAtPath*]"
frida-trace -U -i "CCCryptorCreate*" Twitter

2.3.2 Android

frida-trace -U -i open -N com.android.chrome

attach 模式:-N 写应用名,-n 写包名
-i 语法是搜索模块和方法 模块!方法 但是模块可以省略

2.4 frida

Frida 操作模式

  • CLI,直接使用 JS 编写脚本
  • RPC,通过 Python 编写脚本,本质上还是调用的 JS。

--help 可以查看使用帮助:

  • -l,运行指定的 Frida 脚本 -l 1.js
  • --runtime,执行运行 JS 脚本的 rumtime,--runtime=v8 是 JavaScript V8 引擎,会提供的信息更多,--runtime=qjs 是 QuickJS 引擎
  • --pause,在老版本 frida 中 Hook 的应用运行后会自动暂停在入口线程上(不想暂停需要用 --no-pause),新版本中想要暂停得手动设置,暂停后需要在控制台上输入 %resume 才能恢复应用运行。
  • -U,通过 USB 连接 frida-client
    -D,通过驱动 ID 连接。ID 可以用 frida-ls-devices 打印
    -H,通过 IP 连接 frida-client,-H 192.168.1.1
  • -o,输出运行日志到文件中,-o 1.log

Frida 注入模式

  • Attach,对已经运行的 App 注入脚本。命令 frida -U -l xx.js com.examplefrida -U com.examplefrida -U -N com.example 通过 -N 指定包名,frida -U -p <PID> -p 指定进程 ID,。
  • Spawn,在启动 App 时去注入脚本,就算 App 正在运行也会重启注入。实现的方式是用 -f <Target> 选项, frida -U -f com.example -l xx.js Namefrida -U -f com.example。这里 -U 是 USB 连接,也可以用 -H 通过 IP 进行远程连接,Target 的值是包名,可以用 frida-ps 查看。

有时运行 frida 通过 USB 连接 Android 说识别不到驱动,可能是 adb 服务没启动。

PS C:\Users\gbb> frida -U -l  frida-multiple-unpinning.js AppName
  ____
 / _  |   Frida 15.1.22 - A world-class dynamic instrumentation toolkit
| (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
Waiting for USB device to appear...

一般用 adb 连接后重新运行 frida 脚本就正常。

PS D:\gbb\tools\adb> ./adb shell
* daemon not running; starting now at tcp:5037
* daemon started successfully
haydn:/ $

3 Frida JavaScript API

使用什么语言写脚本?Frida 可以用 JavaScript 编写。体量小的代码直接用 JavaScript 写即可,大量代码最好用 TypeScript 写,Frida 插件对 TypeScript 补全支持最全,还能自动检查类型错误。

编写前先安装 frida-agent-example 插件方便代码自动补全。插件安装方式是先把这个项目下载下来,进入目录执行 npm install,后续就用 Visual Studio Code 打开此目录编写脚本,输入对应关键字会有提示。

Frida 脚本代码提示.png

创建 Frida 脚本 test.js。

console.log(Frida.version)

运行脚本。

(ven_frida) PS D:\raingray\Project\20240521-xxx\TestFile\frida-agent-example-main> frida -H 172.20.10.5 -f com.xx.xx -l .\UIAlertController\test.js
     ____
    / _  |   Frida 16.2.4 - A world-class dynamic instrumentation toolkit
   | (_| |
    > _  |   Commands:
   /_/ |_|       help      -> Displays the help system
   . . . .       object?   -> Display information about 'object'
   . . . .       exit/quit -> Exit
   . . . .
   . . . .   More info at https://frida.re/docs/home/
   . . . .
   . . . .   Connected to 172.20.10.5 (id=socket@172.20.10.5)
Spawning `com.xx.xx`...
16.2.4
Spawned `com.xx.xx`. Resuming main thread!
[Remote::com.xx.xx ]->

当注入成功后,再改动脚本内容为 console.log(Process.id),试图输出输出当前进程 ID,保存时脚本就会自动执行,非常方便。这自动执行跟这个 VSCode 插件没关系,就是注入后的功能。

[Remote::com.xx.xx ]-> 1100
1100

3.1 Java

在 Android App 上用。

注入到进程:

  • setTimeout(functionName, millisecond),参数填写函数名和毫秒,用来延迟注入应用进程,防止有的壳正在加载应用源码,此时指定方法不再内存就进行 Hook 会失败。上次就有遇到使用 Spawn 无法 Hook,说类找不到。
    Frida 直接注入应用报错.png
    最后使用延时运行我们的 Hook 代码 setTimeout(main, 5000); 就没问题了。
  • setImmediate(functionName),参数填写函数名,用来直接注入应用进程,和 setTimeout 比没有延迟。

Frida Java Hook API

  • Java.perform(functionName),参数填写函数名,用来注入到 Java 运行时。
  • Java.use(fullyQualifiedClassNames),参数填类全限定名,这样可以获取类对象、属性、方法,像是 Java 反射。

3.1.1 Android 定位类和方法

使用 Magisk root,Magisk 插件自动安装系统证书

定位方法:
1.现成工具。下载 LSPosed 使用 Magisk 安装,打开算法助手找到加密调用堆栈;
2.关键字搜索。Android 没加固直接 jadx-gui 反编译,找到关键位置邮件直接复制为 frida hook 代码。加固的情况下可以把 App 运行起来,看看请求标识(API/参数),App 已经加载的类名,根据关键字搜索;
3.调用频率。统计类和方法调用次数,根据次数来判断哪些是经常使用。

搜索指定类的所有方法 Objection 搜索可能不全,需要运行 Frida 脚本交叉搜索验证下。

Java.perform(function () {
  // 从内存中获取指定类,搜索类下有哪些方法,类名要求填写类全限定名
  var HttpUrlRequest = Java.use(
    "com.alipay.mobile.common.transport.http.HttpUrlRequest"
  );

  // 方式一
  var methods = HttpUrlRequest.class.getDeclaredMethods();
  methods.forEach(function (method) {
    console.log(method.toGenericString());
  });
  // 方式二
  HttpUrlRequest.class.getDeclaredMethods().forEach(function (method) {
    console.log(method.toString());
  });
})

在某个 Hook 成功方法中可以打印出调用堆栈观察调用顺序。

console.log(
"HttpUrlRequest.setReqData 调用堆栈:\n" +
  Java.use("java.lang.Exception").$new().getStackTrace().toString()
);

3.1.2 Hook 形参和返回值

Hook 无参方法

// 注入函数到 Java 运行时
Java.perform(
    function() {
        var example = Java.use("com.xxx.xx.xxx")
        //  Hook example 类 test() 无参方法。或者说是重写 test 方法
        example.test().implementation = function() {               
            // 再调用这个对象原来的方法
            this.test()
        }
    }
)

Hook 有参方法

Java.perform(
    function() {
        var example = Java.use("com.xxx.xx.xxx")
    // Hook example 类 test(string t) 有参方法。
    // 这里有几个参数就写几个参数。数据类型需要写全限定名,比如 String 就写成 java.lang.String,基本数据类型直接写就行,比如 int,就不用写包装类 java.lang.Integer。
    // 匿名函数中 t 参数是 test 方法参数的参数名。虽然说这个参数可以随便取名,但最好跟原来的保持一致,有识别度。
    example.test("java.lang.String").implementation = function(t) {
        // 控制台打印参数,方便调试。也是确认到底有没 Hook 成功
        console.log("com.xxx.xx.xxx.test(String t) 参数 t 的值:" + t)
    
        var parm = "篡改参数"
    
        // 篡改当前 example 类的 test("java.lang.String") 方法实参
        // 调用它自己原本的方法,把我们篡改的参数传入并执行
        // 获取返回值
        var result = this.test(parm)
    
        // 篡改 test("java.lang.String") 方法返回值。
        // return false
        // 如果不想篡改返回值就调用原本的方法直接返回即可,该传递的参数还是要传递的
        return this.test(t)
    }
)

Hook 重载方法只需要加个 overload。

Java.perform(
    function() {
        var example = Java.use("com.xxx.xx.xxx")
    // Hook example 类重载方法 test(int int)
        example.test.overload("int") = function(t) {
            // 控制台打印原参数,方便调试
            console.log("com.xxx.xx.xxx.test(String t) 参数 t 的值:" + t)
        
            var parm = 1
        
            // 篡改 test("java.lang.String") 方法实参
            var result = this.test(parm)
        
            // 篡改 test("java.lang.String") 方法返回值
            return false
        }
)

Hook 脚本写完后将 main 函数注入到 App 进程。

function main() {
    Java.perform(
        function() {
            ......
        }
    )
}

setImmediate(main)

Hook 方法时,形参类型不确定,可以随便写一个按照报错信息中的建议去改正,其次是整理个表,将常见的类型总结下来。

情况解决方法
参数是基本数据类型使用直接类型名:如 "int""[B"(一维 Byte 数组),"[[I"(二维 Int 数组)。
参数是普通类使用完整类路径:如 "java.util.ArrayList"
参数带泛型或接口忽略泛型部分,直接使用主类类型:如 "java.util.ArrayList"
方法签名复杂且类型不明确使用 getDeclaredMethods 或打印 param.getClass().getName() 进行分析。
需要验证动态类型或多种可能性使用 instanceofoverload 针对不同重载编写代码。

3.1.3 Android 主动调用

静态方法

  var uuid = Java.use("java.util.UUID");
  return uuid.randomUUID().toString();

实例方法

function stringToByte(str) {
  var javaString = Java.use("java.lang.String");
  var bytes = [];
  bytes = javaString.$new(str).getBytes();

  return bytes;
}
// 操作 ArrayList 数组
Java.perform(function () {
  var HttpUrlRequest = Java.use(
    "com.alipay.mobile.common.transport.http.HttpUrlRequest"
  );

  // public java.util.ArrayList<org.apache.http.Header> com.alipay.mobile.common.transport.http.HttpUrlRequest.getHeaders()
  HttpUrlRequest.getHeaders.implementation = function () {
    let headersArray = this.getHeaders();
    let httpHeaderObj = {};

    // Frida 会自动为 Java 对象提供动态绑定,可以直接调用对象的方法。
    for (let index = 0; index < headersArray.size(); index++) {
      let element = headersArray.get(index).toString();
      // console.log("请求头:", element.toString(), typeof element);
      httpHeaderObj[element.split(": ")[0]] = element.split(": ")[1];
    }

    console.log(JSON.stringify(httpHeaderObj));
    return headersArray;
  };
});

3.2 ObjC

API 文档:JavaScript API | Frida • A world-class dynamic instrumentation toolkit
官方 API 示例:iOS系统 |Frida • 世界一流的动态仪器工具包

拿到一个 App 后首先要判断是 Objective-C 还是 Swift 写的,后续才好,不研究原理的情况下有两种判定方式:

  1. 使用 die 或 IDA 把 ipa 包扔进去自动识别
  2. 通过对应 API 来判断当前有没运行环境

     console.log(`当前是不是 ObjC 环境:${ObjC.available}`)
     console.log(`当前是不是 Swift 环境:${Swift.available}`)

Apple Store 中安装的应用怎么把 IPA 包导出来静态分析?

3.2.1 iOS 定位类和方法

打印出 DTRpcMethod 类所有方法。

const resolver = new ApiResolver("objc");
const matches = resolver.enumerateMatches("*[DTRpcMethod *]"); // 模糊匹配类和方法
matches.forEach((match) => {
  console.log(JSON.stringify(match));
});
Frida hook/invoke iOS以及内存搜刮和黑盒调用-iOS安全-看雪-安全社区|安全招聘|kanxue.com

3.2.2 ObjC.classes

ObjC.classes,返回一个 JavaScript 对象,这个对象包含着 App 所有已经加载的类对象 ObjC.Object。大概的结构如下。

ObjC = {}
ObjC['classes'] = {
    "className1": {"handle": "0x107a5b370"},
    "className2": {"handle": "0x107a5a920"}
}

我们可以用 ObjC.classes 遍历系统加载哪些类,最常见的用法遍历的过程中直接过滤出我们想要的类对象。

/**
 * 获取应用已加载的类对象
 * @param {String} filterClassName 要查找的类名
 * @returns 返回类对象
 */
function findClassObjByName(filterClassName) {
  for (var className in ObjC.classes) {
    if (className === filterClassName) {
      console.log(`${className} 类存在`);
      return ObjC.classes.className; 
      // 访问对象属性的另一种方式
      // return ObjC.classes[className];
    }
    return null;
  }
}

另一种获取类的对象的方式是直接写类名。

ObjC.classes.类名

这里需要了解下 ObjC 中方法签名,也就是方法和参数格式是怎么写的。

# -/+[methodName:parameterName:]
# 开头的 - 是实例方法,+ 是类方法,方括号中冒号前半部分是方法名称,后半部分是参数名称,没有参数就只写方法名,后面不用跟冒号。有几个冒号就代表当前方法有几个参数,没有冒号表示无参数。
+[cla]

3.3.3 ObjC.Object

前面获取到了我们的 ObjC.Object 对象后,那么就对这个对象进行操作,比如获取方法名,调用方法,重写方法等等。

ObjC.Object 有很多属性:

  • $methods。返回一个数组,里面包含着当前类和父类的 Native Method 方法名;
  • $ownMethods。返回一个数组,里面包含着当前类 Native Method 方法名;
  • $className。返回这个对象的类名。

获取要 Hook 的目标信息。这里为了方便就用 -H 选项,通过网络的方式来连接。

(ven_frida) PS C:\Users\gbb> frida-ps -H 172.20.10.5 -a
 PID  Name        Identifier
----  ----------  -------------
1076  xxxxxxxxxx  com.toyopagroup.picaboo

运行脚本。通过 —H 选项以 Socket 远程连接服务端,-l 是要运行的脚本

frida -H 172.20.10.5 -f com.toyopagroup.picaboo -l D:\raingray\Project\20240521-xxx\TestFile\frida-agent-example-main\UIAlertController\hook-devicePrisonBreakDetection.js

查询指定类下有哪些方法。

// 列出所有类
for (var className in ObjC.classes) {
    console.log(className);
}

// 列出类的所有方法
var TargetClass = ObjC.classes.TargetClassName;
for (var methodName in TargetClass.$methods) {
    console.log(methodName);
}

展示方法并不会像 Java 那样展示返回值类型,参数名,参数类型这些信息,ObjC 就只会告诉你当前是什么类型的方法,有几个参数。

有了方法和参数需要获取这个方法的对象,重写指定方法,具体操作方式是用 Interceptor.attach 进行操作。

Interceptor.attach(target, {
  onEnter: function (args) {
    ...
  },
  onLeave: function (retval) {
    ...
  },
});

Interceptor.attach 第一个参数是 target,要 attach 的目标,这个 target 它需要传入方法的内存地址或者指针才能操作。

Interceptor.attach 第二个参数是 callbacks 回调方法匹配上后会运行,回调里的 onEnter 是方法第一行代码执行前运行,onLeave 是方法执行到 return 前运行,如果 onEnter 和 onLeave 其中某一个不用的使用可以不写,加快运行效率。

target 参数使用方式一:通过方法 NativePointer attch

这样的好处是可以精准定位重载方法。

let myResolver = new ApiResolver("objc"); // 根据 iOS App 语言来指定对象,是 ObjC 写 objc,Swift 就写 swift
let searchResult = myResolver.enumerateMatches(
  "-[DTURLRequestOperation addHTTPBodyParameter:forKey:]"
); // 这是 Objc 的查询格式,可能 Swift 不一样。
// console.log(JSON.stringify(searchResult));  // 最终返回一个数组,数组内是对象。
let functinoNativePoint = ptr(searchResult[0].address); // 获取方法指针

Interceptor.attach(functinoNativePoint, {
  onEnter: function (args) {
    // ObjC 参数格式: args[0] = self, args[1] = selector, args[2-n] = arguments
    console.log("参数3:" + new ObjC.Object(args[2])); // 第一个参数索引是 2,把参数转换成 Object 对象
  },
  onLeave: function (retval) {
    // 处理返回值
  },
});

target 参数使用方式二:直接扔方法的引用 attach

Interceptor.attach 会自动处理传进来的类引用转成 NativePointer,哪怕不用 ptr 转成指针也没事,frida 会自动处理。

let addHTTPBodyParameter =
  ObjC.classes.DTURLRequestOperation["- addHTTPBodyParameter:forKey:"];

Interceptor.attach(addHTTPBodyParameter.implementation, {
  onEnter: function (args) {
    console.log("第一个参数:" + new ObjC.Object(args[2])); 
  },
  onLeave: function (retval) {
    ...
  },
});

Objective-C 是强类型语言,只知道方法需要传递几个参数,怎么确认当前参数需要传递什么类型?

可以直接输出参数值,如果是 0x1078dc5a8 这种指针类型,那就尝试 new ObjC.Object() 直接转成对象,没有抛异常就说明是 ObjC.Object 类型。

// 第一个参数索引是 2,把参数转换成 Object 对象
console.log("参数3:" + new ObjC.Object(args[2]));

// 打印参数 Object 的类型,后续可以再查里面的方法去 Hook
console.log("参数3:" + new ObjC.Object(args[2]).$className);

PS:在 onEnter 创建的变量想在 onLeave 中使用的操作方法

Interceptor.attach(Module.getExportByName(null, 'read'), {
  onEnter(args) {
    // Save arguments for processing in onLeave.
    this.buf = args[1];
  },
  onLeave(result) {
    // Show argument 1 (buf), saved during onEnter.
    console.log(this.buf);
  }
})

3.2.4 主动调用🔨

方法名后面要加上下划线,括号里面则是实参。

// NSTring 是类名
// stringWithString 是方法名
// _("foo") 是向方法传递参数 foobar
ObjC.classes.NSTring.stringWithString_("foobar");

3.3 Swift🔨

目前 v15.1.0 中加入了 Swift 支持

使用的是 https://github.com/frida/frida-swift-bridge

3.4 Frida Python API

3.4.1 Python 注入模式

iOS

# 获取设备 https://github.com/frida/frida/issues/3278
device, devices = None, frida.enumerate_devices()
for d in devices:
    device = d if 'iPhone' in d.name else None

# 打印进程名称
# processes = device.enumerate_processes()
# for process in processes:
#     print(f"{process.pid} name:'{process.name}'")

# attach 模式(目前测试只能 attach,用 spawn 会超时,但是别人设备没出现此问题),需要输入进程名称
session = device.attach("ssxxs-pro") # XX 应用 attach。
# session = device.attach("ss-ios") # XX 应用 attach

# spawn 模式
# pid = device.spawn(['com.xx.xx']) # XX 应用 spawn 模式、
# pid = device.spawn(['com.xx.xx']) # XX 应用 spawn 模式
# sleep(1)  # 延迟以确保进程已就绪
# session = device.attach(pid)
# device.resume(pid)

Android

# attach 模式
device = frida.get_usb_device()
session = device.attach("应用名称")

# spawn 模式
device = frida.get_usb_device()
pid = device.spawn(['com.xx.xx']) # XX 应用
# pid = device.spawn(['com.xx.xx']) # XX 应用
sleep(1)  # 延迟以确保进程已就绪
session = device.attach(pid)
device.resume(pid)

3.4.2 Frida 与 Python 消息传递

https://frida.re/docs/messages/

注入模式

# attach 模式
frida.get_usb_device().attach('应用名称或者 pid')

# spawn 模式
device=frida.get_remote_device()
pid=device.spawn(['com.xx.xx'])  #包名
device.resume(pid)
session = device.attach(pid)

import time
device = frida.get_usb_device()
pid = device.spawn(['com.xx.xx'])
time.sleep(1)  # 延迟以确保进程已就绪
session = device.attach(pid)
device.resume(pid)

JavaScript 向 Python 发送消息。

send({
  type: "RESP",
  data: byteToString(response.getResData()),
});

recv(function (resp_obj) {
  let newResponseContent = stringToByte(resp_obj.payload);
  response.setResData(newResponseContent);
}).wait();

Python 接收消息。

import frida

# 加载脚本
script = None
with open("Android-xxx-Test.js", encoding='utf-8') as f:
    script = session.create_script(f.read())

# 处理 JS 发来的消息
def on_message(message, data):
    print(f'message = {message}')
    if message['type'] == 'send':
        payload = message['payload']
        _type, data = payload['type'], str(payload['data'])
        if _type == 'RESP':
            r = requests.request(
                'RESPONSE', 
                f'http://127.0.0.1:{ECHO_PORT}/',
                proxies={'http': f'http://127.0.0.1:{BURP_PORT}'},
                data=data.encode('utf-8'))
            # 将修改后的请求(就是代理服务器的响应)信息发给 Frida
            script.post({'type': 'NEW_RESP', 'payload': r.text})

script.on('message', on_message) #注册消息处理函数
script.load() #注入脚本
sys.stdin.read() # 回显log输出

需要注意,一旦使用了 send,那么代码的异常也会被当作消息被发送到 Python。

3.5 其他 API🔨

3.5.1 Stalker

代码跟踪

3.5.2 Thread

线程模块

3.5.3 Process

进程模块

3.5.4 Kernel

内核模块

3.5.5 Memory

内存模块

3.5.6 Module

能够查询加载了那些模块,还可以单独加载模块

4 Objection

Objection 对 Frida 做了包装,可以免去很多代码编写工作。

安装它只需使用 Python3 包管理工具 pip。

PS D:\adb\ > pip install -U objection

使用 Objection 首先先运行 frida-server。

PS D:\adb\ > ./adb shell su -c "/data/local/tmp/frida-server-15.1.10-android-x86 &"

4.1 搜索类和方法

Objection 列出所有内存中的类。

android hooking list classes

Objection 搜包含指定关键字的类。

android hooking search classes response

有时候类有几千个,这些数据终端上不会缓冲,因此前面输出的内容会被覆盖,没法完全显示这些输出日志,好在 objection 会把这些执行结果最终保存到 %userprofile%/.objection/objection.log,当然也可以设置终端的显示行数来规避覆盖问题。

另外这里都是写的 Android 搜索,如果要搜索 iOS,就把开头的 android 换 ios 就好。

Objection 查看指定类有哪些方法。

android hooking list class_methods com.alipay.mobile.common.transport.http.HttpUrlRequest

搜包含指定关键词的方法。

android hooking search methods <keyWord>

4.2 Hook 指定方法

Objection Hook 指定方法并输出调用堆栈、返回参数、形式参数。

ios hooking watch method "-[DTURLRequestOperation:addHTTPBodyParameter:forKey:]" --dump-backtrace --dump-args --dump-return

Objection Hook 指定类下所有方法。

android hooking watch  class com.android.okhttp.RequestBody --dump-args --dump-backtrace --dump-return

PS:使用 Objection 的过程中发现无法 Hook 目标的形参、返回值和堆栈信息,切换到 Frida 去执行 Hook 脚本则没问题。

jobs 命令查看当前 Hook 了那些方法

jobs list

删除 Hook。

jobs kill id

4.3 其他功能

查看 Android 四大件

android hooking list activities | services | receivers | providers

5 对抗🔨

Android-Root 检测

实际情况中我发现目标使用正常版本的 Frida 会闪退,根据 MpaaS文档介绍,加固检测到问题就会闪退。

(ven_frida) PS D:\raingray\Project\20240521-xx\TestFile\MobileAppHook> python .\Android-RoadShow-SendReqDataToBurp.py
Traceback (most recent call last):
  File "D:\raingray\Project\20240521-xx\TestFile\MobileAppHook\Android-RoadShow-SendReqDataToBurp.py", line 48, in <module>
    session = device.attach("XX 应用")
              ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\raingray\Project\20240521-xx\TestFile\ven_frida\Lib\site-packages\frida\core.py", line 86, in wrapper
    return f(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^
  File "D:\raingray\Project\20240521-xx\TestFile\ven_frida\Lib\site-packages\frida\core.py", line 1010, in attach
    return Session(self._impl.attach(self._pid_of(target), **kwargs))  # type: ignore
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
frida.InvalidArgumentError: dlsym failed: invalid handle: 0x2e8ef3fd1e7a987

root 检测安装 Magisk Shamiko 模块后再去 Magisk 设置中配置排除列表加上要绕过 root 的应用。操作步骤参考 https://www.t00ls.com/articles-68782.html

也有直接刷 ROM,重新编译 Android 系统。

iOS-越狱检测

默认会检测 Cydia 商店,更换为 Sileo 就能绕过。

Frida 检测

frida 检测(是壳的检测机制),最后用的 https://github.com/Ylarod/Florida 绕过了检测。这是其他 FridaServer 魔改项目,用作参考:

梳理下来有这些检测点可以判断是不是存在 Frida-Server:

  • 默认文件名 frida-server-16.2.4-android-arm64。更换名字再运行。
  • 默认端口 27042。使用 -l 选项监听其他端口
  • ......

最终都要手动编译源码改特征。

frida-gadget 也能绕

参考文章:

  • [[原创]《安卓逆向这档事》第十八课、表哥,你也不想你的Frida被检测吧!(上)-Android安全-看雪-安全社区|安全招聘|kanxue.com](https://bbs.kanxue.com/thread-282555.htm)
  • [[原创]《安卓逆向这档事》第十九课、表哥,你也不想你的Frida被检测吧!(下)-Android安全-看雪-安全社区|安全招聘|kanxue.com](https://bbs.kanxue.com/thread-282623.htm)

iOS Hook

但切到 Objection 远程连接则没有问题。

(ven_frida) PS D:\raingray\Tools\MobileAppication\adb> objection -N -h 192.168.66.62 -g com.xx.xx explore
Using networked device @`192.168.66.62:27042`
Agent injected and responds ok!

     _   _         _   _
 ___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_|  _|  _| | . |   |
|___|___| |___|___|_| |_|___|_|_|
      |___|(object)inject(ion) v1.11.0

     Runtime Mobile Exploration
        by: @leonjza from @sensepost

[tab] for command suggestions

参考 MpaasPentestTool 项目搜索请求类 DTURLRequestOperation,依旧存在,说明 2020 年到 2024 年代码没有变化。

com.xx.xx on (iPhone: 15.8.2) [net] # ios hooking search classes DTURLRequestOperation
DTURLRequestOperation

Found 1 classes

搜索 DTURLRequestOperation 是不是存在实例方法 - addHTTPBodyParameter:forKey:,发现也存在。

com.xx.xx on (iPhone: 15.8.2) [net] # ios hooking list class_methods DTURLRequestOperation
+ URLRequestThread
+ URLRequestThreadProc:
+ defaultOperationQueue
+ keyPathFromState:
+ stateTransitionIsValid:toState:cancelled:
- RPCId
- doFinalHTTPBody
- isCrypt
- isH2RPC
- requestBodySign
- setMetricsDict:
- setResponseOrg:
- gmFirst
- gzipAndEncrypt:
- httpGET
- requestBodyGZip
- addHTTPBodyParameter:forKey:
- addRetryToRequest:
- asymAlgorithm
- createdThread
- cryptUtils
- customAppKey
- deKey
- evaluateServerTrust:forDomain:
- gatewayURL
- gunzipAndDecrypt:
- httpBodyParameters
- httpDnsIp
- initWithRequest:outputStream:
- netRetryable
- receiveChallengeHandler
- requestTrustedSign
- responseOrg
- rpcEncrypt:
- rpcOperationType
- rpcV1Sign:newSign:request:
- runningThread
- setAsymAlgorithm:
- setCreatedThread:
- setCryptUtils:
- setCustomAppKey:
- setDeKey:
- setGatewayURL:
- setGmFirst:
- setHttpBodyParameters:
- setHttpDnsIp:
- setHttpGET:
- setIsCrypt:
- setIsH2RPC:
- setNetRetryable:
- setRPCId:
- setReceiveChallengeHandler:
- setRequestBodyGZip:
- setRequestBodySign:
- setRequestTrustedSign:
- setResponseObject:
- setRunningThread:
- shouldCrypt
- stopSendRPC
- startConnection
- connection:didReceiveData:
- setTask:
- isExecuting
- setOutputStream:
- setResponseData:
- setLock:
- inputStream
- connection
- isReady
- URLSession:task:didFinishCollectingMetrics:
- cancel
- responseData
- setRequest:
- URLSession:task:didCompleteWithError:
- lock
- hasCanceled
- URLSession:task:didReceiveChallenge:completionHandler:
- finish
- setTotalBytesRead:
- networkActivityIndicatorVisible
- runLoopModes
- connectionDidFinishLoading:
- setHasCanceled:
- isFinished
- isConcurrent
- setMetrics:
- start
- init
- setRunLoopModes:
- commonInit
- task
- URLSession:dataTask:didReceiveData:
- setError:
- initWithRequest:
- didFinish
- connection:didReceiveResponse:
- request
- setConnection:
- state
- .cxx_destruct
- setTimeoutTimer:
- timeoutTimer
- dealloc
- outputStream
- connection:didFailWithError:
- error
- setResponse:
- setState:
- metrics
- didStart
- setNetworkActivityIndicatorVisible:
- setInputStream:
- response
- responseString
- setResponseString:
- cancelConnection
- totalBytesRead
- responseObject
- metricsDict

Found 119 methods
com.xx.xx on (iPhone: 15.8.2) [net] #

mPaaS-请求参数 Hook-v1 到通杀的思路

直接 Hook 确实可以修改请求参数,但是无法获取和修改响应明文,Hook 后一直说这个实例方法不存在。

public void com.alipay.mobile.common.transport.http.HttpUrlRequest.setReqData(byte[])

需要确认 setReq 调用堆栈,通过 Hook 发送请求的方法,手动发送请求获取响应。

console.log(
"HttpUrlRequest.setReqData 调用堆栈:\n" +
  Java.use("java.lang.Exception").$new().getStackTrace().toString()
);

随后就想着直接 Hook 发送请求的方法到底是哪个,通过查看 setReqData 的堆栈。

com.alipay.mobile.common.transport.http.HttpUrlRequest.setReqData(Native Method),
com.alipay.mobile.common.rpc.transport.http.HttpCaller.call(Unknown Source:50),
com.alipay.mobile.common.rpc.RpcInvoker.singleCall(Unknown Source:137),
com.alipay.mobile.common.rpc.RpcInvoker.invoke(Unknown Source:217),
com.alipay.mobile.common.rpc.RpcInvocationHandler.invoke(Unknown Source:11),
java.lang.reflect.Proxy.invoke(Proxy.java:1006),
$Proxy8.getUserInfo(Unknown Source),
java.lang.reflect.Method.invoke(Native Method),
com.xx.xx.rpc.RpcClient$2.subscribe(Unknown Source:90),
io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual(Unknown Source:10),
io.reactivex.Observable.subscribe(Unknown Source:14),
io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(Unknown Source:6),
io.reactivex.Scheduler$DisposeTask.run(Unknown Source:9),
io.reactivex.internal.schedulers.ScheduledRunnable.run(Unknown Source:13),
io.reactivex.internal.schedulers.ScheduledRunnable.call(Unknown Source:0),
java.util.concurrent.FutureTask.run(FutureTask.java:266),
java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301),
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167),
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641),
java.lang.Thread.run(Thread.java:919)

......

com.alipay.mobile.common.transport.http.HttpUrlRequest.setReqData(Native Method),
com.alipay.mobile.common.transport.http.selfencrypt.SelfEncryptUtils.getEncryptedEntity(Unknown Source:40),
com.alipay.mobile.common.transport.http.HttpWorker.getPostData(Unknown Source:80),
com.alipay.mobile.common.transport.http.HttpWorker.getTargetHttpUriRequest(Unknown Source:92),
com.alipay.mobile.common.transport.http.HttpWorker.call(Unknown Source:18),
com.alipay.mobile.common.transport.http.HttpWorker.call(Unknown Source:0),
java.util.concurrent.FutureTask.run(FutureTask.java:266),
com.alipay.mobile.common.transport.concurrent.ZFutureTask.run(Unknown Source:3),
com.alipay.mobile.common.transport.http.HttpTask.run(Unknown Source:0),
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167),
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641),
java.lang.Thread.run(Thread.java:919)

看了几个请求的堆栈,上面都是 com.alipay.mobile.common.rpc.transport.http.HttpCaller 这个类的 call 方法,好像是它发起的请求。

iOS-mPaaS-请求参数

项目原封不动就能用。

但是根据调用链条也能打印出明文参数,感觉其他类中的方法也可以 Hook

DTRpcMethod

// requestHeaderField 能打印出明文参数 args[3]
DTRpcClient [- executeMethod:params:requestHeaderField:responseHeaderFields:"]

// 响应值 hook 能获取到 __NSDictionaryI 类型的响应明文
-[DTRpcClient executeMethod:params:requestHeaderField:responseHeaderFields:]

// 返回参数类型是__NSDictionaryI,是对应响应明文
-[DTRpcClient executeOperation:responseHeaderFields:]

// 返回值能获取到操作类型,比如 Operation-Type: com.dev.getImage
-[DTRpcMethod operationType]

Objection XX 应用测试记录

应用弹窗,可以得知使用的 UIAlertController 类。

应用检测到系统越狱弹窗信息.png

hook 弹窗 UIAlertController 类下所有方法。加号 + 打头的是静态方法,减号 - 开头的是实例方法。以 iOS 8 以上弹警调用为例 UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"通知" message:@"通过检测" preferredStyle:UIAlertControllerStyleAlert]; 的话 Java 语言 UIAlertController("通知", "通过检测", UIAlertControllerStyleAlert)

com.xx.xx on (iPhone: 14.3) [usb] # ios hooking list class_methods UIAlertController

+ mf_muteConfirmationControllerWithHandler:
+ mf_actionSheetWithTitle:cancellationHandler:
+ px_showDebugAlertInKeyWindowWithMessage:
+ px_alertForCPLEnableError:actionHandler:cancelHandler:
+ px_deleteITunesContentAlertWithAssetCount:includesPhotos:includesVideos:actionHandler:cancelHandler:
+ px_alertForStorageUpgradeLoadFailure
+ px_alertControllerWithError:completionHandler:
+ alertControllerWithTitle:message:preferredStyle:
+ _alertControllerContainedInViewController:
+ _shouldUsePresentationController
+ _alertControllerWithTitle:message:
+ _allowInteractiveSheetDismissal
- myFixupLayout
- viewWillLayoutSubviews
- mf_addCancelActionWithHandler:
- mf_presentFromViewController:withSource:
- mf_presentConfirmationSheetFromViewController:withSource:
- setPx_shouldForceAlertStyle:
- px_shouldForceAlertStyle
- canBecomeFirstResponder
- canPerformAction:withSender:
- previewInteractionController:viewControllerForPreviewingAtPosition:inView:presentingViewController:
- willTransitionToTraitCollection:withTransitionCoordinator:
- _getRotationContentSettings:
- _performAction:invokeActionBlock:dismissAndPerformActionIfNotAlreadyPerformed:
- previewInteractionController:willPresentViewController:forPosition:inSourceView:
- previewInteractionController:performCommitForPreviewViewController:committedViewController:
- _presentationControllerForPresentedController:presentingController:sourceController:
- performsViewControllerCommitTransitionForPreviewInteractionController:
- setMessage:
- _dismissAnimated:triggeringAction:triggeredByPopoverDimmingView:dismissCompletion:
- _headerContentViewControllerWillTransitionToSize:withAnimations:
- setContentViewController:
- _postDidBeginSystemProvidedDismissalOfAlertController
- _viewControllerIsPresentedInModalPresentationContext:
- setPreferredStyle:
- shouldAutorotate
- _updateViewFrameForLandscapePresentationInShimIfNecessary
- _childViewController:willTransitionToSize:withAnimations:
- _contentViewControllerWillTransitionToSize:withAnimations:
- _addActionWithTitle:style:handler:shouldDismissHandler:
- _action:changedToKeyCommandWithInput:modifierFlags:
- _setView:forSystemProvidedPresentationWithDelegate:
- _postWillBeginSystemProvidedDismissalOfAlertController
- .cxx_destruct
- traitCollectionDidChange:
- _textFieldContainingViewWithTextField:position:
- supportedInterfaceOrientations
- _hasTitle
- _beginNoPresentingViewControllerPresentation
- _updateTextFieldViewControllerWithVisualStyle:
- _attemptAnimatedDismissWithGestureRecognizer:
- _addKeyCommandForAction:withInput:modifierFlags:
- actions
- preferredStyle
- _shouldFitWidthToContentViewControllerWidth
- dealloc
- preferredContentSizeDidChangeForChildContentContainer:
- message
- contentViewController
- viewWillDisappear:
- _keyCommandForAction:input:modifierFlags:
- _setSeparatedHeaderContentViewController:
- _setPresentationSourceRepresentationView:
- _modalPresentationStyleForResolvedStyle
- _setSystemProvidedPresentationDelegate:
- preferredAction
- setCoordinatedActionPerformingDelegate:
- _updateProvidedStyleWithTraitCollection:
- loadView
- _endNoPresentingViewControllerPresentation
- _addActionWithTitle:image:style:handler:
- _flipFrameForShimDismissalIfNecessary
- addTextFieldWithConfigurationHandler:
- _systemProvidedGestureRecognizer
- _returnKeyPressedInLastTextField
- _setIndexesOfActionSectionSeparators:
- set_shouldFlipFrameForShimDismissal:
- _separatedHeaderContentViewController
- _performBatchActionChangesWithBlock:
- _dismissWithAction:dismissCompletion:
- _canDismissWithGestureRecognizer
- _requiresCustomPresentationController
- _presentationSourceRepresentationView
- _visualStyle
- addAction:
- _shouldFlipFrameForShimDismissal
- _setHeaderContentViewController:
- _restoreInputViewsAnimated:
- viewDidLayoutSubviews
- _setCompatibilityPopoverController:
- _dismissAnimated:triggeringAction:
- _isSupportedInterfaceOrientation:
- setTemporaryAnimationCoordinator:
- _buttonTypeForBackGestureForIdiom:
- _addReturnKeyCommandIfAppropriate
- _hasMessage
- _setPreviewInteractionController:
- _setHidden:
- _didParentTextFieldViewController
- _indexesOfActionSectionSeparators
- _becomeFirstResponderIfAppropriate
- _addActionWithTitle:style:handler:
- _handleReturn
- _willParentTextFieldViewController
- _cancelAction
- _preserveInputViewsAnimated:
- cancelAction
- _setActions:
- _effectAlpha
- _systemProvidedPresentationDelegate
- description
- setTextFieldsCanBecomeFirstResponder:
- _setSystemProvidedPresentationView:
- _shouldSupportReturnKeyPresses
- coordinatedActionPerformingDelegate
- _dismissFromPopoverDimmingView
- setTitle:
- viewWillAppear:
- _recomputePreferredContentSize
- _setShouldAllowNilParameters:
- isSpringLoaded
- _updateModalPresentationStyle
- _installBackGestureRecognizer
- _needsPreferredSizeCalculated
- _shouldTreatEmptyStringsAsNil
- setModalPresentationStyle:
- _previewInteractionController
- _styleProvider
- setSpringLoaded:
- temporaryAnimationCoordinator
- _removeKeyCommandForAction:
- _shouldBecomeFirstResponder
- _alertControllerContainer
- _setHasPreservedInputViews:
- viewWillTransitionToSize:withTransitionCoordinator:
- _uninstallBackGestureRecognizer
- _containedAlertController
- setCancelAction:
- _resolvedStyle
- _compatibilityPopoverController
- _focusedAction
- _contentInsets
- _systemProvidedPresentationView
- _setEffectAlpha:
- _actions
- _isPresented
- _foregroundView
- viewDidAppear:
- _updateShouldAlignToKeyboard
- _allowsShowingDimmingView
- _setAttributedDetailMessage:
- _dismissGestureRecognizer
- _isHidden
- _setShouldReverseActions:
- _headerContentViewController
- _invokeHandlersForAction:
- _shouldProvideDimmingView
- _shouldAllowNilParameters
- viewDidDisappear:
- _shouldPreserveInputViews
- _alignsToKeyboard
- _attributedDetailMessage
- _reevaluateResolvedStyle
- _dismissWithCancelAction
- _setAttributedTitle:
- initWithNibName:bundle:
- _removeAllTextFields
- _textFieldViewController
- _shouldReverseActions
- _removeAllActions
- _dismissFromBackButton:
- _returnKeyCommand
- _hasPreservedInputViews
- _isPresentedAsPopover
- _setTitleLineBreakMode:
- _actionDelimiterIndices
- _resolvedStyleChanged
- _hasDetailMessage
- _hasAttributedMessage
- _idiomSupportsBackGesture:
- _shouldDismissOnSizeChange
- _canBePresentedAtLocation:
- _shouldSizeToFillSuperview
- _setDismissedFromShim:
- _setTitleMaximumLineCount:
- _attributedTitle
- _setShouldAlignToKeyboard:
- setImage:
- _alertControllerView
- _updateProvidedStyle
- _dimmingView
- _clearActionHandlers
- _setVisualStyle:
- _hasContentToDisplay
- _addSectionDelimiter
- linkAlertController:
- _actionForReturnKey
- _hasAttributedTitle
- _dismissWithAction:
- _titleLineBreakMode
- _shouldAlignToKeyboard
- _setAttributedMessage:
- setPreferredAction:
- _powerLoggingEventName
- unlinkAlertController:
- linkedAlertControllers
- _titleMaximumLineCount
- _setSystemProvidedGestureRecognizer:
- textFields
- visualStyleForAlertControllerStyle:traitCollection:descriptor:
- image
- _attributedMessage
- _currentDescriptor
- _logBeingPresented
- _logBeingDismissed
- _dismissedFromShim
- _setStyleProvider:
- viewDidLoad
- _handleKeyCommand:
- viewWillLayoutSubviews

Found 221 methods
com.xx.xx on (iPhone: 14.3) [usb] #

hook 指定方法,这里是 UIAlertController 类下的 message方法。

(ven_frida) C:\Users\gbb>objection -g com.xx.xx explore --startup-command "ios hooking watch method '-[UIAlertController message]' --dump-return --dump-arg"
Using USB device `Apple iPhone`
Agent injected and responds ok!
Running a startup command... ios hooking watch method '-[UIAlertController message]' --dump-return --dump-arg
(agent) Found selector at 0x199d1b068 as -[UIAlertController message]
(agent) Registering job 893910. Type: watch-method for: -[UIAlertController message]

     _   _         _   _
 ___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_|  _|  _| | . |   |
|___|___| |___|___|_| |_|___|_|_|
      |___|(object)inject(ion) v1.11.0

     Runtime Mobile Exploration
        by: @leonjza from @sensepost

[tab] for command suggestions
(agent) com.xx.xx on (iPhone: 14.3) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0
(agent) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0
(agent) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0
(agent) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0
(agent) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0
(agent) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0
(agent) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0
(agent) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0
(agent) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0
(agent) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0
(agent) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0
(agent) [893910] Called: -[UIAlertController message] 0 arguments(Kind: instance) (Super: UIViewController)
(agent) [893910] Return Value: 0x0

为了方便看到 hook 的方法调用链,可以用是 --dump-backtrace。

(ven_frida) C:\Users\gbb>objection -g com.xx.xx explore --startup-command "ios hooking watch method '+[UIAlertController alertControllerWithTitle:message:preferredStyle:]' --dump-backtrace"
Checking for a newer version of objection...
Using USB device `Apple iPhone`
Agent injected and responds ok!
Running a startup command... ios hooking watch method '+[UIAlertController alertControllerWithTitle:message:preferredStyle:]' --dump-backtrace
(agent) Found selector at 0x199d1588c as +[UIAlertController alertControllerWithTitle:message:preferredStyle:]
(agent) Registering job 743735. Type: watch-method for: +[UIAlertController alertControllerWithTitle:message:preferredStyle:]
(agent) [743735] Called: +[UIAlertController alertControllerWithTitle:message:preferredStyle:] 3 arguments(Kind: class) (Super: UIViewController)

     _   _         _   _
 ___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_|  _|  _| | . |   |
|___|___| |___|___|_| |_|___|_|_|
      |___|(object)inject(ion) v1.11.0

     Runtime Mobile Exploration
        by: @leonjza from @sensepost

[tab] for command suggestions
com.xx.xx on (iPhone: 14.3) [usb] # (agent) [743735] +[UIAlertController alertControllerWithTitle:message:preferredStyle:] Backtrace:
        0x100a843a0 /private/var/containers/Bundle/Application/E4111180-3BCD-484F-AB4C-047C9830B959/xx-ios.app/xx-ios!-[AppDelegate devicePrisonBreakDetection]
        0x100a1e968 /private/var/containers/Bundle/Application/E4111180-3BCD-484F-AB4C-047C9830B959/xx-ios.app/xx-ios!-[AppDelegate application:didFinishLaunchingWithOptions:]
        0x19a875320 UIKitCore!-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:]
        0x19a877388 UIKitCore!-[UIApplication _callInitializationDelegatesWithActions:forCanvas:payload:fromOriginatingProcess:]
        0x19a87cde4 UIKitCore!-[UIApplication _runWithMainScene:transitionContext:completion:]
        0x199ed2f20 UIKitCore!-[_UISceneLifecycleMultiplexer completeApplicationLaunchWithFBSScene:transitionContext:]
        0x19a441054 UIKitCore!_UIScenePerformActionsWithLifecycleActionMask
        0x199ed3ab8 UIKitCore!__101-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]_block_invoke
        0x199ed34c0 UIKitCore!-[_UISceneLifecycleMultiplexer _performBlock:withApplicationOfDeactivationReasons:fromReasons:]
        0x199ed38c8 UIKitCore!-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]
        0x199ed3104 UIKitCore!-[_UISceneLifecycleMultiplexer uiScene:transitionedFromState:withTransitionContext:]
        0x199edb650 UIKitCore!__186-[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block_invoke
        0x19a34d6c4 UIKitCore!+[BSAnimationSettings(UIKit) tryAnimatingWithSettings:actions:completion:]
        0x19a45977c UIKitCore!_UISceneSettingsDiffActionPerformChangesWithTransitionContext
        0x199edb348 UIKitCore!-[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]
        0x199d0284c UIKitCore!__64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke
com.xx.xx on (iPhone: 14.3) [usb] #
ios hooking watch method '+[UIAlertController alertControllerWithTitle:message:preferredStyle:]' --dump-return --dump-args --dump-backtrace
objection -g com.xx.xx explore --startup-command "ios hooking watch method '-[AppDelegate devicePrisonBreakDetection]' --dump-return --dump-arg --dump-backtrace

参考链接

最近更新:

发布时间:

摆哈儿龙门阵