mirror of
https://github.com/Lakr233/vphone-cli.git
synced 2026-04-04 20:39:05 +08:00
Create txm_return_mechanism.md
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1 +0,0 @@
|
||||
*.tar.zst filter=lfs diff=lfs merge=lfs -text
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -315,7 +315,7 @@ __marimo__/
|
||||
/.swiftpm
|
||||
*.ipsw
|
||||
/updates-cdn
|
||||
/researchs/jb_asm_refs
|
||||
/research/jb_asm_refs
|
||||
TODO.md
|
||||
/references/
|
||||
scripts/vphoned/vphoned
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "scripts/resources"]
|
||||
path = scripts/resources
|
||||
url = https://github.com/Lakr233/vphone-cli-storage.git
|
||||
@@ -99,9 +99,9 @@ scripts/
|
||||
├── setup_venv.sh # Creates Python venv with native keystone dylib
|
||||
└── setup_libimobiledevice.sh # Builds libimobiledevice toolchain from source
|
||||
|
||||
researchs/
|
||||
├── jailbreak_patches.md # JB vs base patch comparison table
|
||||
└── ... # Component analysis and architecture docs
|
||||
research/
|
||||
├── patch_comparison_all_variants.md # Regular/Dev/JB patch comparison table
|
||||
└── ... # Component analysis and architecture docs
|
||||
```
|
||||
|
||||
### Key Patterns
|
||||
@@ -429,7 +429,7 @@ Branch is 8 commits ahead of `main`. All changes are **additive** — non-JB cod
|
||||
| `patch_thid_should_crash` | Zero `0x67EB50` | String in `__PRELINK_INFO` plist (no code refs); value already `0x00000000` in PCC kernel | Safe to return True (no-op); or find via `sysctl_oid` struct search in `__DATA` |
|
||||
| `patch_hook_cred_label_update_execve` | Shellcode at `0xAB17D8` + ops table at `0xA54518` | Needs `_vfs_context_current` (`0xCC5EAC`) and `_vnode_getattr` (`0xCC91C0`) — 0 symbols available | Find via sandbox ops table → original hook func → BL targets by caller count (vfs_context_current = highest, vnode_getattr = near `mov wN, #0x380`) |
|
||||
|
||||
### Key Findings (from `researchs/kernel_jb_remaining_patches.md`)
|
||||
### Key Findings (from `research/kernel_jb_patch_notes.md`)
|
||||
|
||||
**All offsets in `kernel.py` are file offsets** — `bl_callers` dict, `_is_bl()`, `_disas_at()`, `find_string_refs()` all use file offsets, not VAs.
|
||||
|
||||
|
||||
26
LICENSE
26
LICENSE
@@ -1,13 +1,21 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
MIT License
|
||||
|
||||
Copyright (C) 2026 Sam Hocevar <sam@hocevar.net>
|
||||
Copyright (c) 2026 @Lakr233
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
70
README.md
70
README.md
@@ -1,10 +1,10 @@
|
||||
<div align="right"><strong><a href="./README_ko.md">🇰🇷한국어</a></strong> | <strong><a href="./README_ja.md">🇯🇵日本語</a></strong> | <strong><a href="./README_zh.md">🇨🇳中文</a></strong> | <strong>🇬🇧English</strong></div>
|
||||
<div align="right"><strong><a href="./docs/README_ko.md">🇰🇷한국어</a></strong> | <strong><a href="./docs/README_ja.md">🇯🇵日本語</a></strong> | <strong><a href="./docs/README_zh.md">🇨🇳中文</a></strong> | <strong>🇬🇧English</strong></div>
|
||||
|
||||
# vphone-cli
|
||||
|
||||
Boot a virtual iPhone (iOS 26) via Apple's Virtualization.framework using PCC research VM infrastructure.
|
||||
|
||||

|
||||

|
||||
|
||||
## Tested Environments
|
||||
|
||||
@@ -14,6 +14,18 @@ Boot a virtual iPhone (iOS 26) via Apple's Virtualization.framework using PCC re
|
||||
| Mac16,12 26.3 | `17,3_26.3_23D127` | `26.1-23B85` |
|
||||
| Mac16,12 26.3 | `17,3_26.3_23D127` | `26.3-23D128` |
|
||||
|
||||
## Firmware Variants
|
||||
|
||||
Three patch variants are available with increasing levels of security bypass:
|
||||
|
||||
| Variant | Boot Chain | CFW | Make Targets |
|
||||
| ------------------- | :--------: | :-------: | ---------------------------------- |
|
||||
| **Regular** | 38 patches | 10 phases | `fw_patch` + `cfw_install` |
|
||||
| **Development** | 47 patches | 12 phases | `fw_patch_dev` + `cfw_install_dev` |
|
||||
| **Jailbreak (WIP)** | 84 patches | 14 phases | `fw_patch_jb` + `cfw_install_jb` |
|
||||
|
||||
See [research/patch_comparison_all_variants.md](./research/patch_comparison_all_variants.md) for the detailed per-component breakdown.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
**Host OS:** macOS 15+ (Sequoia) is required for PV=3 virtualization.
|
||||
@@ -38,35 +50,31 @@ Restart once more.
|
||||
**Install dependencies:**
|
||||
|
||||
```bash
|
||||
brew install ideviceinstaller wget gnu-tar openssl@3 ldid-procursus sshpass keystone autoconf automake pkg-config libtool git-lfs
|
||||
brew install ideviceinstaller wget gnu-tar openssl@3 ldid-procursus sshpass keystone autoconf automake pkg-config libtool
|
||||
```
|
||||
|
||||
**Git LFS** — this repo uses Git LFS for large resource archives. Install and pull before building:
|
||||
**Submodules** — this repo uses a git submodule for resource archives. Clone with:
|
||||
|
||||
```bash
|
||||
git lfs install
|
||||
git lfs pull
|
||||
git clone --recurse-submodules https://github.com/Lakr233/vphone-cli.git
|
||||
```
|
||||
|
||||
## First setup
|
||||
|
||||
```bash
|
||||
make setup_machine # full automation through "First Boot" (includes restore/ramdisk/CFW)
|
||||
|
||||
# equivalent manual steps:
|
||||
make setup_tools # install brew deps, build trustcache + libimobiledevice, create Python venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
`make setup_machine` still requires manual **Recovery-mode SIP/research-guest configuration** and an interactive VM console for the First Boot commands it prints. The script does not validate those security settings.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
make setup_machine # full automation through "First Boot" (includes restore/ramdisk/CFW)
|
||||
```
|
||||
|
||||
## Manual Setup
|
||||
|
||||
```bash
|
||||
make setup_tools # install brew deps, build trustcache + libimobiledevice, create Python venv
|
||||
make build # build + sign vphone-cli
|
||||
make vm_new # create vm/ directory (ROMs, disk, SEP storage)
|
||||
make fw_prepare # download IPSWs, extract, merge, generate manifest
|
||||
make fw_patch # patch boot chain (6 components, 41+ modifications)
|
||||
make fw_patch # patch boot chain (regular variant)
|
||||
# or: make fw_patch_dev # dev variant (+ TXM entitlement/debug bypasses)
|
||||
# or: make fw_patch_jb # jailbreak variant (+ full security bypass) (WIP)
|
||||
```
|
||||
|
||||
## Restore
|
||||
@@ -84,7 +92,7 @@ make restore_get_shsh # fetch SHSH blob
|
||||
make restore # flash firmware via idevicerestore
|
||||
```
|
||||
|
||||
## Ramdisk and CFW
|
||||
## Install Custom Firmware
|
||||
|
||||
Stop the DFU boot in terminal 1 (Ctrl+C), then boot into DFU again for the ramdisk:
|
||||
|
||||
@@ -157,24 +165,6 @@ Connect via:
|
||||
- **VNC:** `vnc://127.0.0.1:5901`
|
||||
- [**RPC:**](http://github.com/doronz88/rpc-project) `rpcclient -p 5910 127.0.0.1`
|
||||
|
||||
## All Make Targets
|
||||
|
||||
Run `make help` for the full list. Key targets:
|
||||
|
||||
| Target | Description |
|
||||
| ------------------- | ---------------------------- |
|
||||
| `build` | Build + sign vphone-cli |
|
||||
| `vm_new` | Create VM directory |
|
||||
| `fw_prepare` | Download/merge IPSWs |
|
||||
| `fw_patch` | Patch boot chain |
|
||||
| `boot` / `boot_dfu` | Boot VM (GUI / DFU headless) |
|
||||
| `restore_get_shsh` | Fetch SHSH blob |
|
||||
| `restore` | Flash firmware |
|
||||
| `ramdisk_build` | Build SSH ramdisk |
|
||||
| `ramdisk_send` | Send ramdisk to device |
|
||||
| `cfw_install` | Install CFW mods |
|
||||
| `clean` | Remove build artifacts |
|
||||
|
||||
## FAQ
|
||||
|
||||
> **Before anything else — run `git pull` to make sure you have the latest version.**
|
||||
@@ -187,6 +177,10 @@ AMFI is not disabled. Set the boot-arg and restart:
|
||||
sudo nvram boot-args="amfi_get_out_of_my_way=1 -v"
|
||||
```
|
||||
|
||||
**Q: System apps (App Store, Messages, etc.) won't download or install.**
|
||||
|
||||
During iOS setup, do **not** select **Japan** or **European Union** as your region. These regions enforce additional regulatory checks (e.g., sideloading disclosures, camera shutter requirements) that the virtual machine cannot satisfy, which prevents system apps from being downloaded and installed. Choose any other region (e.g., United States) to avoid this issue.
|
||||
|
||||
**Q: I'm stuck on the "Press home to continue" screen.**
|
||||
|
||||
Connect via VNC (`vnc://127.0.0.1:5901`) and right-click anywhere on the screen (two-finger click on a Mac trackpad). This simulates the home button press.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div align="right"><strong><a href="./README_ko.md">🇰🇷한국어</a></strong> | <strong>🇯🇵日本語</strong> | <strong><a href="./README_zh.md">🇨🇳中文</a></strong> | <strong><a href="./README.md">🇬🇧English</a></strong></div>
|
||||
<div align="right"><strong><a href="./README_ko.md">🇰🇷한국어</a></strong> | <strong>🇯🇵日本語</strong> | <strong><a href="./README_zh.md">🇨🇳中文</a></strong> | <strong><a href="../README.md">🇬🇧English</a></strong></div>
|
||||
|
||||
# vphone-cli
|
||||
|
||||
@@ -8,12 +8,24 @@ Apple の Virtualization.framework と PCC の研究用 VM インフラを使用
|
||||
|
||||
## 検証済み環境
|
||||
|
||||
| ホスト | iPhone | CloudOS |
|
||||
| ホスト | iPhone | CloudOS |
|
||||
| ------------- | ------------------ | ------------- |
|
||||
| Mac16,12 26.3 | `17,3_26.1_23B85` | `26.1-23B85` |
|
||||
| Mac16,12 26.3 | `17,3_26.3_23D127` | `26.1-23B85` |
|
||||
| Mac16,12 26.3 | `17,3_26.3_23D127` | `26.3-23D128` |
|
||||
|
||||
## ファームウェアバリアント
|
||||
|
||||
セキュリティバイパスのレベルが異なる3つのパッチバリアントが利用可能です:
|
||||
|
||||
| バリアント | ブートチェーン | CFW | Make ターゲット |
|
||||
| -------------------- | :----------: | :-------: | ---------------------------------- |
|
||||
| **通常版** | 38 パッチ | 10 フェーズ | `fw_patch` + `cfw_install` |
|
||||
| **開発版** | 47 パッチ | 12 フェーズ | `fw_patch_dev` + `cfw_install_dev` |
|
||||
| **脱獄版(WIP)** | 84 パッチ | 14 フェーズ | `fw_patch_jb` + `cfw_install_jb` |
|
||||
|
||||
詳細なコンポーネントごとの内訳については [research/patch_comparison_all_variants.md](../research/patch_comparison_all_variants.md) を参照してください。
|
||||
|
||||
## 前提条件
|
||||
|
||||
**ホストOS:** PV=3 仮想化には macOS 15+(Sequoia)が必要です。
|
||||
@@ -38,35 +50,31 @@ sudo nvram boot-args="amfi_get_out_of_my_way=1 -v"
|
||||
**依存関係のインストール:**
|
||||
|
||||
```bash
|
||||
brew install ideviceinstaller wget gnu-tar openssl@3 ldid-procursus sshpass keystone autoconf automake pkg-config libtool git-lfs
|
||||
brew install ideviceinstaller wget gnu-tar openssl@3 ldid-procursus sshpass keystone autoconf automake pkg-config libtool
|
||||
```
|
||||
|
||||
**Git LFS** — このリポジトリは大きなリソースアーカイブに Git LFS を使用しています。ビルド前にインストールと pull を行ってください:
|
||||
**Submodules** — このリポジトリはリソースアーカイブに git submodule を使用しています。クローン時に以下を使用してください:
|
||||
|
||||
```bash
|
||||
git lfs install
|
||||
git lfs pull
|
||||
git clone --recurse-submodules https://github.com/Lakr233/vphone-cli.git
|
||||
```
|
||||
|
||||
## 初回セットアップ
|
||||
|
||||
```bash
|
||||
make setup_machine # 初回起動までを完全自動化(復元/ラムディスク/CFWを含む)
|
||||
|
||||
# 手動で行う場合:
|
||||
make setup_tools # brew の依存関係インストール、trustcache + libimobiledevice のビルド、Python venv の作成
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
`make setup_machine` は、依然として手動での **リカバリーモードの SIP/research-guest 設定** と、出力される初回起動コマンドを実行するためのインタラクティブなVMコンソールが必要です。スクリプトはこれらのセキュリティ設定を検証しません。
|
||||
|
||||
## クイックスタート
|
||||
|
||||
```bash
|
||||
make build # vphone-cliのビルド + 署名
|
||||
make setup_machine # 初回起動までを完全自動化(復元/ラムディスク/CFWを含む)
|
||||
```
|
||||
|
||||
## 手動セットアップ
|
||||
|
||||
```bash
|
||||
make setup_tools # brew の依存関係インストール、trustcache + libimobiledevice のビルド、Python venv の作成
|
||||
make build # vphone-cli のビルド + 署名
|
||||
make vm_new # vm/ ディレクトリの作成(ROM、ディスク、SEP ストレージ)
|
||||
make fw_prepare # IPSW のダウンロード、抽出、マージ、マニフェスト生成
|
||||
make fw_patch # ブートチェーンのパッチ当て(6コンポーネント、41箇所以上の変更)
|
||||
make fw_patch # ブートチェーンのパッチ当て(通常バリアント)
|
||||
# または: make fw_patch_dev # 開発バリアント(+ TXM entitlement/デバッグバイパス)
|
||||
# または: make fw_patch_jb # 脱獄バリアント(+ 完全セキュリティバイパス)(WIP)
|
||||
```
|
||||
|
||||
## 復元
|
||||
@@ -84,7 +92,7 @@ make restore_get_shsh # SHSH blob の取得
|
||||
make restore # idevicerestore 経由でファームウェアを焼き込み
|
||||
```
|
||||
|
||||
## Ramdisk と CFW
|
||||
## カスタムファームウェアのインストール
|
||||
|
||||
ターミナル 1 の DFU 起動を停止し(Ctrl+C)、Ramdisk 用に再び DFU で起動します:
|
||||
|
||||
@@ -157,24 +165,6 @@ iproxy 5910 5910 # RPC
|
||||
- **VNC:** `vnc://127.0.0.1:5901`
|
||||
- [**RPC:**](http://github.com/doronz88/rpc-project) `rpcclient -p 5910 127.0.0.1`
|
||||
|
||||
## Makefile の全ターゲット
|
||||
|
||||
完全なリストは `make help` を実行してください。主なターゲット:
|
||||
|
||||
| ターゲット | 説明 |
|
||||
| ------------------- | ---------------------------- |
|
||||
| `build` | vphone-cli のビルド + 署名 |
|
||||
| `vm_new` | VM ディレクトリの作成 |
|
||||
| `fw_prepare` | IPSW のダウンロード/マージ |
|
||||
| `fw_patch` | ブートチェーンのパッチ当て |
|
||||
| `boot` / `boot_dfu` | VMの起動 (GUI / DFU ヘッドレス) |
|
||||
| `restore_get_shsh` | SHSH blobの取得 |
|
||||
| `restore` | ファームウェアのフラッシュ |
|
||||
| `ramdisk_build` | SSH Ramdisk のビルド |
|
||||
| `ramdisk_send` | Ramdisk の送信 |
|
||||
| `cfw_install` | CFW のインストール |
|
||||
| `clean` | ビルドアーティファクトの削除 |
|
||||
|
||||
## よくある質問 (FAQ)
|
||||
|
||||
> **何よりもまず — `git pull` を実行して最新バージョンであることを確認してください**
|
||||
@@ -187,6 +177,10 @@ AMFIが無効化されていません。boot-arg を設定して再起動して
|
||||
sudo nvram boot-args="amfi_get_out_of_my_way=1 -v"
|
||||
```
|
||||
|
||||
**Q: システムアプリ(App Store、メッセージなど)がダウンロード・インストールできません**
|
||||
|
||||
iOS の初期設定時に、地域として**日本**または**欧州連合**を選択**しないでください**。これらの地域では追加の規制チェック(サイドローディングの開示、カメラのシャッター音など)が適用されますが、仮想マシンではこれらの要件を満たせないため、システムアプリのダウンロードおよびインストールができなくなります。この問題を回避するには、他の地域(例: 米国)を選択してください。
|
||||
|
||||
**Q: "Press home to continue" の画面から進めません**
|
||||
|
||||
VNC経由で接続し(`vnc://127.0.0.1:5901`)、画面の任意の場所を右クリック(Mac のトラックパッドでは 2 本指クリック)してください。これによりホームボタンの押下がシミュレートされます。
|
||||
@@ -1,4 +1,4 @@
|
||||
<div align="right"><strong>🇰🇷한국어</strong> | <strong><a href="./README_ja.md">🇯🇵日本語</a></strong> | <strong><a href="./README_zh.md">🇨🇳中文</a></strong> | <strong><a href="./README.md">🇬🇧English</a></strong></div>
|
||||
<div align="right"><strong>🇰🇷한국어</strong> | <strong><a href="./README_ja.md">🇯🇵日本語</a></strong> | <strong><a href="./README_zh.md">🇨🇳中文</a></strong> | <strong><a href="../README.md">🇬🇧English</a></strong></div>
|
||||
|
||||
# vphone-cli
|
||||
|
||||
@@ -14,6 +14,18 @@ PCC 리서치 VM 인프라와 Apple의 Virtualization.framework를 사용하여
|
||||
| Mac16,12 26.3 | `17,3_26.3_23D127` | `26.1-23B85` |
|
||||
| Mac16,12 26.3 | `17,3_26.3_23D127` | `26.3-23D128` |
|
||||
|
||||
## 펌웨어 변형
|
||||
|
||||
보안 우회 수준이 다른 3가지 패치 변형을 사용할 수 있습니다:
|
||||
|
||||
| 변형 | 부트 체인 | CFW | Make 타겟 |
|
||||
| ----------------- | :--------: | :-------: | ---------------------------------- |
|
||||
| **일반** | 38 패치 | 10 페이즈 | `fw_patch` + `cfw_install` |
|
||||
| **개발** | 47 패치 | 12 페이즈 | `fw_patch_dev` + `cfw_install_dev` |
|
||||
| **탈옥 (WIP)** | 84 패치 | 14 페이즈 | `fw_patch_jb` + `cfw_install_jb` |
|
||||
|
||||
컴포넌트별 상세 분류는 [research/patch_comparison_all_variants.md](../research/patch_comparison_all_variants.md)를 참조하세요.
|
||||
|
||||
## 사전 요구 사항
|
||||
|
||||
**호스트 OS:** PV=3 가상화를 위해 macOS 15+(Sequoia)가 필요합니다.
|
||||
@@ -38,35 +50,31 @@ sudo nvram boot-args="amfi_get_out_of_my_way=1 -v"
|
||||
**의존성(Dependencies) 설치:**
|
||||
|
||||
```bash
|
||||
brew install ideviceinstaller wget gnu-tar openssl@3 ldid-procursus sshpass keystone autoconf automake pkg-config libtool git-lfs
|
||||
brew install ideviceinstaller wget gnu-tar openssl@3 ldid-procursus sshpass keystone autoconf automake pkg-config libtool
|
||||
```
|
||||
|
||||
**Git LFS** — 이 저장소는 대용량 리소스 아카이브를 위해 Git LFS를 사용합니다. 빌드하기 전에 설치 및 pull을 진행하세요:
|
||||
**Submodules** — 이 저장소는 리소스 아카이브를 위해 git submodule을 사용합니다. 클론 시 다음 명령어를 사용하세요:
|
||||
|
||||
```bash
|
||||
git lfs install
|
||||
git lfs pull
|
||||
git clone --recurse-submodules https://github.com/Lakr233/vphone-cli.git
|
||||
```
|
||||
|
||||
## 초기 설정
|
||||
|
||||
```bash
|
||||
make setup_machine # "First Boot"까지의 전체 과정 자동화 (복원/Ramdisk/커스텀 펌웨어 포함)
|
||||
|
||||
# 수동 단계(위 명령과 동일):
|
||||
make setup_tools # brew 의존성 설치, trustcache + libimobiledevice 빌드, Python venv 생성
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
`make setup_machine`을 사용하더라도 **복구 모드에서의 SIP/research-guest 설정**과 "First Boot" 명령어를 입력하기 위한 대화형 VM 콘솔 작업은 여전히 수동으로 필요합니다. 이 스크립트는 보안 설정 여부를 별도로 검증하지 않습니다.
|
||||
|
||||
## 빠른 시작
|
||||
|
||||
```bash
|
||||
make setup_machine # "First Boot"까지의 전체 과정 자동화 (복원/Ramdisk/커스텀 펌웨어 포함)
|
||||
```
|
||||
|
||||
## 수동 설정
|
||||
|
||||
```bash
|
||||
make setup_tools # brew 의존성 설치, trustcache + libimobiledevice 빌드, Python venv 생성
|
||||
make build # vphone-cli 빌드 및 서명
|
||||
make vm_new # vm/ 디렉토리 생성 (ROM, 디스크, SEP 저장소)
|
||||
make fw_prepare # IPSW 다운로드, 추출, 병합, manifest 생성
|
||||
make fw_patch # 부트 체인 패치 (6개 구성 요소, 41개 이상의 수정 사항)
|
||||
make fw_patch # 부트 체인 패치 (일반 변형)
|
||||
# 또는: make fw_patch_dev # 개발 변형 (+ TXM 권한/디버그 우회)
|
||||
# 또는: make fw_patch_jb # 탈옥 변형 (+ 전체 보안 우회) (WIP)
|
||||
```
|
||||
|
||||
## 복원
|
||||
@@ -84,7 +92,7 @@ make restore_get_shsh # SHSH blob 가져오기
|
||||
make restore # idevicerestore를 통해 펌웨어 플래싱
|
||||
```
|
||||
|
||||
## 램디스크 및 커스텀 펌웨어
|
||||
## 커스텀 펌웨어 설치
|
||||
|
||||
터미널 1의 DFU 부팅을 중단(Ctrl+C)한 다음, 램디스크를 위해 다시 DFU로 부팅합니다:
|
||||
|
||||
@@ -128,7 +136,7 @@ mkdir -p /var/dropbear
|
||||
cp /iosbinpack64/etc/profile /var/profile
|
||||
cp /iosbinpack64/etc/motd /var/motd
|
||||
|
||||
# generate SSH host keys (required for SSH to work)
|
||||
# SSH 호스트 키 생성 (SSH 작동에 필수)
|
||||
dropbearkey -t rsa -f /var/dropbear/dropbear_rsa_host_key
|
||||
dropbearkey -t ecdsa -f /var/dropbear/dropbear_ecdsa_host_key
|
||||
|
||||
@@ -157,24 +165,6 @@ iproxy 5910 5910 # RPC
|
||||
- **VNC:** `vnc://127.0.0.1:5901`
|
||||
- [**RPC:**](http://github.com/doronz88/rpc-project) `rpcclient -p 5910 127.0.0.1`
|
||||
|
||||
## 전체 Make 타겟
|
||||
|
||||
전체 목록을 보려면 `make help`를 실행하세요. 주요 타겟은 다음과 같습니다:
|
||||
|
||||
| 타겟 | 설명 |
|
||||
| ------------------- | ---------------------------- |
|
||||
| `build` | vphone-cli 빌드 및 서명 |
|
||||
| `vm_new` | VM 디렉토리 생성 |
|
||||
| `fw_prepare` | IPSW 다운로드 및 병합 |
|
||||
| `fw_patch` | 부트 체인 패치 |
|
||||
| `boot` / `boot_dfu` | VM 부팅 (GUI / DFU headless) |
|
||||
| `restore_get_shsh` | SHSH blob 가져오기 |
|
||||
| `restore` | 펌웨어 플래싱 |
|
||||
| `ramdisk_build` | SSH 램디스크 빌드 |
|
||||
| `ramdisk_send` | 장치에 램디스크 전송 |
|
||||
| `cfw_install` | CFW mods 설치 |
|
||||
| `clean` | 빌드 아티팩트 제거 |
|
||||
|
||||
## FAQ
|
||||
|
||||
> **무엇보다 먼저 — `git pull`을 실행하여 최신 버전인지 확인하세요.**
|
||||
@@ -187,6 +177,10 @@ AMFI가 비활성화되지 않았습니다. boot-arg를 설정하고 재시작
|
||||
sudo nvram boot-args="amfi_get_out_of_my_way=1 -v"
|
||||
```
|
||||
|
||||
**Q: 시스템 앱(App Store, 메시지 등)을 다운로드하거나 설치할 수 없습니다.**
|
||||
|
||||
iOS 초기 설정 시 지역을 **일본** 또는 **유럽 연합**으로 선택하지 **마세요**. 이 지역에서는 추가적인 규제 검사(사이드로딩 공개, 카메라 셔터음 등)가 적용되는데, 가상 머신은 이러한 요건을 충족할 수 없어 시스템 앱의 다운로드 및 설치가 불가능합니다. 이 문제를 피하려면 다른 지역(예: 미국)을 선택하세요.
|
||||
|
||||
**Q: "Press home to continue" 화면에서 멈췄습니다.**
|
||||
|
||||
VNC(`vnc://127.0.0.1:5901`)로 접속하여 화면의 아무 곳이나 우클릭(Mac 트랙패드에서는 두 손가락 클릭)하세요. 이것이 홈 버튼 누르기를 시뮬레이션합니다.
|
||||
@@ -1,4 +1,4 @@
|
||||
<div align="right"><strong><a href="./README_ko.md">🇰🇷한국어</a></strong> | <strong><a href="./README_ja.md">🇯🇵日本語</a></strong> | <strong>🇨🇳中文</strong> | <strong><a href="./README.md">🇬🇧English</a></strong></div>
|
||||
<div align="right"><strong><a href="./README_ko.md">🇰🇷한국어</a></strong> | <strong><a href="./README_ja.md">🇯🇵日本語</a></strong> | <strong>🇨🇳中文</strong> | <strong><a href="../README.md">🇬🇧English</a></strong></div>
|
||||
|
||||
# vphone-cli
|
||||
|
||||
@@ -14,6 +14,18 @@
|
||||
| Mac16,12 26.3 | `17,3_26.3_23D127` | `26.1-23B85` |
|
||||
| Mac16,12 26.3 | `17,3_26.3_23D127` | `26.3-23D128` |
|
||||
|
||||
## 固件变体
|
||||
|
||||
提供三种补丁变体,安全绕过级别逐步递增:
|
||||
|
||||
| 变体 | 启动链 | 自定义固件 | Make 目标 |
|
||||
| ----------------- | :--------: | :-------: | ---------------------------------- |
|
||||
| **常规版** | 38 个补丁 | 10 个阶段 | `fw_patch` + `cfw_install` |
|
||||
| **开发版** | 47 个补丁 | 12 个阶段 | `fw_patch_dev` + `cfw_install_dev` |
|
||||
| **越狱版(WIP)** | 84 个补丁 | 14 个阶段 | `fw_patch_jb` + `cfw_install_jb` |
|
||||
|
||||
详见 [research/patch_comparison_all_variants.md](../research/patch_comparison_all_variants.md) 了解各组件的详细分项对比。
|
||||
|
||||
## 先决条件
|
||||
|
||||
**主机系统:** PV=3 虚拟化要求 macOS 15+(Sequoia)。
|
||||
@@ -38,35 +50,31 @@ sudo nvram boot-args="amfi_get_out_of_my_way=1 -v"
|
||||
**安装依赖:**
|
||||
|
||||
```bash
|
||||
brew install ideviceinstaller wget gnu-tar openssl@3 ldid-procursus sshpass keystone autoconf automake pkg-config libtool git-lfs
|
||||
brew install ideviceinstaller wget gnu-tar openssl@3 ldid-procursus sshpass keystone autoconf automake pkg-config libtool
|
||||
```
|
||||
|
||||
**Git LFS** —— 本仓库使用 Git LFS 存储大型资源文件。构建前请先安装并拉取:
|
||||
**Submodules** —— 本仓库使用 git submodule 存储资源文件。克隆时请使用:
|
||||
|
||||
```bash
|
||||
git lfs install
|
||||
git lfs pull
|
||||
git clone --recurse-submodules https://github.com/Lakr233/vphone-cli.git
|
||||
```
|
||||
|
||||
## 第一次设置
|
||||
|
||||
```bash
|
||||
make setup_machine # 完全自动化完成“首次启动”流程(包含 restore/ramdisk/CFW)
|
||||
|
||||
# 等价的手动步骤:
|
||||
make setup_tools # 安装 brew 依赖、构建 trustcache + libimobiledevice、创建 Python 虚拟环境
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
`make setup_machine` 仍然要求手动在恢复模式下配置 SIP/research-guest,并在其打印的首次启动命令中使用交互式 VM 控制台。脚本不会验证这些安全设置。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
make setup_machine # 完全自动化完成"首次启动"流程(包含 restore/ramdisk/CFW)
|
||||
```
|
||||
|
||||
## 手动设置
|
||||
|
||||
```bash
|
||||
make setup_tools # 安装 brew 依赖、构建 trustcache + libimobiledevice、创建 Python 虚拟环境
|
||||
make build # 构建并签名 vphone-cli
|
||||
make vm_new # 创建 vm/ 目录(ROM、磁盘、SEP 存储)
|
||||
make fw_prepare # 下载 IPSWs,提取、合并、生成 manifest
|
||||
make fw_patch # 修补启动链(6 个组件,41+ 处修改)
|
||||
make fw_patch # 修补启动链(常规变体)
|
||||
# 或:make fw_patch_dev # 开发变体(+ TXM 权限/调试绕过)
|
||||
# 或:make fw_patch_jb # 越狱变体(+ 完整安全绕过)(WIP)
|
||||
```
|
||||
|
||||
## 恢复过程
|
||||
@@ -84,7 +92,7 @@ make restore_get_shsh # 获取 SHSH blob
|
||||
make restore # 通过 idevicerestore 刷写固件
|
||||
```
|
||||
|
||||
## Ramdisk 与 CFW
|
||||
## 安装自定义固件
|
||||
|
||||
在终端 1 中停止 DFU 引导(Ctrl+C),然后再次进入 DFU,用于 ramdisk:
|
||||
|
||||
@@ -148,30 +156,14 @@ make boot
|
||||
```bash
|
||||
iproxy 22222 22222 # SSH
|
||||
iproxy 5901 5901 # VNC
|
||||
iproxy 5910 5910 # RPC
|
||||
```
|
||||
|
||||
连接方式:
|
||||
|
||||
- **SSH:** `ssh -p 22222 root@127.0.0.1`(密码:`alpine`)
|
||||
- **VNC:** `vnc://127.0.0.1:5901`
|
||||
|
||||
## 所有 Make 目标
|
||||
|
||||
运行 `make help` 获取完整列表。关键目标:
|
||||
|
||||
| 目标 | 描述 |
|
||||
| ------------------- | ------------------------- |
|
||||
| `build` | 构建并签名 vphone-cli |
|
||||
| `vm_new` | 创建 VM 目录 |
|
||||
| `fw_prepare` | 下载/合并 IPSWs |
|
||||
| `fw_patch` | 修补启动链 |
|
||||
| `boot` / `boot_dfu` | 启动 VM(GUI / 无头 DFU) |
|
||||
| `restore_get_shsh` | 获取 SHSH blob |
|
||||
| `restore` | 刷写固件 |
|
||||
| `ramdisk_build` | 构建 SSH ramdisk |
|
||||
| `ramdisk_send` | 发送 ramdisk 到设备 |
|
||||
| `cfw_install` | 安装 CFW 修改 |
|
||||
| `clean` | 删除构建产物 |
|
||||
- [**RPC:**](http://github.com/doronz88/rpc-project) `rpcclient -p 5910 127.0.0.1`
|
||||
|
||||
## 常见问题(FAQ)
|
||||
|
||||
@@ -185,7 +177,11 @@ AMFI 未禁用。设置 boot-arg 并重启:
|
||||
sudo nvram boot-args="amfi_get_out_of_my_way=1 -v"
|
||||
```
|
||||
|
||||
**问:卡在“Press home to continue”屏幕。**
|
||||
**问:系统应用(App Store、信息等)无法下载或安装。**
|
||||
|
||||
在 iOS 初始设置过程中,请**不要**选择**日本**或**欧盟地区**作为你的国家/地区。这些地区要求额外的合规检查(如侧载披露、相机快门声等),虚拟机无法满足这些要求,因此系统应用无法正常下载安装。请选择其他地区(例如美国)以避免此问题。
|
||||
|
||||
**问:卡在"Press home to continue"屏幕。**
|
||||
|
||||
通过 VNC (`vnc://127.0.0.1:5901`) 连接,并在屏幕上右键单击任意位置(在 Mac 触控板上双指点击)。这会模拟 Home 按钮按下。
|
||||
|
||||
@@ -35,11 +35,11 @@ NSDictionary with a single key:
|
||||
|
||||
### Actions
|
||||
|
||||
| Action | Value | Behavior |
|
||||
|--------|-------|---------|
|
||||
| `kAMFIActionArm` | 0 | Arm developer mode — takes effect on next reboot, user must select "Turn On" |
|
||||
| `kAMFIActionDisable` | 1 | Disable developer mode immediately |
|
||||
| `kAMFIActionStatus` | 2 | Query current state |
|
||||
| Action | Value | Behavior |
|
||||
| -------------------- | ----- | ---------------------------------------------------------------------------- |
|
||||
| `kAMFIActionArm` | 0 | Arm developer mode — takes effect on next reboot, user must select "Turn On" |
|
||||
| `kAMFIActionDisable` | 1 | Disable developer mode immediately |
|
||||
| `kAMFIActionStatus` | 2 | Query current state |
|
||||
|
||||
### Response
|
||||
|
||||
@@ -52,12 +52,12 @@ NSDictionary *dict = _CFXPCCreateCFObjectFromXPCMessage(cfReply);
|
||||
|
||||
Response fields:
|
||||
|
||||
| Key | Type | Description |
|
||||
|-----|------|-------------|
|
||||
| `success` | BOOL | Whether the XPC call succeeded |
|
||||
| `status` | BOOL | Current developer mode state (for Status action) |
|
||||
| `armed` | BOOL | Whether armed for reboot (for Arm action) |
|
||||
| `error` | NSString | Error description if success is false |
|
||||
| Key | Type | Description |
|
||||
| --------- | -------- | ------------------------------------------------ |
|
||||
| `success` | BOOL | Whether the XPC call succeeded |
|
||||
| `status` | BOOL | Current developer mode state (for Status action) |
|
||||
| `armed` | BOOL | Whether armed for reboot (for Arm action) |
|
||||
| `error` | NSString | Error description if success is false |
|
||||
|
||||
## Arming Flow
|
||||
|
||||
@@ -76,6 +76,7 @@ Source: `references/TrollStore/RootHelper/devmode.m`
|
||||
TrollStore separates privileges: the main app has no AMFI entitlement; all privileged operations go through RootHelper which has `com.apple.private.amfi.developer-mode-control`.
|
||||
|
||||
Key functions:
|
||||
|
||||
- `checkDeveloperMode()` — returns current state, YES on iOS <16 (devmode doesn't exist)
|
||||
- `armDeveloperMode(BOOL *alreadyEnabled)` — check + arm in one call
|
||||
- `startConnection()` — creates and resumes XPC connection to `com.apple.amfi.xpc`
|
||||
@@ -88,12 +89,14 @@ Added as `devmode` capability in vphoned guest agent:
|
||||
### Protocol Messages
|
||||
|
||||
**Status query:**
|
||||
|
||||
```json
|
||||
{"t": "devmode", "action": "status"}
|
||||
→ {"t": "ok", "enabled": true}
|
||||
```
|
||||
|
||||
**Enable (arm):**
|
||||
|
||||
```json
|
||||
{"t": "devmode", "action": "enable"}
|
||||
→ {"t": "ok", "already_enabled": false, "msg": "developer mode armed, reboot to activate"}
|
||||
@@ -102,6 +105,7 @@ Added as `devmode` capability in vphoned guest agent:
|
||||
### Entitlements
|
||||
|
||||
Added to `scripts/vphoned/entitlements.plist`:
|
||||
|
||||
```xml
|
||||
<key>com.apple.private.amfi.developer-mode-control</key>
|
||||
<true/>
|
||||
@@ -110,8 +114,8 @@ Added to `scripts/vphoned/entitlements.plist`:
|
||||
### Host-Side API (VPhoneControl.swift)
|
||||
|
||||
```swift
|
||||
control.sendDevModeStatus() // query current state
|
||||
control.sendDevModeEnable() // arm developer mode
|
||||
let status = try await control.sendDevModeStatus() // -> DevModeStatus (enabled: Bool)
|
||||
let result = try await control.sendDevModeEnable() // -> DevModeEnableResult (alreadyEnabled: Bool, message: String)
|
||||
```
|
||||
|
||||
Responses arrive via the existing read loop and are logged to console.
|
||||
Both methods use `sendRequest()` with pending request tracking — the response is returned via `async throws`, not logged to console. Callers (e.g. `VPhoneMenuConnect`) display results as `NSAlert` sheets on the VM window.
|
||||
320
research/firmware_manifest_and_origins.md
Normal file
320
research/firmware_manifest_and_origins.md
Normal file
@@ -0,0 +1,320 @@
|
||||
# Firmware Manifest & Component Origins
|
||||
|
||||
The erase install firmware is a **hybrid** of three source sets:
|
||||
|
||||
1. **PCC vresearch101ap** — boot chain (LLB/iBSS/iBEC/iBoot) and security monitors (SPTM/TXM)
|
||||
2. **PCC vphone600ap** — runtime components (DeviceTree, SEP, KernelCache, RecoveryMode)
|
||||
3. **iPhone 17,3** — OS image, trust caches, filesystem
|
||||
|
||||
The VM hardware identifies as **vresearch101ap** (BDID 0x90) in DFU mode, so the
|
||||
BuildManifest identity must use vresearch101ap fields for TSS/SHSH signing. However,
|
||||
runtime components use the **vphone600** variant because its DeviceTree sets MKB `dt=1`
|
||||
(allows boot without system keybag), its SEP firmware matches the vphone600 device tree,
|
||||
and `hardware target` reports as `vphone600ap` for proper iPhone emulation.
|
||||
|
||||
`fw_prepare.sh` downloads both IPSWs, merges cloudOS firmware into the iPhone
|
||||
restore directory, then `fw_manifest.py` generates the hybrid BuildManifest.
|
||||
|
||||
---
|
||||
|
||||
## 1. Multi-Source IPSW Comparison
|
||||
|
||||
### Identity Count Overview
|
||||
|
||||
| Source | Identities | DeviceClasses |
|
||||
| -------------- | ---------- | ------------------------------------------------------- |
|
||||
| iPhone 26.1 | 5 | All d47ap |
|
||||
| iPhone 26.3 | 5 | All d47ap |
|
||||
| CloudOS 26.1 | 6 | j236cap, j475dap, vphone600ap (x2), vresearch101ap (x2) |
|
||||
| KnownWork 26.1 | 5 | All vresearch101ap |
|
||||
|
||||
### CloudOS 26.1 Identity Structure (6 identities)
|
||||
|
||||
| Index | DeviceClass | Variant | BuildStyle | Manifest Keys |
|
||||
| ----- | -------------- | --------------------------------------------------- | ---------------------- | ---------------------------- |
|
||||
| [0] | j236cap | Darwin Cloud Customer Erase Install (IPSW) | RELEASE build | 37 keys (server hardware) |
|
||||
| [1] | j475dap | Darwin Cloud Customer Erase Install (IPSW) | unknown (no path) | 0 keys (empty placeholder) |
|
||||
| [2] | vphone600ap | Darwin Cloud Customer Erase Install (IPSW) | RELEASE build | 29 keys (includes UI assets) |
|
||||
| [3] | vresearch101ap | Darwin Cloud Customer Erase Install (IPSW) | RELEASE build | 20 keys (no UI assets) |
|
||||
| [4] | vphone600ap | Research Darwin Cloud Customer Erase Install (IPSW) | RESEARCH_RELEASE build | 29 keys (research kernel) |
|
||||
| [5] | vresearch101ap | Research Darwin Cloud Customer Erase Install (IPSW) | RESEARCH_RELEASE build | 20 keys (research kernel) |
|
||||
|
||||
Key distinctions:
|
||||
|
||||
- CloudOS[2] vs [4] (vphone600ap): [2] uses RELEASE boot chain + release kernelcache; [4] uses RESEARCH_RELEASE + research kernelcache + txm.iphoneos.research.im4p
|
||||
- CloudOS[3] vs [5] (vresearch101ap): Same pattern — [3] is RELEASE, [5] is RESEARCH_RELEASE
|
||||
- **vphone600ap has components vresearch101ap lacks**: RecoveryMode, AppleLogo, Battery\*, RestoreLogo, SEP (vphone600 variant)
|
||||
- vresearch101ap has only 20 manifest keys (no UI assets, no RecoveryMode)
|
||||
|
||||
### vphone600ap vs vresearch101ap Key Differences
|
||||
|
||||
| Property | vphone600ap | vresearch101ap |
|
||||
| -------------- | ----------------------------------- | -------------------------------------- |
|
||||
| Ap,ProductType | iPhone99,11 | ComputeModule14,2 |
|
||||
| Ap,Target | VPHONE600AP | VRESEARCH101AP |
|
||||
| ApBoardID | 0x91 | 0x90 |
|
||||
| DeviceTree | DeviceTree.vphone600ap.im4p | DeviceTree.vresearch101ap.im4p |
|
||||
| SEP | sep-firmware.vphone600.RELEASE.im4p | sep-firmware.vresearch101.RELEASE.im4p |
|
||||
| RecoveryMode | recoverymode@2556~iphone-USBc.im4p | **NOT PRESENT** |
|
||||
| MKB dt flag | dt=1 (keybag-less boot OK) | dt=0 (fatal keybag error) |
|
||||
|
||||
---
|
||||
|
||||
## 2. Component Source Tracing
|
||||
|
||||
### Boot Chain (from PCC vresearch101ap)
|
||||
|
||||
| Component | Source Identity | File | Patches Applied |
|
||||
| ------------- | ----------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
||||
| **AVPBooter** | PCC vresearch1 | `AVPBooter*.bin` (vm dir) | DGST validation bypass (`mov x0, #0`) |
|
||||
| **iBSS** | PROD (vresearch101ap release) | `Firmware/dfu/iBSS.vresearch101.RELEASE.im4p` | Serial labels + image4 callback bypass |
|
||||
| **iBEC** | PROD (vresearch101ap release) | `Firmware/dfu/iBEC.vresearch101.RELEASE.im4p` | Serial labels + image4 callback + boot-args |
|
||||
| **LLB** | PROD (vresearch101ap release) | `Firmware/all_flash/LLB.vresearch101.RELEASE.im4p` | Serial labels + image4 callback + boot-args + rootfs + panic (6 patches) |
|
||||
| **iBoot** | RES (vresearch101ap research) | `Firmware/all_flash/iBoot.vresearch101.RESEARCH_RELEASE.im4p` | Not patched (only research identity carries iBoot) |
|
||||
|
||||
### Security Monitors (from PCC, shared across board configs)
|
||||
|
||||
| Component | Source Identity | File | Patches Applied |
|
||||
| ------------------------------------- | --------------- | --------------------------------------- | ------------------------------------------- |
|
||||
| **Ap,RestoreSecurePageTableMonitor** | PROD | `Firmware/sptm.vresearch1.release.im4p` | Not patched |
|
||||
| **Ap,RestoreTrustedExecutionMonitor** | PROD | `Firmware/txm.iphoneos.release.im4p` | Not patched |
|
||||
| **Ap,SecurePageTableMonitor** | PROD | `Firmware/sptm.vresearch1.release.im4p` | Not patched |
|
||||
| **Ap,TrustedExecutionMonitor** | RES (research) | `Firmware/txm.iphoneos.research.im4p` | Trustcache bypass (`mov x0, #0` at 0x2C1F8) |
|
||||
|
||||
### Runtime Components (from PCC vphone600ap)
|
||||
|
||||
| Component | Source Identity | File | Patches Applied |
|
||||
| ---------------------- | -------------------------- | -------------------------------------------------------- | -------------------------------------- |
|
||||
| **DeviceTree** | VP (vphone600ap release) | `Firmware/all_flash/DeviceTree.vphone600ap.im4p` | Not patched |
|
||||
| **RestoreDeviceTree** | VP | `Firmware/all_flash/DeviceTree.vphone600ap.im4p` | Not patched |
|
||||
| **SEP** | VP | `Firmware/all_flash/sep-firmware.vphone600.RELEASE.im4p` | Not patched |
|
||||
| **RestoreSEP** | VP | `Firmware/all_flash/sep-firmware.vphone600.RELEASE.im4p` | Not patched |
|
||||
| **KernelCache** | VPR (vphone600ap research) | `kernelcache.research.vphone600` | 25 dynamic patches via KernelPatcher |
|
||||
| **RestoreKernelCache** | VP (vphone600ap release) | `kernelcache.release.vphone600` | Not patched (used during restore only) |
|
||||
| **RecoveryMode** | VP | `Firmware/all_flash/recoverymode@2556~iphone-USBc.im4p` | Not patched |
|
||||
|
||||
> **Important**: KernelCache (installed to disk, patched) uses the **research** variant.
|
||||
> RestoreKernelCache (used during restore process only) uses the **release** variant.
|
||||
> Only vphone600ap identities carry RecoveryMode — vresearch101ap does not.
|
||||
|
||||
### OS / Filesystem (from iPhone)
|
||||
|
||||
| Component | Source | Notes |
|
||||
| ------------------------------------ | ---------------------------------- | ------------------ |
|
||||
| **OS** | iPhone `iPhone17,3` erase identity | iPhone OS image |
|
||||
| **SystemVolume** | iPhone erase | Root hash |
|
||||
| **StaticTrustCache** | iPhone erase | Static trust cache |
|
||||
| **Ap,SystemVolumeCanonicalMetadata** | iPhone erase | Metadata / mtree |
|
||||
|
||||
### Ramdisk (from PCC)
|
||||
|
||||
| Component | Source | Notes |
|
||||
| --------------------- | ----------------------------- | --------------------- |
|
||||
| **RestoreRamDisk** | PROD (vresearch101ap release) | CloudOS erase ramdisk |
|
||||
| **RestoreTrustCache** | PROD | Ramdisk trust cache |
|
||||
|
||||
---
|
||||
|
||||
## 3. Why the Hybrid Approach
|
||||
|
||||
### Why Not All-vresearch101?
|
||||
|
||||
The vresearch101ap device tree sets MKB `dt=0`, causing a **fatal keybag error** during boot:
|
||||
|
||||
```
|
||||
MKB_INIT: dt = 0, bootarg = 0
|
||||
MKB_INIT: FATAL KEYBAG ERROR: failed to load system bag
|
||||
REBOOTING INTO RECOVERY MODE.
|
||||
```
|
||||
|
||||
Also missing the RecoveryMode entry.
|
||||
|
||||
### Why Not All-vphone600?
|
||||
|
||||
The DFU hardware identifies as BDID 0x90 (vresearch101ap). Using vphone600ap identity
|
||||
(BDID 0x91) fails TSS/SHSH signing and idevicerestore identity matching
|
||||
(`Unable to find a matching build identity`).
|
||||
|
||||
### Solution
|
||||
|
||||
vresearch101ap identity fields for DFU/TSS + vphone600 runtime components for a working
|
||||
boot environment. The vphone600ap device tree sets `dt=1`, allowing boot without a
|
||||
pre-existing system keybag:
|
||||
|
||||
```
|
||||
MKB_INIT: dt = 1, bootarg = 0
|
||||
MKB_INIT: No system keybag loaded.
|
||||
```
|
||||
|
||||
The SEP firmware must match the device tree (vphone600 SEP with vphone600 DT).
|
||||
|
||||
---
|
||||
|
||||
## 4. Patched Components Summary
|
||||
|
||||
All 6 patched components in `fw_patch.py` come from **PCC (cloudOS)**:
|
||||
|
||||
| # | Component | Source Board | Patch Count | Purpose |
|
||||
| --- | ----------- | ----------------- | ----------- | ---------------------------------------------------------- |
|
||||
| 1 | AVPBooter | vresearch1 | 1 | Bypass DGST signature validation |
|
||||
| 2 | iBSS | vresearch101 | 2 | Enable serial output + bypass image4 verification |
|
||||
| 3 | iBEC | vresearch101 | 3 | Enable serial + bypass image4 + inject boot-args |
|
||||
| 4 | LLB | vresearch101 | 6 | Serial + image4 + boot-args + rootfs mount + panic handler |
|
||||
| 5 | TXM | shared (iphoneos) | 1 | Bypass trustcache validation |
|
||||
| 6 | KernelCache | vphone600 | 25 | APFS seal, MAC policy, debugger, launch constraints, etc. |
|
||||
|
||||
All 4 CFW-patched binaries in `patchers/cfw.py` / `cfw_install.sh` come from **iPhone**:
|
||||
|
||||
| # | Binary | Source | Purpose |
|
||||
| --- | -------------------- | ------------------------- | ----------------------------------------------------------- |
|
||||
| 1 | seputil | iPhone (Cryptex SystemOS) | Gigalocker UUID patch (`/%s.gl` → `/AA.gl`) |
|
||||
| 2 | launchd_cache_loader | iPhone (Cryptex SystemOS) | NOP cache validation check |
|
||||
| 3 | mobileactivationd | iPhone (Cryptex SystemOS) | Force `should_hactivate` to return true |
|
||||
| 4 | launchd.plist | iPhone (Cryptex SystemOS) | Inject bash/dropbear/trollvnc/vphoned/rpcserver_ios daemons |
|
||||
|
||||
---
|
||||
|
||||
## 5. idevicerestore Identity Selection
|
||||
|
||||
Source: `idevicerestore/src/idevicerestore.c` lines 2195-2242
|
||||
|
||||
### Matching Algorithm
|
||||
|
||||
idevicerestore selects a Build Identity by iterating through all `BuildIdentities` and returning the **first match** based on two fields:
|
||||
|
||||
1. **`Info.DeviceClass`** — case-insensitive match against device `hardware_model`
|
||||
2. **`Info.Variant`** — substring match against the requested variant string
|
||||
|
||||
For DFU erase restore, the search variant is `"Erase Install (IPSW)"` (defined in `idevicerestore.h`).
|
||||
|
||||
### Matching Modes
|
||||
|
||||
```c
|
||||
// Exact match
|
||||
if (strcmp(str, variant) == 0) return ident;
|
||||
|
||||
// Partial match (when exact=0)
|
||||
if (strstr(str, variant) && !strstr(str, "Research")) return ident;
|
||||
```
|
||||
|
||||
**Critical**: Partial matching **excludes** variants containing `"Research"`. This means:
|
||||
|
||||
- `"Darwin Cloud Customer Erase Install (IPSW)"` — matches (contains "Erase Install (IPSW)", no "Research")
|
||||
- `"Research Darwin Cloud Customer Erase Install (IPSW)"` — skipped (contains "Research")
|
||||
|
||||
### What idevicerestore Does NOT Check
|
||||
|
||||
- ApBoardID / ApChipID (used after selection, not for matching)
|
||||
- Identity index or count (no hardcoded indices)
|
||||
|
||||
### Conclusion for Single Identity
|
||||
|
||||
A BuildManifest with **one identity** works fine. The loop iterates once, and if
|
||||
DeviceClass and Variant match, it's returned. No minimum identity count required.
|
||||
|
||||
---
|
||||
|
||||
## 6. TSS/SHSH Signing
|
||||
|
||||
The TSS request sent to `gs.apple.com` includes:
|
||||
|
||||
- `ApBoardID = 144` (0x90) — must match vresearch101ap
|
||||
- `ApChipID = 65025` (0xFE01)
|
||||
- `Ap,ProductType = ComputeModule14,2`
|
||||
- `Ap,Target = VRESEARCH101AP`
|
||||
- Digests for all 21 manifest components
|
||||
|
||||
Apple's TSS server signs based on these identity fields + component digests.
|
||||
Using vphone600ap identity (BDID 0x91) would fail because the DFU device
|
||||
reports BDID 0x90.
|
||||
|
||||
---
|
||||
|
||||
## 7. Final Design: Single DFU Erase Identity
|
||||
|
||||
Since vphone-cli always boots via DFU restore, only one Build Identity is needed.
|
||||
|
||||
### Identity Metadata (fw_manifest.py)
|
||||
|
||||
```
|
||||
DeviceClass = vresearch101ap (from C[PROD] deep copy)
|
||||
Variant = Darwin Cloud Customer Erase Install (IPSW)
|
||||
Ap,ProductType = ComputeModule14,2
|
||||
Ap,Target = VRESEARCH101AP
|
||||
Ap,TargetType = vresearch101
|
||||
ApBoardID = 0x90
|
||||
ApChipID = 0xFE01
|
||||
ApSecurityDomain = 0x01
|
||||
FDRSupport = False
|
||||
```
|
||||
|
||||
### Source Variable Map
|
||||
|
||||
```
|
||||
PROD = C[vresearch101ap release] — boot chain, SPTM, RestoreTXM, ramdisk, RestoreTrustCache
|
||||
RES = C[vresearch101ap research] — iBoot, TXM research
|
||||
VP = C[vphone600ap release] — DeviceTree, RestoreDeviceTree, SEP, RestoreSEP, RestoreKernelCache, RecoveryMode
|
||||
VPR = C[vphone600ap research] — KernelCache (patched by fw_patch.py)
|
||||
I_ERASE = I[iPhone erase] — OS, trust caches, system volume
|
||||
```
|
||||
|
||||
### All 21 Manifest Entries
|
||||
|
||||
```
|
||||
Boot chain (PROD): LLB, iBSS, iBEC
|
||||
Research iBoot (RES): iBoot
|
||||
Security monitors (PROD): Ap,RestoreSPTM, Ap,RestoreTXM, Ap,SPTM
|
||||
Research TXM (RES): Ap,TXM
|
||||
Device tree (VP): DeviceTree, RestoreDeviceTree
|
||||
SEP (VP): SEP, RestoreSEP
|
||||
Kernel (VPR/VP): KernelCache (research), RestoreKernelCache (release)
|
||||
Recovery (VP): RecoveryMode
|
||||
Ramdisk (PROD): RestoreRamDisk, RestoreTrustCache
|
||||
iPhone OS (I_ERASE): OS, StaticTrustCache, SystemVolume, Ap,SVC Metadata
|
||||
```
|
||||
|
||||
### Full Manifest Component List
|
||||
|
||||
```
|
||||
LLB ← PROD
|
||||
iBSS ← PROD
|
||||
iBEC ← PROD
|
||||
iBoot ← RES
|
||||
Ap,RestoreSecurePageTableMonitor ← PROD
|
||||
Ap,RestoreTrustedExecutionMonitor← PROD
|
||||
Ap,SecurePageTableMonitor ← PROD
|
||||
Ap,TrustedExecutionMonitor ← RES
|
||||
DeviceTree ← VP
|
||||
RestoreDeviceTree ← VP
|
||||
SEP ← VP
|
||||
RestoreSEP ← VP
|
||||
KernelCache ← VPR (research, patched)
|
||||
RestoreKernelCache ← VP (release, unpatched)
|
||||
RecoveryMode ← VP
|
||||
RestoreRamDisk ← PROD
|
||||
RestoreTrustCache ← PROD
|
||||
Ap,SystemVolumeCanonicalMetadata ← I_ERASE
|
||||
OS ← I_ERASE
|
||||
StaticTrustCache ← I_ERASE
|
||||
SystemVolume ← I_ERASE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Restore.plist
|
||||
|
||||
```
|
||||
DeviceMap: [d47ap (iPhone), vphone600ap, vresearch101ap]
|
||||
ProductTypes: [iPhone17,3, ComputeModule14,1, ComputeModule14,2, Mac14,14, iPhone99,11]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
**Boot chain = vresearch101 (matches DFU hardware); runtime = vphone600 (keybag-less boot); OS = iPhone.**
|
||||
|
||||
The firmware is a PCC shell wrapping an iPhone core. The vresearch101 boot chain
|
||||
handles DFU/TSS signing. The vphone600 device tree + SEP + kernel provide the
|
||||
runtime environment. The iPhone userland is patched post-install for activation
|
||||
bypass, jailbreak tools, and persistent SSH/VNC.
|
||||
@@ -7,14 +7,14 @@ Analysis of iBoot patches for vresearch101 from PCC-CloudOS 26.3 (23D128).
|
||||
All six vresearch101 iBoot variants share just two unique **payload** binaries
|
||||
(after IM4P decode/decompress):
|
||||
|
||||
| Variant | IM4P Size | Raw Size | Payload SHA256 (first 16) | Fourcc |
|
||||
|---------|-----------|----------|---------------------------|--------|
|
||||
| iBSS RELEASE | 303068 | 605312 | `4c9e7df663af76fa` | ibss |
|
||||
| iBEC RELEASE | 303098 | 605312 | `4c9e7df663af76fa` | ibec |
|
||||
| LLB RELEASE | 303068 | 605312 | `4c9e7df663af76fa` | illb |
|
||||
| iBSS RESEARCH | 308188 | 622512 | `8c3cc980f25f9027` | ibss |
|
||||
| iBEC RESEARCH | 308218 | 622512 | `8c3cc980f25f9027` | ibec |
|
||||
| LLB RESEARCH | 308188 | 622512 | `8c3cc980f25f9027` | illb |
|
||||
| Variant | IM4P Size | Raw Size | Payload SHA256 (first 16) | Fourcc |
|
||||
| ------------- | --------- | -------- | ------------------------- | ------ |
|
||||
| iBSS RELEASE | 303068 | 605312 | `4c9e7df663af76fa` | ibss |
|
||||
| iBEC RELEASE | 303098 | 605312 | `4c9e7df663af76fa` | ibec |
|
||||
| LLB RELEASE | 303068 | 605312 | `4c9e7df663af76fa` | illb |
|
||||
| iBSS RESEARCH | 308188 | 622512 | `8c3cc980f25f9027` | ibss |
|
||||
| iBEC RESEARCH | 308218 | 622512 | `8c3cc980f25f9027` | ibec |
|
||||
| LLB RESEARCH | 308188 | 622512 | `8c3cc980f25f9027` | illb |
|
||||
|
||||
**Key finding:** This "identical" claim is strictly about the decoded payload bytes.
|
||||
At the IM4P container level, iBSS/iBEC/LLB are still different files (different
|
||||
@@ -36,12 +36,12 @@ each image.
|
||||
|
||||
Single flat ROM segment (no Mach-O, no sections):
|
||||
|
||||
| Property | RELEASE | RESEARCH |
|
||||
|----------|---------|----------|
|
||||
| Base VA | `0x7006C000` | `0x7006C000` |
|
||||
| Size | 605312 (591.1 KB) | 622512 (607.9 KB) |
|
||||
| Compression | BVX2 (LZFSE) | BVX2 (LZFSE) |
|
||||
| Encrypted | No | No |
|
||||
| Property | RELEASE | RESEARCH |
|
||||
| ----------- | ----------------- | ----------------- |
|
||||
| Base VA | `0x7006C000` | `0x7006C000` |
|
||||
| Size | 605312 (591.1 KB) | 622512 (607.9 KB) |
|
||||
| Compression | BVX2 (LZFSE) | BVX2 (LZFSE) |
|
||||
| Encrypted | No | No |
|
||||
|
||||
File offset = VA − `0x7006C000`.
|
||||
|
||||
@@ -49,20 +49,20 @@ File offset = VA − `0x7006C000`.
|
||||
|
||||
### Base Patches (`fw_patch.py` via `IBootPatcher`)
|
||||
|
||||
| # | Patch | iBSS | iBEC | LLB | Total |
|
||||
|---|-------|:----:|:----:|:---:|:-----:|
|
||||
| 1 | Serial labels (×2) | ✅ | ✅ | ✅ | 2 |
|
||||
| 2 | image4 callback bypass | ✅ | ✅ | ✅ | 2 |
|
||||
| 3 | Boot-args redirect | — | ✅ | ✅ | 3 |
|
||||
| 4 | Rootfs bypass | — | — | ✅ | 5 |
|
||||
| 5 | Panic bypass | — | — | ✅ | 1 |
|
||||
| | **Subtotal** | **4** | **7** | **13** | |
|
||||
| # | Patch | iBSS | iBEC | LLB | Total |
|
||||
| --- | ---------------------- | :---: | :---: | :----: | :---: |
|
||||
| 1 | Serial labels (×2) | ✅ | ✅ | ✅ | 2 |
|
||||
| 2 | image4 callback bypass | ✅ | ✅ | ✅ | 2 |
|
||||
| 3 | Boot-args redirect | — | ✅ | ✅ | 3 |
|
||||
| 4 | Rootfs bypass | — | — | ✅ | 5 |
|
||||
| 5 | Panic bypass | — | — | ✅ | 1 |
|
||||
| | **Subtotal** | **4** | **7** | **13** | |
|
||||
|
||||
### JB Extension Patch (implemented)
|
||||
|
||||
| # | Patch | Base | JB |
|
||||
|---|-------|:----:|:--:|
|
||||
| 6 | **Skip generate_nonce** (iBSS only) | — | ✅ |
|
||||
| # | Patch | Base | JB |
|
||||
| --- | ----------------------------------- | :--: | :-: |
|
||||
| 6 | **Skip generate_nonce** (iBSS only) | — | ✅ |
|
||||
|
||||
Status: implemented in `IBootJBPatcher.patch_skip_generate_nonce()` and applied
|
||||
by `fw_patch_jb.py` (JB flow). This follows the current pipeline split where
|
||||
@@ -79,10 +79,10 @@ serial log identification.
|
||||
4 such runs, but only the first 2 are the banners (the other 2 are
|
||||
`"Start of %s serial output"` / `"End of %s serial output"` format strings).
|
||||
|
||||
| Patch | File Offset | VA | Original | Patched |
|
||||
|-------|-------------|-----|----------|---------|
|
||||
| Label 1 | `0x084549` | `0x700F0549` | `=====...` | `Loaded iBSS` |
|
||||
| Label 2 | `0x0845F4` | `0x700F05F4` | `=====...` | `Loaded iBSS` |
|
||||
| Patch | File Offset | VA | Original | Patched |
|
||||
| ------- | ----------- | ------------ | ---------- | ------------- |
|
||||
| Label 1 | `0x084549` | `0x700F0549` | `=====...` | `Loaded iBSS` |
|
||||
| Label 2 | `0x0845F4` | `0x700F05F4` | `=====...` | `Loaded iBSS` |
|
||||
|
||||
Label text changes per mode: `Loaded iBSS` / `Loaded iBEC` / `Loaded LLB`.
|
||||
|
||||
@@ -98,20 +98,22 @@ Dispatches on 4-char property tags (BORD, CHIP, CEPO, CSEC, DICE, BNCH, etc.)
|
||||
and validates each against expected values.
|
||||
|
||||
**Anchoring pattern:**
|
||||
|
||||
1. `B.NE` followed immediately by `MOV X0, X22`
|
||||
2. `CMP` within 8 instructions before the `B.NE`
|
||||
3. `MOVN W22, #0` (setting error return = -1) within 64 instructions before
|
||||
3. `MOVN W22, #0` or `MOV W22, #-1` (setting error return = -1) within 64 instructions before
|
||||
|
||||
The `B.NE` is the stack canary check at the function epilogue. `X22` holds the
|
||||
computed return value (0 = success, -1 = failure). The patch forces return 0
|
||||
regardless of validation results.
|
||||
|
||||
| Patch | File Offset | VA | Original | Patched |
|
||||
|-------|-------------|-----|----------|---------|
|
||||
| NOP b.ne | `0x009D14` | `0x70075D14` | `B.NE 0x70075E50` | `NOP` |
|
||||
| Force ret=0 | `0x009D18` | `0x70075D18` | `MOV X0, X22` | `MOV X0, #0` |
|
||||
| Patch | File Offset | VA | Original | Patched |
|
||||
| ----------- | ----------- | ------------ | ----------------- | ------------ |
|
||||
| NOP b.ne | `0x009D14` | `0x70075D14` | `B.NE 0x70075E50` | `NOP` |
|
||||
| Force ret=0 | `0x009D18` | `0x70075D18` | `MOV X0, X22` | `MOV X0, #0` |
|
||||
|
||||
**Context (function epilogue):**
|
||||
|
||||
```
|
||||
70075CFC MOV W22, #0xFFFFFFFF ; error return code
|
||||
70075D00 LDUR X8, [X29, #var_60] ; load stack canary
|
||||
@@ -132,30 +134,32 @@ regardless of validation results.
|
||||
and debug flags.
|
||||
|
||||
**Anchoring:**
|
||||
|
||||
1. Find `"rd=md0"` string → search nearby for standalone `"%s"` (NUL-terminated)
|
||||
2. Find `ADRP+ADD X2` pair referencing that `"%s"` offset
|
||||
3. Write new string to a NUL-padded area, redirect ADRP+ADD to it
|
||||
|
||||
| Patch | File Offset | VA | Description |
|
||||
|-------|-------------|-----|-------------|
|
||||
| String | `0x023F40` | `0x700D5F40` | New boot-args string |
|
||||
| ADRP x2 | `0x0122E0` | `0x700DE2E0` | Redirect to new page |
|
||||
| ADD x2 | `0x0122E4` | `0x700DE2E4` | Redirect to new offset |
|
||||
| Patch | File Offset | VA | Description |
|
||||
| ------- | ----------- | ------------ | ---------------------- |
|
||||
| String | `0x023F40` | `0x700D5F40` | New boot-args string |
|
||||
| ADRP x2 | `0x0122E0` | `0x700DE2E0` | Redirect to new page |
|
||||
| ADD x2 | `0x0122E4` | `0x700DE2E4` | Redirect to new offset |
|
||||
|
||||
### Patch 4: Rootfs Bypass (LLB only)
|
||||
|
||||
**Purpose:** 5 patches that bypass root filesystem signature verification,
|
||||
allowing modified rootfs to boot.
|
||||
|
||||
| # | File Offset | VA | Original | Patched | Anchor |
|
||||
|---|-------------|-----|----------|---------|--------|
|
||||
| 4a | `0x02B068` | `0x700D7068` | `CBZ W0, ...` | `B ...` | error code `0x3B7` |
|
||||
| 4b | `0x02AD20` | `0x700D6D20` | `B.HS ...` | `NOP` | `CMP X8, #0x400` |
|
||||
| 4c | `0x02B0BC` | `0x700D70BC` | `CBZ W0, ...` | `B ...` | error code `0x3C2` |
|
||||
| 4d | `0x02ED6C` | `0x700DAD6C` | `CBZ X8, ...` | `NOP` | `LDR X8, [xN, #0x78]` |
|
||||
| 4e | `0x02EF68` | `0x700DAF68` | `CBZ W0, ...` | `B ...` | error code `0x110` |
|
||||
| # | File Offset | VA | Original | Patched | Anchor |
|
||||
| --- | ----------- | ------------ | ------------- | ------- | --------------------- |
|
||||
| 4a | `0x02B068` | `0x700D7068` | `CBZ W0, ...` | `B ...` | error code `0x3B7` |
|
||||
| 4b | `0x02AD20` | `0x700D6D20` | `B.HS ...` | `NOP` | `CMP X8, #0x400` |
|
||||
| 4c | `0x02B0BC` | `0x700D70BC` | `CBZ W0, ...` | `B ...` | error code `0x3C2` |
|
||||
| 4d | `0x02ED6C` | `0x700DAD6C` | `CBZ X8, ...` | `NOP` | `LDR X8, [xN, #0x78]` |
|
||||
| 4e | `0x02EF68` | `0x700DAF68` | `CBZ W0, ...` | `B ...` | error code `0x110` |
|
||||
|
||||
**Anchoring techniques:**
|
||||
|
||||
- **4a, 4c, 4e:** Find unique `MOV W8, #<error>` instruction, the `CBZ` is 4 bytes
|
||||
before. Convert conditional branch to unconditional `B` (same target).
|
||||
- **4b:** Find unique `CMP X8, #0x400`, NOP the `B.HS` that follows.
|
||||
@@ -169,9 +173,9 @@ allowing modified rootfs to boot.
|
||||
**Anchoring:** Find `MOV W8, #0x328` followed by `MOVK W8, #0x40, LSL #16`
|
||||
(forming constant `0x400328`), walk forward to `BL; CBNZ W0`, NOP the `CBNZ`.
|
||||
|
||||
| Patch | File Offset | VA | Original | Patched |
|
||||
|-------|-------------|-----|----------|---------|
|
||||
| NOP cbnz | `0x01A038` | `0x70086038` | `CBNZ W0, ...` | `NOP` |
|
||||
| Patch | File Offset | VA | Original | Patched |
|
||||
| -------- | ----------- | ------------ | -------------- | ------- |
|
||||
| NOP cbnz | `0x01A038` | `0x70086038` | `CBNZ W0, ...` | `NOP` |
|
||||
|
||||
### Patch 6: Skip generate_nonce (iBSS only, JB flow)
|
||||
|
||||
@@ -182,13 +186,14 @@ boot, which can interfere with the restore process.
|
||||
**Function:** `sub_70077064` (~0x1C00 bytes) — iBSS platform initialization.
|
||||
|
||||
**Anchoring:** Find `"boot-nonce"` string reference via ADRP+ADD, then scan forward
|
||||
for: `TBZ W0, #0` + `MOV W0, #0` + `BL` pattern. Convert `TBZ` to unconditional `B`.
|
||||
for: `TBZ/TBNZ W0, #0` + `MOV W0, #0` + `BL` pattern. Convert `TBZ/TBNZ` to unconditional `B`.
|
||||
|
||||
| Patch | File Offset | VA | Original | Patched |
|
||||
|-------|-------------|-----|----------|---------|
|
||||
| Skip nonce | `0x00B7B8` | `0x700777B8` | `TBZ W0, #0, 0x700777F0` | `B 0x700777F0` |
|
||||
| Patch | File Offset | VA | Original | Patched |
|
||||
| ---------- | ----------- | ------------ | ------------------------ | -------------- |
|
||||
| Skip nonce | `0x00B7B8` | `0x700777B8` | `TBZ W0, #0, 0x700777F0` | `B 0x700777F0` |
|
||||
|
||||
**Disassembly context:**
|
||||
|
||||
```
|
||||
70077750 ADD X8, X8, #("boot-nonce" - ...) ; 1st ref: read nonce env var
|
||||
70077754 BL sub_70079590 ; env_get
|
||||
@@ -222,13 +227,13 @@ deterministic nonce behavior for restore/research scenarios.
|
||||
Both variants work with all dynamic patches. Offsets differ but the patcher
|
||||
finds them by pattern matching:
|
||||
|
||||
| Patch | RELEASE offset | RESEARCH offset |
|
||||
|-------|---------------|-----------------|
|
||||
| Serial label 1 | `0x084549` | `0x0861C9` |
|
||||
| Serial label 2 | `0x0845F4` | `0x086274` |
|
||||
| image4 callback (nop) | `0x009D14` | `0x00A0DC` |
|
||||
| image4 callback (mov) | `0x009D18` | `0x00A0E0` |
|
||||
| Skip generate_nonce *(JB patch)* | `0x00B7B8` | `0x00BC08` |
|
||||
| Patch | RELEASE offset | RESEARCH offset |
|
||||
| -------------------------------- | -------------- | --------------- |
|
||||
| Serial label 1 | `0x084549` | `0x0861C9` |
|
||||
| Serial label 2 | `0x0845F4` | `0x086274` |
|
||||
| image4 callback (nop) | `0x009D14` | `0x00A0DC` |
|
||||
| image4 callback (mov) | `0x009D18` | `0x00A0E0` |
|
||||
| Skip generate_nonce _(JB patch)_ | `0x00B7B8` | `0x00BC08` |
|
||||
|
||||
`fw_patch.py` targets RELEASE, matching the BuildManifest identity
|
||||
(PCC RELEASE for LLB/iBSS/iBEC). The reference script used RESEARCH_RELEASE.
|
||||
@@ -239,13 +244,13 @@ Both work — the dynamic patcher is variant-agnostic.
|
||||
Reference hardcoded offsets (26.1 RESEARCH_RELEASE) vs dynamic patcher results
|
||||
(26.3 RELEASE):
|
||||
|
||||
| Patch | 26.1 (hardcoded) | 26.3 RELEASE (dynamic) | 26.3 RESEARCH (dynamic) |
|
||||
|-------|-----------------|----------------------|------------------------|
|
||||
| Serial label 1 | `0x84349` | `0x84549` | `0x861C9` |
|
||||
| Serial label 2 | `0x843F4` | `0x845F4` | `0x86274` |
|
||||
| image4 nop | `0x09D10` | `0x09D14` | `0x0A0DC` |
|
||||
| image4 mov | `0x09D14` | `0x09D18` | `0x0A0E0` |
|
||||
| generate_nonce | `0x1B544` | `0x0B7B8` | `0x0BC08` |
|
||||
| Patch | 26.1 (hardcoded) | 26.3 RELEASE (dynamic) | 26.3 RESEARCH (dynamic) |
|
||||
| -------------- | ---------------- | ---------------------- | ----------------------- |
|
||||
| Serial label 1 | `0x84349` | `0x84549` | `0x861C9` |
|
||||
| Serial label 2 | `0x843F4` | `0x845F4` | `0x86274` |
|
||||
| image4 nop | `0x09D10` | `0x09D14` | `0x0A0DC` |
|
||||
| image4 mov | `0x09D14` | `0x09D18` | `0x0A0E0` |
|
||||
| generate_nonce | `0x1B544` | `0x0B7B8` | `0x0BC08` |
|
||||
|
||||
Offsets shift significantly between versions and variants, confirming that
|
||||
hardcoded offsets would break. The dynamic patcher handles all combinations.
|
||||
@@ -528,17 +533,17 @@ the `"dram-vendor"` env_get.
|
||||
|
||||
Reference: `patch(0x1b544, 0x1400000e)` (26.1 RESEARCH, hardcoded)
|
||||
|
||||
| | Reference (26.1) | Dynamic (26.3 RELEASE) | Dynamic (26.3 RESEARCH) |
|
||||
|---|---|---|---|
|
||||
| **Offset** | `0x1B544` | `0x0B7B8` | `0x0BC08` |
|
||||
| **Original** | `TBZ W0, #0, +0x38` | `TBZ W0, #0, +0x38` | `TBZ W0, #0, +0x38` |
|
||||
| **Patched** | `B +0x38` | `B +0x38` | `B +0x38` |
|
||||
| **Bytes** | `0E 00 00 14` | `0E 00 00 14` | `0E 00 00 14` |
|
||||
| | Reference (26.1) | Dynamic (26.3 RELEASE) | Dynamic (26.3 RESEARCH) |
|
||||
| ------------ | ------------------- | ---------------------- | ----------------------- |
|
||||
| **Offset** | `0x1B544` | `0x0B7B8` | `0x0BC08` |
|
||||
| **Original** | `TBZ W0, #0, +0x38` | `TBZ W0, #0, +0x38` | `TBZ W0, #0, +0x38` |
|
||||
| **Patched** | `B +0x38` | `B +0x38` | `B +0x38` |
|
||||
| **Bytes** | `0E 00 00 14` | `0E 00 00 14` | `0E 00 00 14` |
|
||||
|
||||
All three produce byte-identical `0x1400000E` — same branch delta `+0x38` (14 words)
|
||||
across all variants. Only the file offset differs between versions.
|
||||
|
||||
## Status
|
||||
|
||||
`patch_skip_generate_nonce()` is active in the rewrite/JB path via
|
||||
`patch_skip_generate_nonce()` is active in the JB path via
|
||||
`IBootJBPatcher` and `fw_patch_jb.py` (iBSS JB component enabled).
|
||||
@@ -1,6 +1,6 @@
|
||||
# FairPlay IOKit Extensions in PCC Kernel
|
||||
|
||||
**Kernel:** `kernelcache.release.vphone600` (PCC/cloudOS, vresearch101ap)
|
||||
**Kernel:** `kernelcache.research.vphone600` (PCC/cloudOS, vphone600ap research)
|
||||
**Total kexts in kernel:** 161
|
||||
**FairPlay kexts found:** 2
|
||||
|
||||
@@ -8,18 +8,19 @@
|
||||
|
||||
## 1. com.apple.driver.AvpFairPlayDriver
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Path | `/System/Library/Extensions/AvpFairPlayDriver.kext` |
|
||||
| Version | 2.9.0 |
|
||||
| Executable Size | 2,920 bytes |
|
||||
| IOClass | `AvpFairPlayDriver` |
|
||||
| IOProviderClass | `AppleVirtIOTransport` |
|
||||
| IOUserClientClass | `AvpFairPlayUserClient` |
|
||||
| PCI Match | `0x1a08106b` |
|
||||
| Dependencies | AppleVirtIO, IOKit, libkern |
|
||||
| Field | Value |
|
||||
| ----------------- | --------------------------------------------------- |
|
||||
| Path | `/System/Library/Extensions/AvpFairPlayDriver.kext` |
|
||||
| Version | 2.9.0 |
|
||||
| Executable Size | 2,920 bytes |
|
||||
| IOClass | `AvpFairPlayDriver` |
|
||||
| IOProviderClass | `AppleVirtIOTransport` |
|
||||
| IOUserClientClass | `AvpFairPlayUserClient` |
|
||||
| PCI Match | `0x1a08106b` |
|
||||
| Dependencies | AppleVirtIO, IOKit, libkern |
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Virtualization-specific FairPlay driver — matches on a VirtIO PCI device ID (`0x1a08106b`).
|
||||
- Tiny kext (2.9 KB), acts as a paravirtual bridge to the host-side FairPlay backend.
|
||||
- Implies the host Virtualization.framework exposes a FairPlay VirtIO device to the guest.
|
||||
@@ -31,19 +32,20 @@
|
||||
|
||||
## 2. com.apple.driver.FairPlayIOKit
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Path | `/System/Library/Extensions/FairPlayIOKit.kext` |
|
||||
| Version | 72.15.0 |
|
||||
| Executable Size | 269,440 bytes |
|
||||
| IOClass | `com_apple_driver_FairPlayIOKit` |
|
||||
| IOProviderClass | `IOResources` (always-match) |
|
||||
| IOUserClientClass | `com_apple_driver_FairPlayIOKitUserClient` |
|
||||
| IOMatchCategory | `FairPlayIOKit` |
|
||||
| IOProbeScore | 1000 |
|
||||
| Dependencies | bsd, **dsep**, iokit, libkern, mach, private, unsupported |
|
||||
| Field | Value |
|
||||
| ----------------- | --------------------------------------------------------- |
|
||||
| Path | `/System/Library/Extensions/FairPlayIOKit.kext` |
|
||||
| Version | 72.15.0 |
|
||||
| Executable Size | 269,440 bytes |
|
||||
| IOClass | `com_apple_driver_FairPlayIOKit` |
|
||||
| IOProviderClass | `IOResources` (always-match) |
|
||||
| IOUserClientClass | `com_apple_driver_FairPlayIOKitUserClient` |
|
||||
| IOMatchCategory | `FairPlayIOKit` |
|
||||
| IOProbeScore | 1000 |
|
||||
| Dependencies | bsd, **dsep**, iokit, libkern, mach, private, unsupported |
|
||||
|
||||
**Notes:**
|
||||
|
||||
- Full FairPlay DRM framework kext (269 KB of code).
|
||||
- Matches on `IOResources` — loads unconditionally at boot.
|
||||
- Provides FairPlay services to userland via `com_apple_driver_FairPlayIOKitUserClient`.
|
||||
@@ -54,12 +56,12 @@
|
||||
|
||||
## String Occurrences
|
||||
|
||||
| Pattern | Count |
|
||||
|---------|-------|
|
||||
| `FairPlay` (case-sensitive) | 139 |
|
||||
| `fairplay` (lowercase) | 27 |
|
||||
| `com.apple.driver.FairPlayIOKit` | 6 |
|
||||
| `com.apple.driver.AvpFairPlayDriver` | 3 |
|
||||
| Pattern | Count |
|
||||
| ------------------------------------ | ----- |
|
||||
| `FairPlay` (case-sensitive) | 139 |
|
||||
| `fairplay` (lowercase) | 27 |
|
||||
| `com.apple.driver.FairPlayIOKit` | 6 |
|
||||
| `com.apple.driver.AvpFairPlayDriver` | 3 |
|
||||
|
||||
Lowercase `fairplay` strings include launch constraint labels: `com.apple.fairplayd`, `com.apple.fairplayd.A2`, `com.apple.fairplayd.A2.dev`, `com.apple.fairplayd.G1` — these are userland daemon identifiers referenced in kernel launch constraint plists.
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
# Kernel JB Remaining Patches — Research Notes
|
||||
|
||||
Last updated: 2026-03-01
|
||||
Last updated: 2026-03-04
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/patchers/kernel_jb.py` has 22 patch methods in `find_all()`. As of this writing:
|
||||
`scripts/patchers/kernel_jb.py` has 24 patch methods in `find_all()`. Current status:
|
||||
|
||||
- **19 PASSING**: All Group A + most Group B + some Group C patches
|
||||
- **3 FAILING**: `patch_nvram_verify_permission`, `patch_thid_should_crash`, `patch_hook_cred_label_update_execve`
|
||||
- **1 FIXED this session**: `patch_syscallmask_apply_to_proc` (bl_callers key bug + now passing)
|
||||
- **2 FIXED prior session**: `patch_task_for_pid`, `patch_load_dylinker` (complete rewrites)
|
||||
- **24 PASSING**: All patches implemented and functional
|
||||
- **0 FAILING**
|
||||
|
||||
Two methods added since initial document: `patch_shared_region_map`, `patch_io_secure_bsd_root`.
|
||||
Three previously failing patches (`patch_nvram_verify_permission`, `patch_thid_should_crash`, `patch_hook_cred_label_update_execve`) have been implemented — see details below.
|
||||
|
||||
Upstream reference: `/Users/qaq/Documents/GitHub/super-tart-vphone/CFW/patch_fw.py`
|
||||
|
||||
Test kernel: `vm/iPhone17,3_26.1_23B85_Restore/kernelcache.release.vphone600` (IM4P-wrapped, bvx2 compressed)
|
||||
|
||||
Key facts about the kernel:
|
||||
|
||||
- **0 symbols resolved** (fully stripped)
|
||||
- `base_va = 0xFFFFFE0007004000` (typical PCC)
|
||||
- `kern_text = 0xA74000 - 0x24B0000`
|
||||
@@ -45,14 +47,14 @@ One single NOP at file offset `0x1234034`. The BL being NOPed calls memmove (311
|
||||
|
||||
#### Full BL targets in the function:
|
||||
|
||||
| Offset | Delta | Target | Callers | Likely Identity |
|
||||
|--------|-------|--------|---------|-----------------|
|
||||
| 0x1233F0C | +0x0CC | 0x0AD10DC | 6190 | lck_rw_done / lock_release |
|
||||
| 0x1234034 | +0x1F4 | 0x12CB0D0 | 3114 | **memmove** ← PATCH THIS |
|
||||
| 0x1234048 | +0x208 | 0x0ACB418 | 423 | OSObject::release |
|
||||
| 0x1234070 | +0x230 | 0x0AD029C | 4921 | lck_rw_lock_exclusive |
|
||||
| 0x123407C | +0x23C | 0x0AD10DC | 6190 | lck_rw_done |
|
||||
| 0x123408C | +0x24C | 0x0AD10DC | 6190 | lck_rw_done |
|
||||
| Offset | Delta | Target | Callers | Likely Identity |
|
||||
| --------- | ------ | --------- | ------- | -------------------------- |
|
||||
| 0x1233F0C | +0x0CC | 0x0AD10DC | 6190 | lck_rw_done / lock_release |
|
||||
| 0x1234034 | +0x1F4 | 0x12CB0D0 | 3114 | **memmove** ← PATCH THIS |
|
||||
| 0x1234048 | +0x208 | 0x0ACB418 | 423 | OSObject::release |
|
||||
| 0x1234070 | +0x230 | 0x0AD029C | 4921 | lck_rw_lock_exclusive |
|
||||
| 0x123407C | +0x23C | 0x0AD10DC | 6190 | lck_rw_done |
|
||||
| 0x123408C | +0x24C | 0x0AD10DC | 6190 | lck_rw_done |
|
||||
|
||||
#### Key instructions in the function:
|
||||
|
||||
@@ -62,7 +64,7 @@ One single NOP at file offset `0x1234034`. The BL being NOPed calls memmove (311
|
||||
- `movk x17, #0xcda1, lsl #48` — PAC discriminator for IONVRAMController class
|
||||
- `RETAB` — PAC return
|
||||
- `mov x8, #-1; str x8, [x19]` — cleanup pattern near end
|
||||
- `ubfiz x2, x8, #3, #0x20` before BL memmove — size = count * 8
|
||||
- `ubfiz x2, x8, #3, #0x20` before BL memmove — size = count \* 8
|
||||
|
||||
#### "Remove from array" pattern (at patch site):
|
||||
|
||||
@@ -118,15 +120,15 @@ __DATA_CONST @ 0x7410B8: raw=0x8011377101233E40 → decoded=0x1233E40 (verifyPer
|
||||
|
||||
**Vtable layout at 0x7410B8:**
|
||||
|
||||
| Vtable Idx | File Offset | Content | First Insn |
|
||||
|------------|-------------|---------|------------|
|
||||
| [-3] 0x7410A0 | | NULL | |
|
||||
| [-2] 0x7410A8 | | NULL | |
|
||||
| [-1] 0x7410B0 | | NULL | |
|
||||
| [0] 0x7410B8 | 0x1233E40 | **verifyPermission** | pacibsp |
|
||||
| [1] 0x7410C0 | 0x1233BF0 | sister method | pacibsp |
|
||||
| [2] 0x7410C8 | 0x10EA4E0 | | ret |
|
||||
| [3] 0x7410D0 | 0x10EA4D8 | | mov |
|
||||
| Vtable Idx | File Offset | Content | First Insn |
|
||||
| ------------- | ----------- | -------------------- | ---------- |
|
||||
| [-3] 0x7410A0 | | NULL | |
|
||||
| [-2] 0x7410A8 | | NULL | |
|
||||
| [-1] 0x7410B0 | | NULL | |
|
||||
| [0] 0x7410B8 | 0x1233E40 | **verifyPermission** | pacibsp |
|
||||
| [1] 0x7410C0 | 0x1233BF0 | sister method | pacibsp |
|
||||
| [2] 0x7410C8 | 0x10EA4E0 | | ret |
|
||||
| [3] 0x7410D0 | 0x10EA4D8 | | mov |
|
||||
|
||||
**IONVRAMController metaclass constructor pattern:**
|
||||
|
||||
@@ -162,7 +164,8 @@ Wait — it actually points to `0x7410A8`, not `0x7410B8`. The vtable pointer wi
|
||||
|
||||
**Chain**: "IONVRAMController" string → ADRP+ADD refs → metaclass constructor → extract instance size `0x88` → find the combined class registration function (0x12376D8) that calls OSMetaClass::OSMetaClass() with `mov w3, #0x88` AND uses "IONVRAMController" name → extract the vtable base from the ADRP+ADD+ADD that follows → vtable[0] = verifyPermission → find BL to memmove-like target (>2000 callers) and NOP it.
|
||||
|
||||
**Alternative (simpler)**: From the metaclass constructor, extract the PAC discriminator `#0xcda1` and the instance size `#0x88`. Then search __DATA_CONST for chained fixup pointer entries where:
|
||||
**Alternative (simpler)**: From the metaclass constructor, extract the PAC discriminator `#0xcda1` and the instance size `#0x88`. Then search \_\_DATA_CONST for chained fixup pointer entries where:
|
||||
|
||||
- The preceding 3 entries (at -8, -16, -24) are NULL (vtable header)
|
||||
- The decoded function pointer has 0 BL callers
|
||||
- The function contains CASA
|
||||
@@ -172,7 +175,8 @@ Wait — it actually points to `0x7410A8`, not `0x7410B8`. The vtable pointer wi
|
||||
|
||||
This last filter is the KEY discriminator. Among the 332 candidate functions, only IONVRAMController methods use PAC disc `0xcda1`. Combined with "first entry in vtable" (preceded by 3 nulls), this should be unique.
|
||||
|
||||
**Simplest approach**: Search all chained fixup pointers in __DATA_CONST where:
|
||||
**Simplest approach**: Search all chained fixup pointers in \_\_DATA_CONST where:
|
||||
|
||||
1. Preceded by 3 null entries (vtable start)
|
||||
2. Decoded target is a function in kern_text
|
||||
3. Function contains `movk x17, #0xcda1, lsl #48`
|
||||
@@ -209,24 +213,27 @@ Writes 4 bytes of zero at file offset `0x67EB50`.
|
||||
### Proposed Dynamic Strategy
|
||||
|
||||
The variable at `0x67EB50` is in the kernel's `__DATA` segment (BSS or initialized data). Since:
|
||||
|
||||
- The string is only in `__PRELINK_INFO` (plist), not usable as a code anchor
|
||||
- The variable has no symbols
|
||||
- The value is already 0
|
||||
|
||||
**Option A: Skip this patch gracefully.** If the value is already 0, the patch has no effect. Log a message and return True (success, nothing to do).
|
||||
|
||||
**Option B: Find via sysctl table structure.** The sysctl_oid structure in __DATA contains:
|
||||
**Option B: Find via sysctl table structure.** The sysctl_oid structure in \_\_DATA contains:
|
||||
|
||||
- A pointer to the name string
|
||||
- A pointer to the data variable
|
||||
- Various flags
|
||||
|
||||
But the name string pointer would be a chained fixup pointer to the string in __PRELINK_INFO, which is hard to search for.
|
||||
But the name string pointer would be a chained fixup pointer to the string in \_\_PRELINK_INFO, which is hard to search for.
|
||||
|
||||
**Option C: Find via `__PRELINK_INFO` plist parsing.** Parse the XML plist to find the `_PrelinkKCID` or sysctl registration info. This is complex and fragile.
|
||||
|
||||
**Recommended: Option A** — the variable is already 0 in PCC kernels. Emit a write-zero anyway at the upstream-equivalent location if we can find it, or just return True if we can't find the variable (safe no-op).
|
||||
|
||||
Actually, better approach: search `__DATA` segments for a `sysctl_oid` struct. The struct layout includes:
|
||||
|
||||
```c
|
||||
struct sysctl_oid {
|
||||
struct sysctl_oid_list *oid_parent; // +0x00
|
||||
@@ -242,7 +249,7 @@ struct sysctl_oid {
|
||||
|
||||
So search all `__DATA` segments for an 8-byte value at offset +0x28 that decodes to the "thid_should_crash" string offset. Then read +0x18 to get the variable pointer.
|
||||
|
||||
But the string is in __PRELINK_INFO, which complicates decoding the chained fixup pointer.
|
||||
But the string is in \_\_PRELINK_INFO, which complicates decoding the chained fixup pointer.
|
||||
|
||||
---
|
||||
|
||||
@@ -262,12 +269,13 @@ But the string is in __PRELINK_INFO, which complicates decoding the chained fixu
|
||||
### Why It Fails
|
||||
|
||||
The patch needs two kernel functions that have **no symbols**:
|
||||
|
||||
- `_vfs_context_current` at file offset `0xCC5EAC`
|
||||
- `_vnode_getattr` at file offset `0xCC91C0`
|
||||
|
||||
Without these, the shellcode can't be assembled (the BL offsets depend on the target addresses).
|
||||
|
||||
### Analysis of _vfs_context_current (0xCC5EAC)
|
||||
### Analysis of \_vfs_context_current (0xCC5EAC)
|
||||
|
||||
```
|
||||
Expected: A very short function (2-4 instructions) that:
|
||||
@@ -280,7 +288,7 @@ Should have extremely high caller count (VFS is used everywhere).
|
||||
|
||||
Let me verify: check `bl_callers.get(0xCC5EAC, [])` — should have many callers.
|
||||
|
||||
### Analysis of _vnode_getattr (0xCC91C0)
|
||||
### Analysis of \_vnode_getattr (0xCC91C0)
|
||||
|
||||
```
|
||||
Expected: A moderate-sized function that:
|
||||
@@ -291,7 +299,7 @@ Expected: A moderate-sized function that:
|
||||
Should have moderate caller count (hundreds).
|
||||
```
|
||||
|
||||
### Finding Strategy for _vfs_context_current
|
||||
### Finding Strategy for \_vfs_context_current
|
||||
|
||||
1. **From sandbox ops table**: We already have `_find_sandbox_ops_table_via_conf()`. The hook_cred_label_update_execve entry (index 16) in the ops table points to the original sandbox hook function (at `0x239A0B4` per upstream).
|
||||
|
||||
@@ -302,7 +310,7 @@ Should have moderate caller count (hundreds).
|
||||
- Very high caller count (>1000)
|
||||
- Return type is pointer (loads from struct offset)
|
||||
|
||||
### Finding Strategy for _vnode_getattr
|
||||
### Finding Strategy for \_vnode_getattr
|
||||
|
||||
1. **From the original hook function**: The hook function likely also calls `_vnode_getattr`. Find BL targets in the hook that have moderate caller count.
|
||||
|
||||
@@ -326,15 +334,15 @@ Should have moderate caller count (hundreds).
|
||||
|
||||
## Patch Status Summary
|
||||
|
||||
| Patch | Status | Blocker | Strategy |
|
||||
|-------|--------|---------|----------|
|
||||
| nvram_verify_permission | FAILING | Can't distinguish among 332 identical IOKit methods | Use PAC disc `#0xcda1` + vtable header (3 nulls) to find unique IONVRAMController vtable entry |
|
||||
| thid_should_crash | FAILING | String in __PRELINK_INFO, no code refs, value already 0 | Option A: return True (safe no-op); Option B: sysctl_oid struct search |
|
||||
| hook_cred_label_update_execve | FAILING | Can't find vfs_context_current and vnode_getattr without symbols | Find via sandbox ops table → original hook → BL targets by caller count |
|
||||
| Patch | Status | Implementation |
|
||||
| ----------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| nvram_verify_permission | IMPLEMENTED | Uses "krn." string anchor → NOP TBZ/TBNZ guard near string ref |
|
||||
| thid_should_crash | IMPLEMENTED | Multi-strategy: symbol lookup, sysctl_oid struct scanning, ADRP+ADD fallback |
|
||||
| hook_cred_label_update_execve | IMPLEMENTED | Inline vfs_context via `mrs x8, tpidr_el1` + `stp`; vnode_getattr via string anchor; dynamic hook index + code cave |
|
||||
|
||||
---
|
||||
|
||||
## Previously Fixed Patches (This Session)
|
||||
## Previously Fixed Patches
|
||||
|
||||
### patch_task_for_pid — FIXED
|
||||
|
||||
@@ -354,6 +362,21 @@ Should have moderate caller count (hundreds).
|
||||
**Fix**: Changed to `self.bl_callers.get(target, [])` at line ~1661.
|
||||
**Status**: Now PASSING (40 patches emitted for shellcode + redirect).
|
||||
|
||||
### patch_nvram_verify_permission — FIXED
|
||||
|
||||
**Problem**: 332 identical IOKit methods match structural filter; "krn." string leads to wrong function.
|
||||
**Solution**: Uses "krn." string anchor to find the function, then NOPs TBZ/TBNZ guard near the string ref. Different mechanism from upstream (NOP BL memmove) but achieves the same NVRAM bypass.
|
||||
|
||||
### patch_thid_should_crash — FIXED
|
||||
|
||||
**Problem**: String in `__PRELINK_INFO` plist (no code refs); value already `0x00000000` in PCC kernel.
|
||||
**Solution**: Multi-strategy approach — symbol lookup, string search + sysctl_oid struct scanning (checking forward 128 bytes for chained fixup pointers), and ADRP+ADD fallback.
|
||||
|
||||
### patch_hook_cred_label_update_execve — FIXED
|
||||
|
||||
**Problem**: Needed `_vfs_context_current` and `_vnode_getattr` — 0 symbols available.
|
||||
**Solution**: Eliminated `_vfs_context_current` entirely — shellcode constructs vfs_context inline on stack via `mrs x8, tpidr_el1` + `stp x8, x0, [sp, #0x70]`. `_vnode_getattr` found via "vnode_getattr" string anchor. Hook index found dynamically (scan first 30 ops entries). Code cave allocated via `_find_code_cave(180)`.
|
||||
|
||||
---
|
||||
|
||||
## Environment Notes
|
||||
@@ -377,6 +400,7 @@ print(f'Total patches: {len(patches)}')
|
||||
### Running on Linux (cloud)
|
||||
|
||||
Requirements:
|
||||
|
||||
- Python 3.10+
|
||||
- `pip install capstone keystone-engine pyimg4`
|
||||
- Note: `keystone-engine` may need `cmake` and C++ compiler on Linux
|
||||
@@ -384,6 +408,7 @@ Requirements:
|
||||
- The `setup_venv.sh` script has macOS-specific keystone dylib handling — on Linux, pip install should work directly
|
||||
|
||||
Files needed:
|
||||
|
||||
- `scripts/patchers/kernel.py` (base class)
|
||||
- `scripts/patchers/kernel_jb.py` (JB patcher)
|
||||
- `scripts/patchers/__init__.py`
|
||||
@@ -395,7 +420,7 @@ Files needed:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Quick test for failing patches."""
|
||||
"""Test all 24 JB kernel patch methods."""
|
||||
import sys
|
||||
sys.path.insert(0, 'scripts')
|
||||
from fw_patch import load_firmware
|
||||
@@ -403,40 +428,34 @@ from patchers.kernel_jb import KernelJBPatcher
|
||||
|
||||
_, data, _, _ = load_firmware('vm/iPhone17,3_26.1_23B85_Restore/kernelcache.release.vphone600')
|
||||
p = KernelJBPatcher(data, verbose=True)
|
||||
|
||||
failing = ['patch_nvram_verify_permission', 'patch_thid_should_crash',
|
||||
'patch_hook_cred_label_update_execve']
|
||||
for name in failing:
|
||||
p.patches = []
|
||||
result = getattr(p, name)()
|
||||
status = "PASS" if result else "FAIL"
|
||||
print(f'\n>>> {name}: {status} ({len(p.patches)} patches)')
|
||||
patches = p.find_all()
|
||||
print(f'\n>>> Total: {len(patches)} patches from 24 methods')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Upstream Offsets Reference (iPhone17,3 26.1 23B85)
|
||||
|
||||
| Symbol / Patch | File Offset | Notes |
|
||||
|----------------|-------------|-------|
|
||||
| kern_text start | 0xA74000 | |
|
||||
| kern_text end | 0x24B0000 | |
|
||||
| base_va | 0xFFFFFE0007004000 | |
|
||||
| _thid_should_crash var | 0x67EB50 | DATA, value=0 |
|
||||
| _task_for_pid func | 0xFC3718 | patch at 0xFC383C |
|
||||
| _load_dylinker patch | 0x1052A28 | TST → B |
|
||||
| verifyPermission func | 0x1233E40 | patch BL at 0x1234034 |
|
||||
| verifyPermission vtable | 0x7410B8 | __DATA_CONST |
|
||||
| IONVRAMController metaclass | 0x26FEA38 | |
|
||||
| IONVRAMController metaclass ctor | 0x125D2C0 | refs "IONVRAMController" string |
|
||||
| IONVRAMController PAC disc | 0xcda1 | movk x17, #0xcda1 |
|
||||
| IONVRAMController instance size | 0x88 | mov w3, #0x88 |
|
||||
| _vfs_context_current | 0xCC5EAC | (from upstream BL encoding) |
|
||||
| _vnode_getattr | 0xCC91C0 | (from upstream BL encoding) |
|
||||
| shellcode cave (upstream) | 0xAB1740 | syscallmask |
|
||||
| shellcode cave 2 (upstream) | 0xAB17D8 | hook_cred_label |
|
||||
| sandbox ops table (hook entry) | 0xA54518 | index 16 |
|
||||
| _hook_cred_label_update_execve | 0x239A0B4 | original hook func |
|
||||
| memmove | 0x12CB0D0 | 3114 callers |
|
||||
| OSMetaClass::OSMetaClass() | 0x10EA790 | 5236 callers |
|
||||
| _panic | varies | 8000+ callers typically |
|
||||
| Symbol / Patch | File Offset | Notes |
|
||||
| -------------------------------- | ------------------ | ------------------------------- |
|
||||
| kern_text start | 0xA74000 | |
|
||||
| kern_text end | 0x24B0000 | |
|
||||
| base_va | 0xFFFFFE0007004000 | |
|
||||
| \_thid_should_crash var | 0x67EB50 | DATA, value=0 |
|
||||
| \_task_for_pid func | 0xFC3718 | patch at 0xFC383C |
|
||||
| \_load_dylinker patch | 0x1052A28 | TST → B |
|
||||
| verifyPermission func | 0x1233E40 | patch BL at 0x1234034 |
|
||||
| verifyPermission vtable | 0x7410B8 | \_\_DATA_CONST |
|
||||
| IONVRAMController metaclass | 0x26FEA38 | |
|
||||
| IONVRAMController metaclass ctor | 0x125D2C0 | refs "IONVRAMController" string |
|
||||
| IONVRAMController PAC disc | 0xcda1 | movk x17, #0xcda1 |
|
||||
| IONVRAMController instance size | 0x88 | mov w3, #0x88 |
|
||||
| \_vfs_context_current | 0xCC5EAC | (from upstream BL encoding) |
|
||||
| \_vnode_getattr | 0xCC91C0 | (from upstream BL encoding) |
|
||||
| shellcode cave (upstream) | 0xAB1740 | syscallmask |
|
||||
| shellcode cave 2 (upstream) | 0xAB17D8 | hook_cred_label |
|
||||
| sandbox ops table (hook entry) | 0xA54518 | index 16 |
|
||||
| \_hook_cred_label_update_execve | 0x239A0B4 | original hook func |
|
||||
| memmove | 0x12CB0D0 | 3114 callers |
|
||||
| OSMetaClass::OSMetaClass() | 0x10EA790 | 5236 callers |
|
||||
| \_panic | varies | 8000+ callers typically |
|
||||
@@ -3,17 +3,20 @@
|
||||
Date: 2026-02-27
|
||||
|
||||
## Scope
|
||||
Verify that the dynamic kernel patch finder (`Scripts/kernel_patcher.py`) produces
|
||||
|
||||
Verify that the dynamic kernel patch finder (`scripts/patchers/kernel.py`) produces
|
||||
the same binary result as the legacy hardcoded patch list on vphone600, then
|
||||
apply the dynamic patcher to a freshly extracted vresearch101 kernelcache.
|
||||
|
||||
## Inputs
|
||||
|
||||
- Original vphone600 raw kernel: `/tmp/kc_vphone600_orig.raw`
|
||||
- vphone600 upstream hardcoded patch list: `super-tart-vphone-private/CFW/patch_fw.py`
|
||||
- Dynamic patcher: `Scripts/kernel_patcher.py`
|
||||
- Dynamic patcher: `scripts/patchers/kernel.py`
|
||||
- VM kernelcache image (vresearch101): `VM/iPhone17,3_26.1_23B85_Restore/kernelcache.research.vresearch101`
|
||||
|
||||
## Method
|
||||
|
||||
1. Apply legacy hardcoded patches to `/tmp/kc_vphone600_orig.raw` using binary
|
||||
replacement (32-bit writes) and save as `/tmp/kc_vphone600_upstream.raw`.
|
||||
2. Run `KernelPatcher.find_all()` on `/tmp/kc_vphone600_orig.raw`, apply all
|
||||
@@ -25,12 +28,14 @@ apply the dynamic patcher to a freshly extracted vresearch101 kernelcache.
|
||||
`/tmp/kc_vresearch1_dynamic.raw`.
|
||||
|
||||
## Outputs
|
||||
|
||||
- `/tmp/kc_vphone600_upstream.raw`
|
||||
- `/tmp/kc_vphone600_dynamic.raw`
|
||||
- `/tmp/kc_vresearch1_orig.raw`
|
||||
- `/tmp/kc_vresearch1_dynamic.raw`
|
||||
|
||||
## Checksums (SHA-256)
|
||||
|
||||
- `/tmp/kc_vphone600_orig.raw`:
|
||||
`b6846048f3a60eab5f360fcc0f3dcb5198aa0476c86fb06eb42f6267cdbfcae0`
|
||||
- `/tmp/kc_vphone600_upstream.raw`:
|
||||
@@ -43,6 +48,7 @@ apply the dynamic patcher to a freshly extracted vresearch101 kernelcache.
|
||||
`f36a78ce59c658df85ecdead56d46370a1107181689091cf798e529664f6e2b5`
|
||||
|
||||
## vphone600: Hardcoded vs Dynamic
|
||||
|
||||
Result: **byte-identical** output between hardcoded and dynamic patching.
|
||||
|
||||
- `KernelPatcher` patches found: 25
|
||||
@@ -51,49 +57,55 @@ Result: **byte-identical** output between hardcoded and dynamic patching.
|
||||
no output (files identical)
|
||||
|
||||
### Hardcoded Patch List (vphone600)
|
||||
|
||||
Offsets and 32-bit patch values, taken from `patch_fw.py`:
|
||||
|
||||
| # | Offset (hex) | Patch value | Purpose |
|
||||
|---|-------------:|------------:|---------|
|
||||
| 1 | 0x2476964 | 0xD503201F | _apfs_vfsop_mount root snapshot NOP |
|
||||
| 2 | 0x23CFDE4 | 0xD503201F | _authapfs_seal_is_broken NOP |
|
||||
| 3 | 0x00F6D960 | 0xD503201F | _bsd_init rootvp NOP |
|
||||
| 4 | 0x163863C | 0x52800000 | _proc_check_launch_constraints mov w0,#0 |
|
||||
| 5 | 0x1638640 | 0xD65F03C0 | _proc_check_launch_constraints ret |
|
||||
| 6 | 0x12C8138 | 0xD2800020 | _PE_i_can_has_debugger mov x0,#1 |
|
||||
| 7 | 0x12C813C | 0xD65F03C0 | _PE_i_can_has_debugger ret |
|
||||
| 8 | 0x00FFAB98 | 0xD503201F | TXM post-validation NOP (tbnz) |
|
||||
| 9 | 0x16405AC | 0x6B00001F | postValidation cmp w0,w0 |
|
||||
| 10 | 0x16410BC | 0x52800020 | _check_dyld_policy_internal mov w0,#1 (1) |
|
||||
| 11 | 0x16410C8 | 0x52800020 | _check_dyld_policy_internal mov w0,#1 (2) |
|
||||
| 12 | 0x242011C | 0x52800000 | _apfs_graft mov w0,#0 |
|
||||
| 13 | 0x2475044 | 0xEB00001F | _apfs_vfsop_mount cmp x0,x0 |
|
||||
| 14 | 0x2476C00 | 0x52800000 | _apfs_mount_upgrade_checks mov w0,#0 |
|
||||
| 15 | 0x248C800 | 0x52800000 | _handle_fsioc_graft mov w0,#0 |
|
||||
| 16 | 0x23AC528 | 0xD2800000 | _hook_file_check_mmap mov x0,#0 |
|
||||
| 17 | 0x23AC52C | 0xD65F03C0 | _hook_file_check_mmap ret |
|
||||
| 18 | 0x23AAB58 | 0xD2800000 | _hook_mount_check_mount mov x0,#0 |
|
||||
| 19 | 0x23AAB5C | 0xD65F03C0 | _hook_mount_check_mount ret |
|
||||
| 20 | 0x23AA9A0 | 0xD2800000 | _hook_mount_check_remount mov x0,#0 |
|
||||
| 21 | 0x23AA9A4 | 0xD65F03C0 | _hook_mount_check_remount ret |
|
||||
| 22 | 0x23AA80C | 0xD2800000 | _hook_mount_check_umount mov x0,#0 |
|
||||
| 23 | 0x23AA810 | 0xD65F03C0 | _hook_mount_check_umount ret |
|
||||
| 24 | 0x23A5514 | 0xD2800000 | _hook_vnode_check_rename mov x0,#0 |
|
||||
| 25 | 0x23A5518 | 0xD65F03C0 | _hook_vnode_check_rename ret |
|
||||
| # | Offset (hex) | Patch value | Purpose |
|
||||
| --- | -----------: | ----------: | ------------------------------------------ |
|
||||
| 1 | 0x2476964 | 0xD503201F | \_apfs_vfsop_mount root snapshot NOP |
|
||||
| 2 | 0x23CFDE4 | 0xD503201F | \_authapfs_seal_is_broken NOP |
|
||||
| 3 | 0x00F6D960 | 0xD503201F | \_bsd_init rootvp NOP |
|
||||
| 4 | 0x163863C | 0x52800000 | \_proc_check_launch_constraints mov w0,#0 |
|
||||
| 5 | 0x1638640 | 0xD65F03C0 | \_proc_check_launch_constraints ret |
|
||||
| 6 | 0x12C8138 | 0xD2800020 | \_PE_i_can_has_debugger mov x0,#1 |
|
||||
| 7 | 0x12C813C | 0xD65F03C0 | \_PE_i_can_has_debugger ret |
|
||||
| 8 | 0x00FFAB98 | 0xD503201F | TXM post-validation NOP (tbnz) |
|
||||
| 9 | 0x16405AC | 0x6B00001F | postValidation cmp w0,w0 |
|
||||
| 10 | 0x16410BC | 0x52800020 | \_check_dyld_policy_internal mov w0,#1 (1) |
|
||||
| 11 | 0x16410C8 | 0x52800020 | \_check_dyld_policy_internal mov w0,#1 (2) |
|
||||
| 12 | 0x242011C | 0x52800000 | \_apfs_graft mov w0,#0 |
|
||||
| 13 | 0x2475044 | 0xEB00001F | \_apfs_vfsop_mount cmp x0,x0 |
|
||||
| 14 | 0x2476C00 | 0x52800000 | \_apfs_mount_upgrade_checks mov w0,#0 |
|
||||
| 15 | 0x248C800 | 0x52800000 | \_handle_fsioc_graft mov w0,#0 |
|
||||
| 16 | 0x23AC528 | 0xD2800000 | \_hook_file_check_mmap mov x0,#0 |
|
||||
| 17 | 0x23AC52C | 0xD65F03C0 | \_hook_file_check_mmap ret |
|
||||
| 18 | 0x23AAB58 | 0xD2800000 | \_hook_mount_check_mount mov x0,#0 |
|
||||
| 19 | 0x23AAB5C | 0xD65F03C0 | \_hook_mount_check_mount ret |
|
||||
| 20 | 0x23AA9A0 | 0xD2800000 | \_hook_mount_check_remount mov x0,#0 |
|
||||
| 21 | 0x23AA9A4 | 0xD65F03C0 | \_hook_mount_check_remount ret |
|
||||
| 22 | 0x23AA80C | 0xD2800000 | \_hook_mount_check_umount mov x0,#0 |
|
||||
| 23 | 0x23AA810 | 0xD65F03C0 | \_hook_mount_check_umount ret |
|
||||
| 24 | 0x23A5514 | 0xD2800000 | \_hook_vnode_check_rename mov x0,#0 |
|
||||
| 25 | 0x23A5518 | 0xD65F03C0 | \_hook_vnode_check_rename ret |
|
||||
|
||||
## TXM Patch Details
|
||||
|
||||
Dynamic patcher locates the `"TXM [Error]: CodeSignature"` string, finds the
|
||||
following `tbnz` in the log/error path, and NOPs it.
|
||||
|
||||
### vphone600 disassembly around the patch (0xFFAB98)
|
||||
|
||||
Before:
|
||||
|
||||
```
|
||||
0x00FFAB90: mov w0, #5
|
||||
0x00FFAB94: ldrb w8, [x19, #6]
|
||||
0x00FFAB98: tbnz w8, #0, #0xffac80
|
||||
0x00FFAB9C: ldp x29, x30, [sp, #0x100]
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```
|
||||
0x00FFAB90: mov w0, #5
|
||||
0x00FFAB94: ldrb w8, [x19, #6]
|
||||
@@ -102,7 +114,9 @@ After:
|
||||
```
|
||||
|
||||
## vresearch101: Dynamic Patch Run
|
||||
|
||||
Extraction:
|
||||
|
||||
```
|
||||
pyimg4 im4p extract \
|
||||
-i VM/iPhone17,3_26.1_23B85_Restore/kernelcache.research.vresearch101 \
|
||||
@@ -110,11 +124,13 @@ pyimg4 im4p extract \
|
||||
```
|
||||
|
||||
Dynamic patcher results:
|
||||
|
||||
- Patches found/applied: 25
|
||||
- TXM patch location: `0xFA6B98` (NOP `tbnz w8, #0, #0xfa6c80`)
|
||||
- Patched output: `/tmp/kc_vresearch1_dynamic.raw`
|
||||
|
||||
## Conclusion
|
||||
|
||||
For vphone600, the dynamic patcher output is byte-identical to the legacy
|
||||
hardcoded patch list, indicating functional equivalence on this kernelcache.
|
||||
The same dynamic patcher also successfully patches the freshly extracted
|
||||
@@ -10,7 +10,7 @@ keyboard events flow from the macOS host to the virtual iPhone guest.
|
||||
|
||||
There are two pipelines for sending keyboard events to the VM:
|
||||
|
||||
### Pipeline 1: _VZKeyEvent -> sendKeyEvents: (Standard Keys)
|
||||
### Pipeline 1: \_VZKeyEvent -> sendKeyEvents: (Standard Keys)
|
||||
|
||||
```
|
||||
_VZKeyEvent(type, keyCode)
|
||||
@@ -22,7 +22,7 @@ _VZKeyEvent(type, keyCode)
|
||||
-> [fallback] eventSender.sendKeyboardEvents:keyboardID: (VzCore C++ layer)
|
||||
```
|
||||
|
||||
### Pipeline 2: _processHIDReports (Raw HID Reports)
|
||||
### Pipeline 2: \_processHIDReports (Raw HID Reports)
|
||||
|
||||
```
|
||||
Raw HID report bytes
|
||||
@@ -35,7 +35,7 @@ Raw HID report bytes
|
||||
|
||||
---
|
||||
|
||||
## _VZKeyEvent Structure
|
||||
## \_VZKeyEvent Structure
|
||||
|
||||
From IDA + LLDB inspection:
|
||||
|
||||
@@ -52,7 +52,7 @@ Initializer: `_VZKeyEvent(type: Int64, keyCode: UInt16)`
|
||||
|
||||
---
|
||||
|
||||
## _VZKeyboard Object Layout
|
||||
## \_VZKeyboard Object Layout
|
||||
|
||||
From LLDB memory dump:
|
||||
|
||||
@@ -80,7 +80,7 @@ the entry. The lower 32 bits of combined become the intermediate index.
|
||||
### Sample Table 2 Entries
|
||||
|
||||
| Apple VK | Key | Table2 (Index) | HID Page | HID Usage |
|
||||
|----------|---------|----------------|----------|-----------|
|
||||
| -------- | ------- | -------------- | -------- | --------- |
|
||||
| 0x00 | A | 0x00 | 7 | 0x04 |
|
||||
| 0x01 | S | 0x12 | 7 | 0x16 |
|
||||
| 0x24 | Return | 0x24 | 7 | 0x28 |
|
||||
@@ -114,32 +114,32 @@ For type-2 keyboards, the intermediate index is mapped to
|
||||
|
||||
### Standard Keyboard Entries (HID Page 7)
|
||||
|
||||
| Index | HID Page | HID Usage | Meaning |
|
||||
|-----------|----------|-----------|------------------|
|
||||
| 0x00-0x19 | 7 | 4-29 | Letters a-z |
|
||||
| 0x1A-0x23 | 7 | 30-39 | Digits 1-0 |
|
||||
| 0x24 | 7 | 40 | Return |
|
||||
| 0x25 | 7 | 41 | Escape |
|
||||
| 0x29 | 7 | 44 | Space |
|
||||
| 0x48-0x4B | 7 | 79-82 | Arrow keys |
|
||||
| Index | HID Page | HID Usage | Meaning |
|
||||
| --------- | -------- | --------- | -------------------- |
|
||||
| 0x00-0x19 | 7 | 4-29 | Letters a-z |
|
||||
| 0x1A-0x23 | 7 | 30-39 | Digits 1-0 |
|
||||
| 0x24 | 7 | 40 | Return |
|
||||
| 0x25 | 7 | 41 | Escape |
|
||||
| 0x29 | 7 | 44 | Space |
|
||||
| 0x48-0x4B | 7 | 79-82 | Arrow keys |
|
||||
| 0x50-0x53 | 7 | 224-227 | L-Ctrl/Shift/Alt/Cmd |
|
||||
|
||||
### Consumer / System Entries (Non-Standard Pages)
|
||||
|
||||
| Index | HID Page | HID Usage | Meaning |
|
||||
|-------|----------|-----------|---------------------------|
|
||||
| 0x6E | **12** | 671 | **Consumer Volume Down** |
|
||||
| 0x6F | **12** | 674 | **Consumer Volume Up** |
|
||||
| 0x70 | **12** | 207 | **Consumer Play/Pause** |
|
||||
| 0x71 | **12** | 545 | **Consumer Snapshot** |
|
||||
| 0x72 | **1** | 155 | **Generic Desktop Wake** |
|
||||
| Index | HID Page | HID Usage | Meaning |
|
||||
| ----- | -------- | --------- | ------------------------ |
|
||||
| 0x6E | **12** | 671 | **Consumer Volume Down** |
|
||||
| 0x6F | **12** | 674 | **Consumer Volume Up** |
|
||||
| 0x70 | **12** | 207 | **Consumer Play/Pause** |
|
||||
| 0x71 | **12** | 545 | **Consumer Snapshot** |
|
||||
| 0x72 | **1** | 155 | **Generic Desktop Wake** |
|
||||
|
||||
**Home/Menu (Consumer page 0x0C, usage 0x40) has NO intermediate index.** It cannot
|
||||
be sent through Pipeline 1 at all.
|
||||
|
||||
---
|
||||
|
||||
## _processHIDReports Parameter Format
|
||||
## \_processHIDReports Parameter Format
|
||||
|
||||
From IDA decompilation of
|
||||
`VZVirtualMachine._processHIDReports:forDevice:deviceType:` at 0x2301b2310.
|
||||
@@ -193,7 +193,7 @@ or the framework will dereference invalid pointers.
|
||||
|
||||
## Swift Implementation Notes
|
||||
|
||||
### Accessing _VZKeyboard
|
||||
### Accessing \_VZKeyboard
|
||||
|
||||
```swift
|
||||
// Get keyboards array
|
||||
@@ -215,7 +215,7 @@ withUnsafeMutablePointer(to: &vec) { vecPtr in
|
||||
}
|
||||
```
|
||||
|
||||
### Constructing vector<span<unsigned char>> for _processHIDReports
|
||||
### Constructing vector<span<unsigned char>> for \_processHIDReports
|
||||
|
||||
```swift
|
||||
let reportPtr = UnsafeMutablePointer<UInt8>.allocate(capacity: N)
|
||||
@@ -242,10 +242,10 @@ Dynamic(vm)._processHIDReports(UnsafeRawPointer(vecPtr), forDevice: deviceId, de
|
||||
|
||||
### Key Functions Analyzed
|
||||
|
||||
| Function | Address |
|
||||
|----------|---------|
|
||||
| `-[_VZKeyboard sendKeyEvents:]` | 0x2301b2f54 |
|
||||
| `-[_VZKeyboard sendKeyboardEventsHIDReport:keyboardID:]` | 0x2301b3230 |
|
||||
| Function | Address |
|
||||
| ------------------------------------------------------------------------------- | ----------- |
|
||||
| `-[_VZKeyboard sendKeyEvents:]` | 0x2301b2f54 |
|
||||
| `-[_VZKeyboard sendKeyboardEventsHIDReport:keyboardID:]` | 0x2301b3230 |
|
||||
| `-[VZVirtualMachine(_VZHIDAdditions) _processHIDReports:forDevice:deviceType:]` | 0x2301b2310 |
|
||||
| `-[VZVirtualMachineView _sendKeyEventsToVirtualMachine:]` | -- |
|
||||
| `-[_VZHIDEventMonitor getHIDReportsFromHIDEvent:]` | 0x2301b2af0 |
|
||||
| `-[VZVirtualMachineView _sendKeyEventsToVirtualMachine:]` | -- |
|
||||
| `-[_VZHIDEventMonitor getHIDReportsFromHIDEvent:]` | 0x2301b2af0 |
|
||||
285
research/patch_comparison_all_variants.md
Normal file
285
research/patch_comparison_all_variants.md
Normal file
@@ -0,0 +1,285 @@
|
||||
# Patch Comparison: Regular / Development / Jailbreak
|
||||
|
||||
Three firmware variants are available, each building on the previous:
|
||||
|
||||
- **Regular** (`make fw_patch` + `make cfw_install`) — Minimal patches for VM boot with signature bypass and SSV override.
|
||||
- **Development** (`make fw_patch_dev` + `make cfw_install_dev`) — Regular + TXM entitlement/developer-mode bypasses + launchd jetsam fix. Enables debugging and code signing flexibility without full jailbreak.
|
||||
- **Jailbreak** (`make fw_patch_jb` + `make cfw_install_jb`) — Regular + comprehensive security bypass across iBSS, TXM, kernel, and userland. Full code execution, sandbox escape, and package management.
|
||||
|
||||
## Boot Chain Patches
|
||||
|
||||
### AVPBooter
|
||||
|
||||
| # | Patch | Purpose | Regular | Dev | JB |
|
||||
| --- | ------------ | -------------------------------- | :-----: | :-: | :-: |
|
||||
| 1 | `mov x0, #0` | DGST signature validation bypass | Y | Y | Y |
|
||||
|
||||
### iBSS
|
||||
|
||||
| # | Patch | Purpose | Regular | Dev | JB |
|
||||
| --- | --------------------------------- | ----------------------------------------------------------- | :-----: | :-: | :-: |
|
||||
| 1 | Serial labels (2x) | "Loaded iBSS" in serial log | Y | Y | Y |
|
||||
| 2 | image4_validate_property_callback | Signature bypass (`b.ne` → NOP, `mov x0,x22` → `mov x0,#0`) | Y | Y | Y |
|
||||
| 3 | Skip generate_nonce | Keep apnonce stable for SHSH (`tbz` → unconditional `b`) | — | — | Y |
|
||||
|
||||
### iBEC
|
||||
|
||||
| # | Patch | Purpose | Regular | Dev | JB |
|
||||
| --- | --------------------------------- | ----------------------------------------- | :-----: | :-: | :-: |
|
||||
| 1 | Serial labels (2x) | "Loaded iBEC" in serial log | Y | Y | Y |
|
||||
| 2 | image4_validate_property_callback | Signature bypass | Y | Y | Y |
|
||||
| 3 | Boot-args redirect | ADRP+ADD → `serial=3 -v debug=0x2014e %s` | Y | Y | Y |
|
||||
|
||||
### LLB
|
||||
|
||||
| # | Patch | Purpose | Regular | Dev | JB |
|
||||
| --- | --------------------------------- | ----------------------------------------- | :-----: | :-: | :-: |
|
||||
| 1 | Serial labels (2x) | "Loaded LLB" in serial log | Y | Y | Y |
|
||||
| 2 | image4_validate_property_callback | Signature bypass | Y | Y | Y |
|
||||
| 3 | Boot-args redirect | ADRP+ADD → `serial=3 -v debug=0x2014e %s` | Y | Y | Y |
|
||||
| 4 | Rootfs bypass (5 patches) | Allow edited rootfs loading | Y | Y | Y |
|
||||
| 5 | Panic bypass | NOP `cbnz` after `mov w8,#0x328` check | Y | Y | Y |
|
||||
|
||||
### TXM
|
||||
|
||||
The three variants use different TXM patchers. Regular uses `txm.py` (1 patch), Dev uses `txm_dev.py` (10 patches), JB uses `txm_jb.py` (12 patches).
|
||||
|
||||
| # | Patch | Purpose | Regular | Dev | JB |
|
||||
| --- | ------------------------------------------------- | ----------------------------------------------------------- | :-----: | :-: | :-: |
|
||||
| 1 | Trustcache binary-search bypass | `bl hash_cmp` → `mov x0, #0` | Y | Y | Y |
|
||||
| 2 | Selector24 hash extraction: NOP LDR X1 | Bypass CS hash flag extraction | — | — | Y |
|
||||
| 3 | Selector24 hash extraction: NOP BL | Bypass CS hash flag check | — | — | Y |
|
||||
| 4 | get-task-allow (selector 41\|29) | `bl` → `mov x0, #1` — allow get-task-allow | — | Y | Y |
|
||||
| 5 | Selector42\|29 shellcode: branch to cave | Redirect dispatch stub to shellcode | — | Y | Y |
|
||||
| 6 | Selector42\|29 shellcode: NOP pad | UDF → NOP in code cave | — | Y | Y |
|
||||
| 7 | Selector42\|29 shellcode: `mov x0, #1` | Set return value to true | — | Y | Y |
|
||||
| 8 | Selector42\|29 shellcode: `strb w0, [x20, #0x30]` | Set manifest flag | — | Y | Y |
|
||||
| 9 | Selector42\|29 shellcode: `mov x0, x20` | Restore context pointer | — | Y | Y |
|
||||
| 10 | Selector42\|29 shellcode: branch back | Return from shellcode to stub+4 | — | Y | Y |
|
||||
| 11 | Debugger entitlement (selector 42\|37) | `bl` → `mov w0, #1` — allow `com.apple.private.cs.debugger` | — | Y | Y |
|
||||
| 12 | Developer mode bypass | NOP conditional guard before deny path | — | Y | Y |
|
||||
|
||||
### Kernelcache
|
||||
|
||||
Regular and Dev share the same 25 base kernel patches. JB adds 34 additional patches.
|
||||
|
||||
#### Base patches (all variants)
|
||||
|
||||
| # | Patch | Function | Purpose | Regular | Dev | JB |
|
||||
| ----- | -------------------------- | -------------------------------- | ---------------------------------------- | :-----: | :-: | :-: |
|
||||
| 1 | NOP `tbnz w8,#5` | `_apfs_vfsop_mount` | Skip "root snapshot" sealed volume check | Y | Y | Y |
|
||||
| 2 | NOP conditional | `_authapfs_seal_is_broken` | Skip "root volume seal" panic | Y | Y | Y |
|
||||
| 3 | NOP conditional | `_bsd_init` | Skip "rootvp not authenticated" panic | Y | Y | Y |
|
||||
| 4–5 | `mov w0,#0; ret` | `_proc_check_launch_constraints` | Bypass launch constraints | Y | Y | Y |
|
||||
| 6–7 | `mov x0,#1` (2x) | `PE_i_can_has_debugger` | Enable kernel debugger | Y | Y | Y |
|
||||
| 8 | NOP | `_postValidation` | Skip AMFI post-validation | Y | Y | Y |
|
||||
| 9 | `cmp w0,w0` | `_postValidation` | Force comparison true | Y | Y | Y |
|
||||
| 10–11 | `mov w0,#1` (2x) | `_check_dyld_policy_internal` | Allow dyld loading | Y | Y | Y |
|
||||
| 12 | `mov w0,#0` | `_apfs_graft` | Allow APFS graft | Y | Y | Y |
|
||||
| 13 | `cmp x0,x0` | `_apfs_vfsop_mount` | Skip mount check | Y | Y | Y |
|
||||
| 14 | `mov w0,#0` | `_apfs_mount_upgrade_checks` | Allow mount upgrade | Y | Y | Y |
|
||||
| 15 | `mov w0,#0` | `_handle_fsioc_graft` | Allow fsioc graft | Y | Y | Y |
|
||||
| 16–25 | `mov x0,#0; ret` (5 hooks) | Sandbox MACF ops table | Stub 5 sandbox hooks | Y | Y | Y |
|
||||
|
||||
#### JB-only kernel patches
|
||||
|
||||
| # | Patch | Function | Purpose | Regular | Dev | JB |
|
||||
| --- | ---------------------------- | ------------------------------------ | ------------------------------------------ | :-----: | :-: | :-: |
|
||||
| 26 | Function rewrite | `AMFIIsCDHashInTrustCache` | Always return true + store hash | — | — | Y |
|
||||
| 27 | Shellcode + branch | `_cred_label_update_execve` | Set cs_flags (platform+entitlements) | — | — | Y |
|
||||
| 28 | `cmp w0,w0` | `_postValidation` (additional) | Force validation pass | — | — | Y |
|
||||
| 29 | Shellcode + branch | `_syscallmask_apply_to_proc` | Patch zalloc_ro_mut for syscall mask | — | — | Y |
|
||||
| 30 | Shellcode + ops redirect | `_hook_cred_label_update_execve` | vnode_getattr ownership + suid propagation | — | — | Y |
|
||||
| 31 | `mov x0,#0; ret` (20+ hooks) | Sandbox MACF ops (extended) | Stub remaining 20+ sandbox hooks | — | — | Y |
|
||||
| 32 | `cmp xzr,xzr` | `_task_conversion_eval_internal` | Allow task conversion | — | — | Y |
|
||||
| 33 | `mov x0,#0; ret` | `_proc_security_policy` | Bypass security policy | — | — | Y |
|
||||
| 34 | NOP (2x) | `_proc_pidinfo` | Allow pid 0 info | — | — | Y |
|
||||
| 35 | `b` (skip panic) | `_convert_port_to_map_with_flavor` | Skip kernel map panic | — | — | Y |
|
||||
| 36 | NOP | `_vm_fault_enter_prepare` | Skip fault check | — | — | Y |
|
||||
| 37 | `b` (skip check) | `_vm_map_protect` | Allow VM protect | — | — | Y |
|
||||
| 38 | NOP + `mov x8,xzr` | `___mac_mount` | Bypass MAC mount check | — | — | Y |
|
||||
| 39 | NOP | `_dounmount` | Allow unmount | — | — | Y |
|
||||
| 40 | `mov x0,#0` | `_bsd_init` (2nd) | Skip auth at @%s:%d | — | — | Y |
|
||||
| 41 | NOP (2x) | `_spawn_validate_persona` | Skip persona validation | — | — | Y |
|
||||
| 42 | NOP | `_task_for_pid` | Allow task_for_pid | — | — | Y |
|
||||
| 43 | `b` (skip check) | `_load_dylinker` | Allow dylinker loading | — | — | Y |
|
||||
| 44 | `cmp x0,x0` | `_shared_region_map_and_slide_setup` | Force shared region | — | — | Y |
|
||||
| 45 | NOP BL | `_verifyPermission` (NVRAM) | Allow NVRAM writes | — | — | Y |
|
||||
| 46 | `b` (skip check) | `_IOSecureBSDRoot` | Skip secure root check | — | — | Y |
|
||||
| 47 | Syscall 439 + shellcode | kcall10 (`SYS_kas_info` replacement) | Kernel arbitrary call from userspace | — | — | Y |
|
||||
| 48 | Zero out | `_thid_should_crash` | Prevent GUARD_TYPE_MACH_PORT crash | — | — | Y |
|
||||
|
||||
## CFW Installation Patches
|
||||
|
||||
### Binary patches applied over SSH ramdisk
|
||||
|
||||
| # | Patch | Binary | Purpose | Regular | Dev | JB |
|
||||
| --- | ----------------------- | -------------------- | ----------------------------------------- | :-----: | :-: | :-: |
|
||||
| 1 | `/%s.gl` → `/AA.gl` | seputil | Gigalocker UUID fix | Y | Y | Y |
|
||||
| 2 | NOP cache validation | launchd_cache_loader | Allow modified launchd.plist | Y | Y | Y |
|
||||
| 3 | `mov x0,#1; ret` | mobileactivationd | Activation bypass | Y | Y | Y |
|
||||
| 4 | Plist injection | launchd.plist | bash/dropbear/trollvnc/vphoned daemons | Y | Y | Y |
|
||||
| 5 | `b` (skip jetsam guard) | launchd | Prevent jetsam panic on boot | — | Y | Y |
|
||||
| 6 | LC_LOAD_DYLIB injection | launchd | Load `/cores/launchdhook.dylib` at launch | — | — | Y |
|
||||
|
||||
### Installed components
|
||||
|
||||
| # | Component | Description | Regular | Dev | JB |
|
||||
| --- | ------------------------ | ----------------------------------------------------------------- | :-----: | :-: | :-: |
|
||||
| 1 | Cryptex SystemOS + AppOS | Decrypt AEA + mount + copy to device | Y | Y | Y |
|
||||
| 2 | GPU driver | AppleParavirtGPUMetalIOGPUFamily bundle | Y | Y | Y |
|
||||
| 3 | iosbinpack64 | Jailbreak tools (base set) | Y | Y | Y |
|
||||
| 4 | iosbinpack64 dev overlay | Replace `rpcserver_ios` with dev build | — | Y | — |
|
||||
| 5 | vphoned | vsock HID/control daemon (built + signed) | Y | Y | Y |
|
||||
| 6 | LaunchDaemons | bash, dropbear, trollvnc, rpcserver_ios, vphoned plists | Y | Y | Y |
|
||||
| 7 | Procursus bootstrap | Bootstrap filesystem + optional Sileo deb | — | — | Y |
|
||||
| 8 | BaseBin hooks | systemhook.dylib, launchdhook.dylib, libellekit.dylib → `/cores/` | — | — | Y |
|
||||
|
||||
## Summary
|
||||
|
||||
| Component | Regular | Dev | JB |
|
||||
| ------------------------ | :-----: | :----: | :----: |
|
||||
| AVPBooter | 1 | 1 | 1 |
|
||||
| iBSS | 2 | 2 | 3 |
|
||||
| iBEC | 3 | 3 | 3 |
|
||||
| LLB | 6 | 6 | 6 |
|
||||
| TXM | 1 | 10 | 12 |
|
||||
| Kernel | 25 | 25 | 59 |
|
||||
| **Boot chain total** | **38** | **47** | **84** |
|
||||
| | | | |
|
||||
| CFW binary patches | 4 | 5 | 6 |
|
||||
| CFW installed components | 6 | 7 | 8 |
|
||||
| **CFW total** | **10** | **12** | **14** |
|
||||
| | | | |
|
||||
| **Grand total** | **48** | **59** | **98** |
|
||||
|
||||
### What each variant adds
|
||||
|
||||
**Regular → Dev** (+11 patches):
|
||||
|
||||
- TXM: +9 patches (get-task-allow, selector42|29 shellcode, debugger entitlement, developer mode bypass)
|
||||
- CFW: +1 binary patch (launchd jetsam), +1 component (dev rpcserver_ios overlay)
|
||||
|
||||
**Regular → JB** (+50 patches):
|
||||
|
||||
- iBSS: +1 (nonce skip)
|
||||
- TXM: +11 (hash extraction NOP, get-task-allow, selector42|29 shellcode, debugger entitlement, dev mode bypass)
|
||||
- Kernel: +34 (trustcache, execve, sandbox, task/VM, memory, kcall10)
|
||||
- CFW: +2 binary patches (launchd jetsam + dylib injection), +2 components (procursus + BaseBin hooks)
|
||||
|
||||
## JB Install Flow (`make cfw_install_jb`)
|
||||
|
||||
- Entry: `scripts/cfw_install_jb.sh` runs `scripts/cfw_install.sh` with `CFW_SKIP_HALT=1`, then continues with JB phases.
|
||||
- Added JB phases in install pipeline:
|
||||
- `JB-1`: patch `/mnt1/sbin/launchd` via `inject-dylib` (adds `/cores/launchdhook.dylib` LC_LOAD_DYLIB) + `patch-launchd-jetsam` (dynamic string+xref).
|
||||
- `JB-2`: unpack procursus bootstrap (`bootstrap-iphoneos-arm64.tar.zst`) into `/mnt5/<bootManifestHash>/jb-vphone/procursus`.
|
||||
- `JB-3`: deploy BaseBin hook dylibs (`systemhook.dylib`, `launchdhook.dylib`, `libellekit.dylib`) to `/mnt1/cores/`, re-signed with ldid + signcert.p12.
|
||||
- JB resources now packaged in:
|
||||
- `scripts/resources/cfw_jb_input.tar.zst`
|
||||
- contains:
|
||||
- `jb/bootstrap-iphoneos-arm64.tar.zst`
|
||||
- `jb/org.coolstar.sileo_2.5.1_iphoneos-arm64.deb`
|
||||
- `basebin/*.dylib` (BaseBin hooks for JB-3)
|
||||
|
||||
## Dynamic Implementation Log (JB Patchers)
|
||||
|
||||
### TXM (`txm_jb.py`)
|
||||
|
||||
All TXM JB patches are implemented with dynamic binary analysis and
|
||||
keystone/capstone-encoded instructions only.
|
||||
|
||||
1. `selector24 A1` (2x nop: LDR + BL)
|
||||
- Locator: unique guarded `mov w0,#0xa1` site, scan for `ldr x1,[xN,#0x38] ; add x2 ; bl ; ldp` pattern.
|
||||
- Patch bytes: keystone `nop` on the LDR and the BL.
|
||||
2. `selector41/29 get-task-allow`
|
||||
- Locator: xref to `"get-task-allow"` + nearby `bl` followed by `tbnz w0,#0`.
|
||||
- Patch bytes: keystone `mov x0, #1`.
|
||||
3. `selector42/29 shellcode trampoline`
|
||||
- Locator:
|
||||
- Find dispatch stub pattern `bti j ; mov x0,x20 ; bl ; mov x1,x21 ; mov x2,x22 ; bl ; b`.
|
||||
- Select stub whose second `bl` target is the debugger-gate function (pattern verified by string-xref + call-shape).
|
||||
- Find executable UDF cave dynamically.
|
||||
- Patch bytes:
|
||||
- Stub head -> keystone `b #cave`.
|
||||
- Cave payload -> `nop ; mov x0,#1 ; strb w0,[x20,#0x30] ; mov x0,x20 ; b #return`.
|
||||
4. `selector42/37 debugger entitlement`
|
||||
- Locator: xref to `"com.apple.private.cs.debugger"` + strict nearby call-shape
|
||||
(`mov x0,#0 ; mov x2,#0 ; bl ; tbnz w0,#0`).
|
||||
- Patch bytes: keystone `mov w0, #1`.
|
||||
5. `developer mode bypass`
|
||||
- Locator: xref to `"developer mode enabled due to system policy configuration"`
|
||||
- nearest guard branch on `w9`.
|
||||
- Patch bytes: keystone `nop`.
|
||||
|
||||
#### TXM Binary-Alignment Validation
|
||||
|
||||
- `patch.upstream.raw` generated from upstream-equivalent TXM static patch semantics.
|
||||
- `patch.dyn.raw` generated by `TXMJBPatcher` on the same input.
|
||||
- Result: byte-identical (`cmp -s` success, SHA-256 matched).
|
||||
|
||||
### Kernelcache (`kernel_jb.py`)
|
||||
|
||||
All 24 kernel JB patch methods are implemented in `scripts/patchers/kernel_jb.py`
|
||||
with capstone semantic matching and keystone-generated patch bytes only:
|
||||
|
||||
**Group A: Core patches**
|
||||
|
||||
1. `AMFIIsCDHashInTrustCache` function rewrite
|
||||
- Locator: semantic function-body matcher in AMFI text.
|
||||
- Patch: `mov x0,#1 ; cbz x2,+8 ; str x0,[x2] ; ret`.
|
||||
2. AMFI execve kill path bypass (2 BL sites)
|
||||
- Locator: string xref to `"AMFI: hook..execve() killing"` (fallback `"execve() killing"`),
|
||||
then function-local early `bl` + `cbz/cbnz w0` pair matcher.
|
||||
- Patch: `bl -> mov x0,#0` at two helper callsites.
|
||||
3. `task_conversion_eval_internal` guard bypass
|
||||
- Locator: unique cmp/branch motif:
|
||||
`ldr xN,[xN,#imm] ; cmp xN,x0 ; b.eq ; cmp xN,x1 ; b.eq`.
|
||||
- Patch: `cmp xN,x0 -> cmp xzr,xzr`.
|
||||
4. Extended sandbox MACF hook stubs (25 hooks, JB-only set)
|
||||
- Locator: dynamic `mac_policy_conf -> mpc_ops` discovery, then hook-index resolution.
|
||||
- Patch per hook function: `mov x0,#0 ; ret`.
|
||||
- JB extended indices include vnode/proc hooks beyond base 5 hooks.
|
||||
|
||||
**Group B: Simple patches (string-anchored / pattern-matched)**
|
||||
|
||||
5. `_postValidation` additional CMP bypass
|
||||
6. `_proc_security_policy` stub (mov x0,#0; ret)
|
||||
7. `_proc_pidinfo` pid-0 guard NOP (2 sites)
|
||||
8. `_convert_port_to_map_with_flavor` panic skip
|
||||
9. `_vm_fault_enter_prepare` PMAP check NOP
|
||||
10. `_vm_map_protect` permission check skip
|
||||
11. `___mac_mount` MAC check bypass (NOP + mov x8,xzr)
|
||||
12. `_dounmount` MAC check NOP
|
||||
13. `_bsd_init` auth bypass (mov x0,#0)
|
||||
14. `_spawn_validate_persona` NOP (2 sites)
|
||||
15. `_task_for_pid` proc_ro security copy NOP
|
||||
16. `_load_dylinker` PAC rebase bypass
|
||||
17. `_shared_region_map_and_slide_setup` force (cmp x0,x0)
|
||||
18. `_verifyPermission` (NVRAM) NOP
|
||||
19. `_IOSecureBSDRoot` check skip
|
||||
20. `_thid_should_crash` zero out
|
||||
|
||||
**Group C: Complex shellcode patches**
|
||||
|
||||
21. `_cred_label_update_execve` cs_flags shellcode
|
||||
22. `_syscallmask_apply_to_proc` filter mask shellcode
|
||||
23. `_hook_cred_label_update_execve` ops table + vnode_getattr shellcode
|
||||
24. `kcall10` syscall 439 replacement shellcode
|
||||
|
||||
## Cross-Version Dynamic Snapshot
|
||||
|
||||
Validated using pristine inputs from `updates-cdn/`:
|
||||
|
||||
| Case | TXM_JB_PATCHES | KERNEL_JB_PATCHES |
|
||||
| ------------------- | -------------: | ----------------: |
|
||||
| PCC 26.1 (`23B85`) | 14 | 59 |
|
||||
| PCC 26.3 (`23D128`) | 14 | 59 |
|
||||
| iOS 26.1 (`23B85`) | 14 | 59 |
|
||||
| iOS 26.3 (`23D127`) | 14 | 59 |
|
||||
|
||||
> Note: These emit counts were captured at validation time and may differ from
|
||||
> the current source if methods were subsequently refactored. The TXM JB patcher
|
||||
> currently has 5 methods emitting 11 patches; the kernel JB patcher has 24
|
||||
> methods. Actual emit counts depend on how many dynamic targets resolve per binary.
|
||||
|
||||
All patches are applied dynamically via string anchors, instruction patterns, and cross-reference analysis — no hardcoded offsets — ensuring portability across iOS versions.
|
||||
711
research/txm_jb_patches.md
Normal file
711
research/txm_jb_patches.md
Normal file
@@ -0,0 +1,711 @@
|
||||
# TXM Jailbreak Patch Analysis
|
||||
|
||||
Analysis of 6 logical TXM jailbreak patches (11 instruction modifications) applied by `txm_jb.py` on the RESEARCH variant
|
||||
of TXM from iPhone17,3 / PCC-CloudOS 26.x.
|
||||
|
||||
## TXM Execution Model
|
||||
|
||||
TXM runs at a guested exception level (GL) under SPTM's supervision:
|
||||
|
||||
```
|
||||
SPTM (GL2) — Secure Page Table Monitor
|
||||
↕ svc #0
|
||||
TXM (GL1) — Trusted Execution Monitor
|
||||
↕ trap
|
||||
Kernel (EL1/GL0)
|
||||
```
|
||||
|
||||
SPTM dispatches selector calls into TXM. TXM **cannot** execute SPTM code
|
||||
(instruction fetch permission fault). TXM must return to SPTM via `svc #0`.
|
||||
|
||||
---
|
||||
|
||||
## Return Path: TXM → SPTM
|
||||
|
||||
All TXM functions return through this chain:
|
||||
|
||||
```
|
||||
TXM function
|
||||
→ bl return_helper (0x26c04)
|
||||
→ bl return_trap_stub (0x49b40)
|
||||
→ movk x16, ... (set SPTM return code)
|
||||
→ b trampoline (0x60000)
|
||||
→ pacibsp
|
||||
→ svc #0 ← traps to SPTM
|
||||
→ retab ← resumes after SPTM returns
|
||||
```
|
||||
|
||||
### Trampoline at 0x60000 (`__TEXT_BOOT_EXEC`)
|
||||
|
||||
```asm
|
||||
0x060000: pacibsp
|
||||
0x060004: svc #0 ; supervisor call → SPTM handles the trap
|
||||
0x060008: retab ; return after SPTM gives control back
|
||||
```
|
||||
|
||||
### Return Trap Stub at 0x49B40 (`__TEXT_EXEC`)
|
||||
|
||||
```asm
|
||||
0x049B40: bti c
|
||||
0x049B44: movk x16, #0, lsl #48
|
||||
0x049B48: movk x16, #0xfd, lsl #32 ; x16 = 0x000000FD00000000
|
||||
0x049B4C: movk x16, #0, lsl #16 ; (SPTM return code identifier)
|
||||
0x049B50: movk x16, #0
|
||||
0x049B54: b #0x60000 ; → trampoline → svc #0
|
||||
```
|
||||
|
||||
x16 carries a return code that SPTM uses to identify which TXM operation completed.
|
||||
|
||||
### Return Helper at 0x26C04 (`__TEXT_EXEC`)
|
||||
|
||||
```asm
|
||||
0x026C04: pacibsp
|
||||
0x026C08: stp x20, x19, [sp, #-0x20]!
|
||||
0x026C0C: stp x29, x30, [sp, #0x10]
|
||||
0x026C10: add x29, sp, #0x10
|
||||
0x026C14: mov x19, x0 ; save result code
|
||||
0x026C18: bl #0x29024 ; get TXM context
|
||||
0x026C1C: ldrb w8, [x0] ; check context flag
|
||||
0x026C20: cbz w8, #0x26c30
|
||||
0x026C24: mov x20, x0
|
||||
0x026C28: bl #0x29010 ; cleanup if flag set
|
||||
0x026C2C: strb wzr, [x20, #0x58]
|
||||
0x026C30: mov x0, x19 ; restore result
|
||||
0x026C34: bl #0x49b40 ; → svc #0 → SPTM
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handler
|
||||
|
||||
### Error Handler at 0x25924
|
||||
|
||||
```asm
|
||||
0x025924: pacibsp
|
||||
...
|
||||
0x025978: stp x19, x20, [sp] ; x19=error_code, x20=param
|
||||
0x02597C: adrp x0, #0x1000
|
||||
0x025980: add x0, x0, #0x8d8 ; format string
|
||||
0x025984: bl #0x25744 ; log error → eventually svc #0
|
||||
```
|
||||
|
||||
### Panic Format
|
||||
|
||||
`TXM [Error]: CodeSignature: selector: 24 | 0xA1 | 0x30 | 1`
|
||||
|
||||
This is a **kernel-side** message (not in TXM binary). The kernel receives the
|
||||
non-zero return from the `svc #0` trap and formats the error:
|
||||
`selector: <selector_num> | <low_byte> | <mid_byte> | <high_byte>`
|
||||
|
||||
For `0x000130A1`: low=`0xA1`, mid=`0x30`, high=`0x1` → `| 0xA1 | 0x30 | 1`
|
||||
|
||||
---
|
||||
|
||||
## Why `ret`/`retab` Fails
|
||||
|
||||
> **Historical reference:** These three failed attempts document why patching TXM
|
||||
> functions to return early via normal return instructions does not work. The only
|
||||
> viable path is to let the function proceed through the normal `svc #0` return chain
|
||||
> (see [Return Path](#return-path-txm--sptm) above).
|
||||
|
||||
### Attempt 1: `mov x0, #0; retab` replacing PACIBSP
|
||||
|
||||
```
|
||||
0x026C80: mov x0, #0 ; (was pacibsp)
|
||||
0x026C84: retab ; verify PAC on LR → FAIL
|
||||
```
|
||||
|
||||
**Result**: `[TXM] Unhandled synchronous exception at pc 0x...6C84`
|
||||
|
||||
RETAB tries to verify the PAC signature on LR. Since PACIBSP was replaced,
|
||||
LR was never signed. RETAB detects the invalid PAC → exception.
|
||||
|
||||
### Attempt 2: `mov x0, #0; ret` replacing PACIBSP
|
||||
|
||||
```
|
||||
0x026C80: mov x0, #0 ; (was pacibsp)
|
||||
0x026C84: ret ; jump to LR (SPTM address)
|
||||
```
|
||||
|
||||
**Result**: `[TXM] Unhandled synchronous exception at pc 0x...FA88` (SPTM space)
|
||||
|
||||
`ret` strips PAC and jumps to clean LR, which points to SPTM code (the caller).
|
||||
TXM cannot execute SPTM code → **instruction fetch permission fault** (ESR EC=0x20, IFSC=0xF).
|
||||
|
||||
### Attempt 3: `pacibsp; mov x0, #0; retab`
|
||||
|
||||
```
|
||||
0x026C80: pacibsp ; signs LR correctly
|
||||
0x026C84: mov x0, #0
|
||||
0x026C88: retab ; verifies PAC (OK), jumps to LR (SPTM address)
|
||||
```
|
||||
|
||||
**Result**: Same permission fault — RETAB succeeds (PAC valid), but the return
|
||||
address is in SPTM space. TXM still cannot execute there.
|
||||
|
||||
**Conclusion**: No form of `ret`/`retab` works because the **caller is SPTM**
|
||||
and TXM cannot return to SPTM via normal returns. The only way back is `svc #0`.
|
||||
|
||||
---
|
||||
|
||||
## Address Mapping
|
||||
|
||||
| Segment | VM Address | File Offset | Size |
|
||||
| ------------------ | -------------------- | ----------- | --------- |
|
||||
| `__TEXT_EXEC` | `0xFFFFFFF017020000` | `0x1c000` | `0x44000` |
|
||||
| `__TEXT_BOOT_EXEC` | `0xFFFFFFF017064000` | `0x60000` | `0xc000` |
|
||||
|
||||
Conversion: `VA = file_offset - 0x1c000 + 0xFFFFFFF017020000` (for `__TEXT_EXEC`)
|
||||
|
||||
---
|
||||
|
||||
## TXM Selector Dispatch
|
||||
|
||||
All TXM operations enter through a single dispatch function (`sub_FFFFFFF01702AE80`),
|
||||
a large switch on the selector number (1-51). Each case validates arguments and calls
|
||||
a dedicated handler. Relevant selectors:
|
||||
|
||||
| Selector | Handler | Purpose |
|
||||
| -------- | ----------------------------------------- | ------------------------------------------------- |
|
||||
| 24 | `sub_FFFFFFF017024834` → validation chain | CodeSignature validation |
|
||||
| 41 | `sub_FFFFFFF017023558` | Process entitlement setup (get-task-allow) |
|
||||
| 42 | `sub_FFFFFFF017023368` | Debug memory mapping |
|
||||
| --- | `sub_FFFFFFF017023A20` | Developer mode configuration (called during init) |
|
||||
|
||||
The dispatcher passes raw page pointers through `sub_FFFFFFF0170280A4` (a bounds
|
||||
validator that returns the input pointer unchanged) before calling handlers.
|
||||
|
||||
---
|
||||
|
||||
## Patch 1-2: CodeSignature Hash Comparison Bypass (selector 24)
|
||||
|
||||
**Error**: `TXM [Error]: CodeSignature: selector: 24 | 0xA1 | 0x30 | 1`
|
||||
|
||||
### Addresses
|
||||
|
||||
| File Offset | VA | Original Instruction | Patch |
|
||||
| ----------- | -------------------- | ------------------------- | ----- |
|
||||
| `0x313ec` | `0xFFFFFFF0170353EC` | `LDR X1, [X20, #0x38]` | NOP |
|
||||
| `0x313f4` | `0xFFFFFFF0170353F4` | `BL sub_FFFFFFF0170335F8` | NOP |
|
||||
|
||||
### selector24 Full Control Flow (0x026C80 - 0x026E7C)
|
||||
|
||||
The selector 24 handler is the entry point for all CodeSignature validation.
|
||||
Understanding its full control flow is essential context for why the NOP approach
|
||||
was chosen over earlier redirect-based patches.
|
||||
|
||||
```
|
||||
0x026C80: pacibsp ; prologue
|
||||
0x026C84: sub sp, sp, #0x70
|
||||
0x026C88-0x026C98: save x19-x30, setup fp
|
||||
|
||||
0x026CA0-0x026CB4: save args to x20-x25
|
||||
0x026CB8: bl #0x29024 ; get TXM context → x0
|
||||
0x026CBC: adrp x8, #0x6c000 ; flag address (page)
|
||||
0x026CC0: add x8, x8, #0x5c0 ; flag address (offset)
|
||||
0x026CC4: ldrb w8, [x8] ; flag check
|
||||
0x026CC8: cbnz w8, #0x26cfc ; if flag → error 0xA0
|
||||
0x026CCC: mov x19, x0 ; save context
|
||||
|
||||
0x026CD4: cmp w25, #2 ; switch on sub-selector (arg0)
|
||||
b.gt → check 3,4,5
|
||||
0x026CDC: cbz w25 → case 0
|
||||
0x026CE4: b.eq → case 1
|
||||
0x026CEC: b.ne → default (0xA1 error)
|
||||
|
||||
case 0: setup, b 0x26dc0
|
||||
case 1: flag check, setup, b 0x26dfc
|
||||
case 2: bl 0x1e0e8, b 0x26db8
|
||||
case 3: bl 0x1e148, b 0x26db8
|
||||
case 4: bl 0x1e568, b 0x26db8
|
||||
case 5: flag → { mov x0,#0; b 0x26db8 } or { bl 0x1e70c; b 0x26db8 }
|
||||
default: mov w0, #0xa1; b 0x26d00 (error path)
|
||||
|
||||
0x026DB8: and w8, w0, #0xffff ; result processing
|
||||
0x026DBC: and x9, x0, #0xffffffffffff0000
|
||||
0x026DC0: mov w10, w8
|
||||
0x026DC4: orr x9, x9, x10
|
||||
0x026DC8: str x9, [x19, #8] ; store result to context
|
||||
0x026DCC: cmp w8, #0
|
||||
0x026DD0: csetm x0, ne ; x0 = 0 (success) or -1 (error)
|
||||
0x026DD4: bl #0x26c04 ; return via svc #0 ← SUCCESS RETURN
|
||||
|
||||
ERROR PATH:
|
||||
0x026D00: mov w1, #0
|
||||
0x026D04: bl #0x25924 ; error handler → svc #0
|
||||
```
|
||||
|
||||
#### Existing Success Path
|
||||
|
||||
The function already has a success path at `0x026D30` (reached by case 5 when flag is set):
|
||||
|
||||
```asm
|
||||
0x026D30: mov x0, #0 ; success result
|
||||
0x026D34: b #0x26db8 ; → process result → str [x19,#8] → bl return_helper
|
||||
```
|
||||
|
||||
> **Historical note:** An earlier approach tried redirecting to this success path by
|
||||
> patching 2 instructions at `0x26CBC` (`mov x19, x0` / `b #0x26D30`). This was
|
||||
> replaced with the more surgical NOP approach below because the redirect did not
|
||||
> properly handle the hash validation return value.
|
||||
|
||||
#### Error Codes
|
||||
|
||||
| Return Value | Meaning |
|
||||
| ------------ | ----------------------------------------- |
|
||||
| `0x00` | Success (only via case 5 flag path) |
|
||||
| `0xA0` | Early flag check failure |
|
||||
| `0xA1` | Unknown sub-selector / validation failure |
|
||||
| `0x130A1` | Hash mismatch (hash presence != flag) |
|
||||
| `0x22DA1` | Version-dependent validation failure |
|
||||
|
||||
### Function: `sub_FFFFFFF0170353B8` --- CS hash flags validator
|
||||
|
||||
**Call chain**: selector 24 → `sub_FFFFFFF017024834` (CS handler) →
|
||||
`sub_FFFFFFF0170356F8` (CS validation pipeline) → `sub_FFFFFFF017035A00`
|
||||
(multi-step validation, step 4 of 8) → `sub_FFFFFFF0170353B8`
|
||||
|
||||
### Decompiled (pre-patch)
|
||||
|
||||
```c
|
||||
// sub_FFFFFFF0170353B8(manifest_ptr, version)
|
||||
__int64 __fastcall sub_FFFFFFF0170353B8(__int64 **a1, unsigned int a2)
|
||||
{
|
||||
__int64 v4 = **a1;
|
||||
__int64 v7 = 0; // hash data pointer
|
||||
int v6 = 0; // hash flags
|
||||
|
||||
// Patch 1: NOP removes arg load (LDR X1, [X20, #0x38])
|
||||
// Patch 2: NOP removes this call entirely:
|
||||
sub_FFFFFFF0170335F8(a1[6], a1[7], &v6); // extract hash flags from CS blob
|
||||
sub_FFFFFFF017033718(a1[6], a1[7], &v7); // extract hash data pointer
|
||||
|
||||
if ( a2 >= 6 && *(v4 + 8) )
|
||||
return 0xA1; // 161
|
||||
|
||||
// Critical comparison: does hash presence match flags?
|
||||
if ( (v7 != 0) == ((v6 & 2) >> 1) )
|
||||
return 0x130A1; // 77985 — hash mismatch
|
||||
|
||||
// ... further version-dependent checks return 0xA1 or 0x22DA1
|
||||
}
|
||||
```
|
||||
|
||||
### What `sub_FFFFFFF0170335F8` does
|
||||
|
||||
Extracts hash flags from the CodeSignature blob header. Reads `bswap32(*(blob + 12))`
|
||||
into the output parameter (the flags bitmask). Bit 1 of the flags indicates whether
|
||||
a code hash is present.
|
||||
|
||||
### What `sub_FFFFFFF017033718` does
|
||||
|
||||
Locates the hash data within the CodeSignature blob. Validates blob header version
|
||||
(`bswap32(*(blob+8)) >> 9 >= 0x101`), then follows a length-prefixed string pointer
|
||||
at offset 48 to find the hash data. Returns the hash data pointer via output param.
|
||||
|
||||
### Effect of NOP
|
||||
|
||||
With `sub_FFFFFFF0170335F8` NOPed, `v6` stays at its initialized value of **0**.
|
||||
This means `(v6 & 2) >> 1 = 0` (hash-present flag is cleared). As long as
|
||||
`sub_FFFFFFF017033718` returns a non-null hash pointer (`v7 != 0`), the comparison
|
||||
becomes `(1 == 0)` → **false**, so the `0x130A1` error is skipped. The function
|
||||
falls through to the version checks which return success for version <= 5.
|
||||
|
||||
This effectively bypasses CodeSignature hash validation --- the hash data exists
|
||||
in the blob but the hash-present flag is suppressed, so the consistency check passes.
|
||||
|
||||
### `txm_jb.py` dynamic finder: `patch_selector24_hash_extraction_nop()`
|
||||
|
||||
Scans for `mov w0, #0xa1` as a unique anchor to locate the CS hash validator function,
|
||||
finds PACIBSP to determine function start, then matches the pattern
|
||||
`LDR X1,[Xn,#0x38]` / `ADD X2,...` / `BL` / `LDP` within it. NOPs the `LDR X1`
|
||||
(arg setup) and the `BL hash_flags_extract` (call).
|
||||
|
||||
### UUID Canary Verification
|
||||
|
||||
To confirm which TXM variant is loaded during boot, XOR the last byte of `LC_UUID`:
|
||||
|
||||
| | UUID |
|
||||
| -------------------------- | -------------------------------------- |
|
||||
| Original research | `0FFA437D-376F-3F8E-AD26-317E2111655D` |
|
||||
| Original release | `3C1E0E65-BFE2-3113-9C65-D25926C742B4` |
|
||||
| Canary (research XOR 0x01) | `0FFA437D-376F-3F8E-AD26-317E2111655C` |
|
||||
|
||||
Panic log `TXM UUID:` line confirmed canary `...655C` → **patched research TXM IS loaded**.
|
||||
The problem was exclusively in the selector24 patch logic (the earlier redirect approach
|
||||
did not properly handle the hash validation return value).
|
||||
|
||||
---
|
||||
|
||||
## Patch 3: get-task-allow Force True (selector 41)
|
||||
|
||||
**Error**: `TXM [Error]: selector: 41 | 29`
|
||||
|
||||
### Address
|
||||
|
||||
| File Offset | VA | Original Instruction | Patch |
|
||||
| ----------- | -------------------- | ------------------------- | ------------ |
|
||||
| `0x1f5d4` | `0xFFFFFFF0170235D4` | `BL sub_FFFFFFF017022A30` | `MOV X0, #1` |
|
||||
|
||||
### Function: `sub_FFFFFFF017023558` --- selector 41 handler
|
||||
|
||||
**Call chain**: selector 41 → `sub_FFFFFFF0170280A4` (ptr validation) →
|
||||
`sub_FFFFFFF017023558`
|
||||
|
||||
### Decompiled (pre-patch)
|
||||
|
||||
```c
|
||||
// sub_FFFFFFF017023558(manifest)
|
||||
__int64 __fastcall sub_FFFFFFF017023558(__int64 a1)
|
||||
{
|
||||
// Check developer mode is enabled (byte_FFFFFFF017070F24)
|
||||
if ( (byte_FFFFFFF017070F24 & 1) == 0 )
|
||||
return 27; // developer mode not enabled
|
||||
|
||||
// Check license-to-operate entitlement (always first)
|
||||
sub_FFFFFFF017022A30(0, "research.com.apple.license-to-operate", 0);
|
||||
|
||||
// Lock manifest
|
||||
sub_FFFFFFF017027074(a1, 0, 0);
|
||||
|
||||
if ( *(a1 + 36) == 1 ) // special manifest type
|
||||
goto error_path; // return via panic(0x81)
|
||||
|
||||
// === PATCHED INSTRUCTION ===
|
||||
// Original: BL sub_FFFFFFF017022A30 — entitlement_lookup(manifest, "get-task-allow", 0)
|
||||
// Patched: MOV X0, #1
|
||||
if ( (sub_FFFFFFF017022A30(a1, "get-task-allow", 0) & 1) != 0 ) // TBNZ w0, #0
|
||||
{
|
||||
v3 = 0; // success
|
||||
*(a1 + 0x30) = 1; // set get-task-allow flag on manifest
|
||||
}
|
||||
else
|
||||
{
|
||||
v3 = 29; // ERROR 29: no get-task-allow entitlement
|
||||
}
|
||||
|
||||
sub_FFFFFFF01702717C(a1, 0); // unlock manifest
|
||||
return v3;
|
||||
}
|
||||
```
|
||||
|
||||
### Assembly at patch site
|
||||
|
||||
```asm
|
||||
FFFFFFF0170235C4 ADRL X1, "get-task-allow"
|
||||
FFFFFFF0170235CC MOV X0, X19 ; manifest object
|
||||
FFFFFFF0170235D0 MOV X2, #0
|
||||
FFFFFFF0170235D4 BL sub_FFFFFFF017022A30 ; <-- PATCHED to MOV X0, #1
|
||||
FFFFFFF0170235D8 TBNZ W0, #0, loc_... ; always taken when x0=1
|
||||
```
|
||||
|
||||
### Effect
|
||||
|
||||
Replaces the entitlement lookup call with a constant `1`. The subsequent `TBNZ W0, #0`
|
||||
always takes the branch to the success path, which sets `*(manifest + 0x30) = 1`
|
||||
(the get-task-allow flag byte). Every process now has get-task-allow, enabling
|
||||
debugging via `task_for_pid` and LLDB attach.
|
||||
|
||||
### What `sub_FFFFFFF017022A30` does
|
||||
|
||||
Universal entitlement lookup function. When `a1 != 0`, it resolves the manifest's
|
||||
entitlement dictionary and searches for the named key via `sub_FFFFFFF017036294`.
|
||||
Returns a composite status word where bit 0 indicates the entitlement was found.
|
||||
|
||||
### `txm_jb.py` dynamic finder: `patch_get_task_allow_force_true()`
|
||||
|
||||
Searches for string refs to `"get-task-allow"`, then scans forward for the pattern
|
||||
`BL X / TBNZ w0, #0, Y`. Patches the BL to `MOV X0, #1`.
|
||||
|
||||
---
|
||||
|
||||
## Patch 4: selector 42|29 Shellcode (Debug Mapping Gate)
|
||||
|
||||
**Error**: `TXM [Error]: selector: 42 | 29`
|
||||
|
||||
### Addresses
|
||||
|
||||
| File Offset | VA | Patch |
|
||||
| ----------- | -------------------- | -------------------------- |
|
||||
| `0x2717c` | `0xFFFFFFF01702B17C` | `B #0x36238` (→ shellcode) |
|
||||
| `0x5d3b4` | `0xFFFFFFF0170613B4` | `NOP` (pad) |
|
||||
| `0x5d3b8` | `0xFFFFFFF0170613B8` | `MOV X0, #1` |
|
||||
| `0x5d3bc` | `0xFFFFFFF0170613BC` | `STRB W0, [X20, #0x30]` |
|
||||
| `0x5d3c0` | `0xFFFFFFF0170613C0` | `MOV X0, X20` |
|
||||
| `0x5d3c4` | `0xFFFFFFF0170613C4` | `B #-0x36244` (→ 0xB180) |
|
||||
|
||||
### Context: Dispatcher case 42
|
||||
|
||||
```asm
|
||||
; jumptable case 42 entry in sub_FFFFFFF01702AE80:
|
||||
FFFFFFF01702B178 BTI j
|
||||
FFFFFFF01702B17C MOV X0, X20 ; <-- PATCHED to B shellcode
|
||||
FFFFFFF01702B180 BL sub_FFFFFFF0170280A4 ; validate pointer
|
||||
FFFFFFF01702B184 MOV X1, X21
|
||||
FFFFFFF01702B188 MOV X2, X22
|
||||
FFFFFFF01702B18C BL sub_FFFFFFF017023368 ; selector 42 handler
|
||||
FFFFFFF01702B190 B loc_FFFFFFF01702B344 ; return result
|
||||
```
|
||||
|
||||
### Shellcode (at zero-filled code cave in `__TEXT_EXEC`)
|
||||
|
||||
```asm
|
||||
; 0xFFFFFFF0170613B4 — cave was all zeros
|
||||
NOP ; pad (original 0x00000000)
|
||||
MOV X0, #1 ; value to store
|
||||
STRB W0, [X20, #0x30] ; force manifest->get_task_allow = 1
|
||||
MOV X0, X20 ; restore original instruction (was at 0xB17C)
|
||||
B #-0x36244 ; jump back to 0xFFFFFFF01702B180 (BL validate)
|
||||
```
|
||||
|
||||
### Why this is needed
|
||||
|
||||
Selector 42's handler `sub_FFFFFFF017023368` checks the get-task-allow byte early:
|
||||
|
||||
```c
|
||||
// sub_FFFFFFF017023368(manifest, addr, size)
|
||||
// ... after debugger entitlement check ...
|
||||
v8 = atomic_load((unsigned __int8 *)(a1 + 48)); // offset 0x30
|
||||
if ( (v8 & 1) == 0 )
|
||||
{
|
||||
v6 = 29; // ERROR 29: get-task-allow not set
|
||||
goto unlock_and_return;
|
||||
}
|
||||
// ... proceed with debug memory mapping ...
|
||||
```
|
||||
|
||||
Selector 41 (patch 3) sets this byte during entitlement validation, but
|
||||
there are code paths where selector 42 can be called before selector 41 has run
|
||||
for a given manifest. The shellcode ensures the flag is always set at the dispatch
|
||||
level before the handler even sees it.
|
||||
|
||||
### `sub_FFFFFFF0170280A4` --- pointer validator
|
||||
|
||||
```c
|
||||
// Validates page alignment and bounds, returns input pointer unchanged
|
||||
unsigned __int64 sub_FFFFFFF0170280A4(unsigned __int64 a1) {
|
||||
if ( (a1 & ~0x3FFF) == 0 ) panic(64);
|
||||
if ( a1 >= 0xFFFFFFFFFFFFC000 ) panic(66);
|
||||
// ... bounds checks ...
|
||||
return (a1 & ~0x3FFF) + (a1 & 0x3FFF); // == a1
|
||||
}
|
||||
```
|
||||
|
||||
Since the validator returns the pointer unchanged, `x20` (raw arg) and the validated
|
||||
pointer both refer to the same object. The shellcode's `STRB W0, [X20, #0x30]`
|
||||
writes to the correct location.
|
||||
|
||||
### `txm_jb.py` dynamic finder: `patch_selector42_29_shellcode()`
|
||||
|
||||
1. Finds the "debugger gate function" via string refs to `"com.apple.private.cs.debugger"`
|
||||
2. Locates the dispatch stub by matching `BTI j / MOV X0, X20 / BL / MOV X1, X21 / MOV X2, X22 / BL debugger_gate / B`
|
||||
3. Finds a zero-filled code cave via `_find_udf_cave()` near the stub
|
||||
4. Emits the branch + shellcode + branch-back
|
||||
|
||||
---
|
||||
|
||||
## Patch 5: Debugger Entitlement Force True (selector 42)
|
||||
|
||||
**Error**: `TXM [Error]: selector: 42 | 37`
|
||||
|
||||
### Address
|
||||
|
||||
| File Offset | VA | Original Instruction | Patch |
|
||||
| ----------- | -------------------- | ------------------------- | ------------ |
|
||||
| `0x1f3b8` | `0xFFFFFFF0170233B8` | `BL sub_FFFFFFF017022A30` | `MOV W0, #1` |
|
||||
|
||||
### Function: `sub_FFFFFFF017023368` --- selector 42 handler (debug memory mapping)
|
||||
|
||||
### Assembly at patch site
|
||||
|
||||
```asm
|
||||
; Check com.apple.private.cs.debugger entitlement
|
||||
FFFFFFF0170233A8 ADRL X1, "com.apple.private.cs.debugger"
|
||||
FFFFFFF0170233B0 MOV X0, #0 ; check global manifest (a1=0)
|
||||
FFFFFFF0170233B4 MOV X2, #0
|
||||
FFFFFFF0170233B8 BL sub_FFFFFFF017022A30 ; <-- PATCHED to MOV W0, #1
|
||||
FFFFFFF0170233BC TBNZ W0, #0, loc_... ; always taken when w0=1
|
||||
FFFFFFF0170233C0 ADRL X8, fallback_flag ; secondary check (also bypassed)
|
||||
FFFFFFF0170233C8 LDRB W8, [X8, #offset]
|
||||
FFFFFFF0170233CC TBNZ W8, #0, loc_... ; secondary bypass path
|
||||
FFFFFFF0170233D0 ADRL X0, "disallowed non-debugger initiated debug mapping"
|
||||
FFFFFFF0170233D8 BL sub_FFFFFFF017025B7C ; log error
|
||||
FFFFFFF0170233DC MOV W20, #0x25 ; error 37
|
||||
FFFFFFF0170233E0 B unlock_return
|
||||
```
|
||||
|
||||
### Decompiled (pre-patch)
|
||||
|
||||
```c
|
||||
// First check in sub_FFFFFFF017023368 after input validation:
|
||||
if ( (sub_FFFFFFF017022A30(0, "com.apple.private.cs.debugger", 0) & 1) == 0 )
|
||||
{
|
||||
// Fallback: check a static byte flag
|
||||
if ( (fallback_flag & 1) == 0 )
|
||||
{
|
||||
log("disallowed non-debugger initiated debug mapping");
|
||||
return 37; // 0x25
|
||||
}
|
||||
}
|
||||
// Continue with debug mapping...
|
||||
```
|
||||
|
||||
### Effect
|
||||
|
||||
Replaces the entitlement lookup with `MOV W0, #1`. The `TBNZ W0, #0` always
|
||||
branches to the success path, bypassing both the entitlement check and the
|
||||
fallback flag check. This allows any process to create debug memory mappings
|
||||
regardless of whether it has `com.apple.private.cs.debugger`.
|
||||
|
||||
### `txm_jb.py` dynamic finder: `patch_debugger_entitlement_force_true()`
|
||||
|
||||
Searches for string refs to `"com.apple.private.cs.debugger"`, then matches
|
||||
the pattern: `mov x0, #0 / mov x2, #0 / bl X / tbnz w0, #0, Y`. Patches the BL
|
||||
to `MOV W0, #1`.
|
||||
|
||||
---
|
||||
|
||||
## Patch 6: Developer Mode Bypass
|
||||
|
||||
### Address
|
||||
|
||||
| File Offset | VA | Original Instruction | Patch |
|
||||
| ----------- | -------------------- | ----------------------------------- | ----- |
|
||||
| `0x1FA58` | `0xFFFFFFF017023A58` | `TBNZ W9, #0, loc_FFFFFFF017023A6C` | NOP |
|
||||
|
||||
### Function: `sub_FFFFFFF017023A20` --- developer mode configuration
|
||||
|
||||
Called during TXM initialization to determine and store the developer mode state.
|
||||
The result is stored in `byte_FFFFFFF017070F24`, which is the gate flag checked by
|
||||
selector 41 (`sub_FFFFFFF017023558`).
|
||||
|
||||
### Assembly at patch site
|
||||
|
||||
```asm
|
||||
; Check system policy configuration
|
||||
FFFFFFF017023A50 LDR X9, [X8, #off_FFFFFFF0170146C0]
|
||||
FFFFFFF017023A54 LDRB W9, [X9, #0x4D] ; load system policy byte
|
||||
FFFFFFF017023A58 TBNZ W9, #0, loc_FFFFFFF017023A6C ; <-- PATCHED to NOP
|
||||
; Fall through to force-enable:
|
||||
FFFFFFF017023A5C MOV W20, #1 ; developer_mode = ENABLED
|
||||
FFFFFFF017023A60 ADRL X0, "developer mode enabled due to system policy configuration"
|
||||
FFFFFFF017023A68 B log_and_store
|
||||
```
|
||||
|
||||
### Decompiled (pre-patch)
|
||||
|
||||
```c
|
||||
__int64 sub_FFFFFFF017023A20(__int64 manifest)
|
||||
{
|
||||
char devmode;
|
||||
|
||||
// Check 1: PCC research variant flag
|
||||
if ( pcc_research_flag )
|
||||
{
|
||||
devmode = 1;
|
||||
goto apply;
|
||||
}
|
||||
|
||||
// Check 2: System policy (patched here)
|
||||
byte policy = *(system_config_ptr + 0x4D);
|
||||
if ( (policy & 1) != 0 ) // <-- TBNZ jumps past force-enable
|
||||
goto normal_path; // to xART / user-config checks
|
||||
|
||||
// Force-enable path (reached by NOPing the TBNZ):
|
||||
devmode = 1;
|
||||
log("developer mode enabled due to system policy configuration");
|
||||
goto apply;
|
||||
|
||||
normal_path:
|
||||
// ... xART availability check ...
|
||||
// ... user configuration check ...
|
||||
// May set devmode = 0 (disabled) based on config
|
||||
|
||||
apply:
|
||||
byte_FFFFFFF017070F24 = devmode; // global developer mode state
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Effect
|
||||
|
||||
NOPing the `TBNZ` makes execution always fall through to `MOV W20, #1`, forcing
|
||||
developer mode enabled regardless of the system policy byte. Without this:
|
||||
|
||||
- The `TBNZ` would jump to `loc_FFFFFFF017023A6C` (the normal path)
|
||||
- The normal path checks xART availability, device tree flags, and user configuration
|
||||
- On PCC VMs, this can result in developer mode being **disabled**
|
||||
|
||||
Developer mode is a **prerequisite** for selectors 41 and 42 --- the selector 41
|
||||
handler returns error 27 immediately if `byte_FFFFFFF017070F24` is not set:
|
||||
|
||||
```c
|
||||
// In sub_FFFFFFF017023558 (selector 41):
|
||||
if ( (byte_FFFFFFF017070F24 & 1) == 0 )
|
||||
return 27; // developer mode not enabled
|
||||
```
|
||||
|
||||
### `txm_jb.py` dynamic finder: `patch_developer_mode_bypass()`
|
||||
|
||||
Searches for string refs to `"developer mode enabled due to system policy
|
||||
configuration"`, then scans backwards for a `tbz/tbnz/cbz/cbnz` instruction
|
||||
matching `w9, #0`. NOPs it.
|
||||
|
||||
---
|
||||
|
||||
## Patch Dependency Chain
|
||||
|
||||
The patches have a logical ordering --- later patches depend on earlier ones:
|
||||
|
||||
```
|
||||
Patch 6: Developer Mode Bypass
|
||||
| Forces byte_FFFFFFF017070F24 = 1
|
||||
|
|
||||
|---> Patch 3: get-task-allow Force True (selector 41)
|
||||
| Requires developer mode (checks byte_FFFFFFF017070F24)
|
||||
| Forces manifest[0x30] = 1
|
||||
|
|
||||
|---> Patch 4: selector 42|29 Shellcode
|
||||
| Forces manifest[0x30] = 1 at dispatch level
|
||||
| Safety net for Patch 3 (covers cases where sel 42 runs before sel 41)
|
||||
|
|
||||
|---> Patch 5: Debugger Entitlement Force True (selector 42)
|
||||
| Bypasses com.apple.private.cs.debugger check
|
||||
| Allows debug memory mapping for all processes
|
||||
|
|
||||
└---> Patches 1-2: CodeSignature Hash Bypass (selector 24)
|
||||
Independent — bypasses CS hash validation in the signature chain
|
||||
```
|
||||
|
||||
### Boot-time flow
|
||||
|
||||
1. TXM initializes → `sub_FFFFFFF017023A20` runs → **Patch 6** forces devmode ON
|
||||
2. Process loads → selector 24 validates CodeSignature → **Patches 1-2** skip hash check
|
||||
3. Process requests entitlements → selector 41 → **Patch 3** grants get-task-allow
|
||||
4. Debugger attaches → selector 42 → **Patch 4** pre-sets flag + **Patch 5** grants debugger ent
|
||||
5. Debug mapping succeeds → LLDB can attach to any process
|
||||
|
||||
---
|
||||
|
||||
## Summary Table
|
||||
|
||||
| # | File Offset | VA | Function | Patch | Purpose |
|
||||
| --- | ----------- | -------------------- | -------------------------------------------- | ---------------------- | --------------------------------- |
|
||||
| 1 | `0x313ec` | `0xFFFFFFF0170353EC` | `sub_FFFFFFF0170353B8` (CS hash validator) | NOP | Remove hash flag load |
|
||||
| 2 | `0x313f4` | `0xFFFFFFF0170353F4` | `sub_FFFFFFF0170353B8` (CS hash validator) | NOP | Skip hash flag extraction call |
|
||||
| 3 | `0x1f5d4` | `0xFFFFFFF0170235D4` | `sub_FFFFFFF017023558` (selector 41) | `MOV X0, #1` | Force get-task-allow = true |
|
||||
| 4 | `0x2717c` | `0xFFFFFFF01702B17C` | `sub_FFFFFFF01702AE80` (dispatcher, case 42) | `B shellcode` | Redirect to shellcode cave |
|
||||
| 4a | `0x5d3b4` | `0xFFFFFFF0170613B4` | code cave (zeros) | `NOP` | Shellcode padding |
|
||||
| 4b | `0x5d3b8` | `0xFFFFFFF0170613B8` | code cave | `MOV X0, #1` | Set value for flag |
|
||||
| 4c | `0x5d3bc` | `0xFFFFFFF0170613BC` | code cave | `STRB W0, [X20,#0x30]` | Force get-task-allow flag |
|
||||
| 4d | `0x5d3c0` | `0xFFFFFFF0170613C0` | code cave | `MOV X0, X20` | Restore original instruction |
|
||||
| 4e | `0x5d3c4` | `0xFFFFFFF0170613C4` | code cave | `B back` | Return to dispatcher |
|
||||
| 5 | `0x1f3b8` | `0xFFFFFFF0170233B8` | `sub_FFFFFFF017023368` (selector 42) | `MOV W0, #1` | Force debugger entitlement = true |
|
||||
| 6 | `0x1FA58` | `0xFFFFFFF017023A58` | `sub_FFFFFFF017023A20` (devmode init) | NOP | Force developer mode ON |
|
||||
|
||||
**Total**: 6 logical patches, 11 instruction modifications (counting shellcode), enabling:
|
||||
|
||||
- CodeSignature bypass (patches 1-2)
|
||||
- Universal get-task-allow (patches 3-4)
|
||||
- Universal debugger entitlement (patch 5)
|
||||
- Forced developer mode (patch 6)
|
||||
@@ -5,41 +5,41 @@ and PCC-CloudOS 26.3 (23D128) IPSWs.
|
||||
|
||||
## Source Files
|
||||
|
||||
| Source | Variant | IM4P Size | SHA256 |
|
||||
|--------|---------|-----------|--------|
|
||||
| cloudos | release | 161025 | `3453eb476cfb53d8...` |
|
||||
| cloudos | research | 161028 | `93ad9e382d8c6353...` |
|
||||
| iphone | release | 161025 | `3453eb476cfb53d8...` |
|
||||
| iphone | research | 161028 | `93ad9e382d8c6353...` |
|
||||
| Source | Variant | IM4P Size | SHA256 |
|
||||
| ------- | -------- | --------- | --------------------- |
|
||||
| cloudos | release | 161025 | `3453eb476cfb53d8...` |
|
||||
| cloudos | research | 161028 | `93ad9e382d8c6353...` |
|
||||
| iphone | release | 161025 | `3453eb476cfb53d8...` |
|
||||
| iphone | research | 161028 | `93ad9e382d8c6353...` |
|
||||
|
||||
**Key finding:** Both IPSWs contain identical TXM files (same SHA256).
|
||||
The TXM binary is shared across iPhone and cloudOS IPSWs.
|
||||
|
||||
## Decompressed Binary Overview
|
||||
|
||||
| Property | RELEASE | RESEARCH |
|
||||
|----------|---------|----------|
|
||||
| Compressed size | 160726 bytes | 160729 bytes |
|
||||
| Decompressed size | 458784 bytes | 458784 bytes |
|
||||
| Compression | BVX2 (LZFSE) | BVX2 (LZFSE) |
|
||||
| Format | Mach-O 64-bit ARM64 | Mach-O 64-bit ARM64 |
|
||||
| SHA256 | `bfc493e3c7b7dc00...` | `62f40b9cd32a2a03...` |
|
||||
| File type | 2 (MH_EXECUTE) | 2 (MH_EXECUTE) |
|
||||
| Load commands | 11 | 11 |
|
||||
| Flags | `0x00200001` | `0x00200001` |
|
||||
| Property | RELEASE | RESEARCH |
|
||||
| ----------------- | --------------------- | --------------------- |
|
||||
| Compressed size | 160726 bytes | 160729 bytes |
|
||||
| Decompressed size | 458784 bytes | 458784 bytes |
|
||||
| Compression | BVX2 (LZFSE) | BVX2 (LZFSE) |
|
||||
| Format | Mach-O 64-bit ARM64 | Mach-O 64-bit ARM64 |
|
||||
| SHA256 | `bfc493e3c7b7dc00...` | `62f40b9cd32a2a03...` |
|
||||
| File type | 2 (MH_EXECUTE) | 2 (MH_EXECUTE) |
|
||||
| Load commands | 11 | 11 |
|
||||
| Flags | `0x00200001` | `0x00200001` |
|
||||
|
||||
## Mach-O Segments
|
||||
|
||||
Both variants have identical segment layout:
|
||||
|
||||
| Segment | VM Address | VM Size | File Offset | File Size |
|
||||
|---------|------------|---------|-------------|-----------|
|
||||
| `__TEXT` | `0xfffffff017004000` | `0x10000` | `0x0` | `0x10000` |
|
||||
| `__DATA_CONST` | `0xfffffff017014000` | `0xc000` | `0x10000` | `0xc000` |
|
||||
| `__TEXT_EXEC` | `0xfffffff017020000` | `0x44000` | `0x1c000` | `0x44000` |
|
||||
| `__TEXT_BOOT_EXEC` | `0xfffffff017064000` | `0xc000` | `0x60000` | `0xc000` |
|
||||
| `__DATA` | `0xfffffff017070000` | `0x4000` | `0x6c000` | `0x4000` |
|
||||
| `__LINKEDIT` | `0xfffffff017074000` | `0x4000` | `0x70000` | `0x20` |
|
||||
| Segment | VM Address | VM Size | File Offset | File Size |
|
||||
| ------------------ | -------------------- | --------- | ----------- | --------- |
|
||||
| `__TEXT` | `0xfffffff017004000` | `0x10000` | `0x0` | `0x10000` |
|
||||
| `__DATA_CONST` | `0xfffffff017014000` | `0xc000` | `0x10000` | `0xc000` |
|
||||
| `__TEXT_EXEC` | `0xfffffff017020000` | `0x44000` | `0x1c000` | `0x44000` |
|
||||
| `__TEXT_BOOT_EXEC` | `0xfffffff017064000` | `0xc000` | `0x60000` | `0xc000` |
|
||||
| `__DATA` | `0xfffffff017070000` | `0x4000` | `0x6c000` | `0x4000` |
|
||||
| `__LINKEDIT` | `0xfffffff017074000` | `0x4000` | `0x70000` | `0x20` |
|
||||
|
||||
Segment layout identical: **True**
|
||||
|
||||
@@ -50,10 +50,10 @@ Segment layout identical: **True**
|
||||
|
||||
### Diffs by Segment
|
||||
|
||||
| Segment | Regions | Bytes Changed | % of Segment |
|
||||
|---------|---------|---------------|--------------|
|
||||
| `__TEXT` | 3 | 3304 | 5.04% |
|
||||
| `__TEXT_EXEC` | 84 | 409 | 0.15% |
|
||||
| Segment | Regions | Bytes Changed | % of Segment |
|
||||
| ------------- | ------- | ------------- | ------------ |
|
||||
| `__TEXT` | 3 | 3304 | 5.04% |
|
||||
| `__TEXT_EXEC` | 84 | 409 | 0.15% |
|
||||
|
||||
## Diff Classification
|
||||
|
||||
@@ -62,12 +62,13 @@ Segment layout identical: **True**
|
||||
The largest diff region (`0x17c5` - `0x2496`, 3282 bytes) is in the `__TEXT` segment
|
||||
string/const data area. The key difference is the build variant identifier:
|
||||
|
||||
| Offset | RELEASE | RESEARCH |
|
||||
|--------|---------|----------|
|
||||
| Offset | RELEASE | RESEARCH |
|
||||
| -------- | ------------------------------------------------ | ------------------------------------------------- |
|
||||
| `0x17c5` | `lease.TrustedExecutionMonitor_Guarded-182.40.3` | `search.TrustedExecutionMonitor_Guarded-182.40.3` |
|
||||
| `0xcb7f` | `lease` | `search` |
|
||||
| `0xcb7f` | `lease` | `search` |
|
||||
|
||||
Full build string:
|
||||
|
||||
- **RELEASE:** `release.TrustedExecutionMonitor_Guarded-182.40.3`
|
||||
- **RESEARCH:** `research.TrustedExecutionMonitor_Guarded-182.40.3`
|
||||
|
||||
@@ -91,8 +92,8 @@ RESEARCH: add x8, x8, #0x823 ; points to same string, shifted +1
|
||||
|
||||
Sample code diffs (first 10):
|
||||
|
||||
| Offset | RELEASE instruction | RESEARCH instruction |
|
||||
|--------|---------------------|----------------------|
|
||||
| Offset | RELEASE instruction | RESEARCH instruction |
|
||||
| --------- | -------------------- | -------------------- |
|
||||
| `0x2572c` | `add x8, x8, #0x822` | `add x8, x8, #0x823` |
|
||||
| `0x25794` | `add x8, x8, #0x861` | `add x8, x8, #0x862` |
|
||||
| `0x257d8` | `add x0, x0, #0x877` | `add x0, x0, #0x878` |
|
||||
@@ -104,7 +105,7 @@ Sample code diffs (first 10):
|
||||
| `0x25c58` | `add x2, x2, #0x919` | `add x2, x2, #0x91a` |
|
||||
| `0x25c98` | `add x0, x0, #0x927` | `add x0, x0, #0x928` |
|
||||
|
||||
### 4. Functional Differences
|
||||
### 3. Functional Differences
|
||||
|
||||
**None.** All code diffs are string pointer adjustments caused by the 1-byte
|
||||
shift from `"release"` to `"research"`. The two variants are **functionally
|
||||
@@ -114,24 +115,24 @@ identical** — same logic, same security policies, same code paths.
|
||||
|
||||
Both variants contain identical security-relevant strings:
|
||||
|
||||
| Offset | String |
|
||||
|--------|--------|
|
||||
| `0xd31` | `restricted execution mode` |
|
||||
| `0x1919` | `debug-enabled` |
|
||||
| `0x1a4e` | `darwinos-security-environment` |
|
||||
| `0x1ad0` | `security-mode-change-enable` |
|
||||
| `0x1b4b` | `amfi-only-platform-code` |
|
||||
| `0x1bd6` | `research-enabled` |
|
||||
| Offset | String |
|
||||
| -------- | --------------------------------- |
|
||||
| `0xd31` | `restricted execution mode` |
|
||||
| `0x1919` | `debug-enabled` |
|
||||
| `0x1a4e` | `darwinos-security-environment` |
|
||||
| `0x1ad0` | `security-mode-change-enable` |
|
||||
| `0x1b4b` | `amfi-only-platform-code` |
|
||||
| `0x1bd6` | `research-enabled` |
|
||||
| `0x1c4c` | `sec-research-device-erm-enabled` |
|
||||
| `0x1cca` | `vmm-present` |
|
||||
| `0x1d33` | `sepfw-load-at-boot` |
|
||||
| `0x1de8` | `sepfw-never-boot` |
|
||||
| `0x1e85` | `osenvironment` |
|
||||
| `0x1ec4` | `device-recovery` |
|
||||
| `0x1f81` | `TrustCache` |
|
||||
| `0x202a` | `iboot-build-variant` |
|
||||
| `0x20a9` | `development` |
|
||||
| `0x23da` | `image4 dispatch` |
|
||||
| `0x1cca` | `vmm-present` |
|
||||
| `0x1d33` | `sepfw-load-at-boot` |
|
||||
| `0x1de8` | `sepfw-never-boot` |
|
||||
| `0x1e85` | `osenvironment` |
|
||||
| `0x1ec4` | `device-recovery` |
|
||||
| `0x1f81` | `TrustCache` |
|
||||
| `0x202a` | `iboot-build-variant` |
|
||||
| `0x20a9` | `development` |
|
||||
| `0x23da` | `image4 dispatch` |
|
||||
|
||||
## Implications for Patching
|
||||
|
||||
@@ -152,4 +153,4 @@ The TXM `release` and `research` variants are **cosmetically different but
|
||||
functionally identical**. The only real difference is the embedded build variant
|
||||
string (`"release"` vs `"research"`), which causes a 1-byte cascade in string
|
||||
offsets and corresponding `ADD` immediate adjustments in code.
|
||||
Both IPSWs (iPhone and cloudOS) ship the same pair of TXM binaries.
|
||||
Both IPSWs (iPhone and cloudOS) ship the same pair of TXM binaries.
|
||||
@@ -1,178 +0,0 @@
|
||||
|
||||
# BuildManifest.plist Research
|
||||
|
||||
## 1. Multi-Source Comparison
|
||||
|
||||
### Identity Count Overview
|
||||
|
||||
| Source | Identities | DeviceClasses |
|
||||
|--------|-----------|---------------|
|
||||
| iPhone 26.1 | 5 | All d47ap |
|
||||
| iPhone 26.3 | 5 | All d47ap |
|
||||
| CloudOS 26.1 | 6 | j236cap, j475dap, vphone600ap (x2), vresearch101ap (x2) |
|
||||
| KnownWork 26.1 | 5 | All vresearch101ap |
|
||||
|
||||
### CloudOS 26.1 Identity Structure (6 identities)
|
||||
|
||||
| Index | DeviceClass | Variant | BuildStyle | Manifest Keys |
|
||||
|-------|-------------|---------|------------|---------------|
|
||||
| [0] | j236cap | Darwin Cloud Customer Erase Install (IPSW) | RELEASE build | 37 keys (server hardware) |
|
||||
| [1] | j475dap | Darwin Cloud Customer Erase Install (IPSW) | unknown (no path) | 0 keys (empty placeholder) |
|
||||
| [2] | vphone600ap | Darwin Cloud Customer Erase Install (IPSW) | RELEASE build | 29 keys (includes UI assets) |
|
||||
| [3] | vresearch101ap | Darwin Cloud Customer Erase Install (IPSW) | RELEASE build | 20 keys (no UI assets) |
|
||||
| [4] | vphone600ap | Research Darwin Cloud Customer Erase Install (IPSW) | RESEARCH_RELEASE build | 29 keys (research kernel) |
|
||||
| [5] | vresearch101ap | Research Darwin Cloud Customer Erase Install (IPSW) | RESEARCH_RELEASE build | 20 keys (research kernel) |
|
||||
|
||||
Key distinctions:
|
||||
- CloudOS[2] vs [4] (vphone600ap): [2] uses RELEASE boot chain + release kernelcache; [4] uses RESEARCH_RELEASE + research kernelcache + txm.iphoneos.research.im4p
|
||||
- CloudOS[3] vs [5] (vresearch101ap): Same pattern — [3] is RELEASE, [5] is RESEARCH_RELEASE
|
||||
- **vphone600ap has components vresearch101ap lacks**: RecoveryMode, AppleLogo, Battery*, RestoreLogo, SEP (vphone600 variant)
|
||||
- vresearch101ap has only 20 manifest keys (no UI assets, no RecoveryMode)
|
||||
|
||||
### vphone600ap vs vresearch101ap Key Differences
|
||||
|
||||
| Property | vphone600ap | vresearch101ap |
|
||||
|----------|-------------|----------------|
|
||||
| Ap,ProductType | iPhone99,11 | ComputeModule14,2 |
|
||||
| Ap,Target | VPHONE600AP | VRESEARCH101AP |
|
||||
| ApBoardID | 0x91 | 0x90 |
|
||||
| DeviceTree | DeviceTree.vphone600ap.im4p | DeviceTree.vresearch101ap.im4p |
|
||||
| SEP | sep-firmware.vphone600.RELEASE.im4p | sep-firmware.vresearch101.RELEASE.im4p |
|
||||
| RecoveryMode | recoverymode@2556~iphone-USBc.im4p | **NOT PRESENT** |
|
||||
| MKB dt flag | dt=1 (keybag-less boot OK) | dt=0 (fatal keybag error) |
|
||||
|
||||
---
|
||||
|
||||
## 2. Component Source Tracing (Corrected)
|
||||
|
||||
### Hybrid Identity: vresearch101 boot chain + vphone600 runtime
|
||||
|
||||
The working configuration mixes components from both board configs:
|
||||
|
||||
| Component | Source Identity | File | Why This Source |
|
||||
|-----------|---------------|------|-----------------|
|
||||
| LLB | PROD (vresearch101 release) | `LLB.vresearch101.RELEASE.im4p` | Matches DFU hardware (BDID 0x90) |
|
||||
| iBSS | PROD | `iBSS.vresearch101.RELEASE.im4p` | Matches DFU hardware |
|
||||
| iBEC | PROD | `iBEC.vresearch101.RELEASE.im4p` | Matches DFU hardware |
|
||||
| iBoot | RES (vresearch101 research) | `iBoot.vresearch101.RESEARCH_RELEASE.im4p` | Only research identity has iBoot |
|
||||
| SPTM (all) | PROD | `sptm.vresearch1.release.im4p` | Shared across board configs |
|
||||
| TXM restore | PROD | `txm.iphoneos.release.im4p` | RELEASE for restore |
|
||||
| TXM installed | RES | `txm.iphoneos.research.im4p` | Research variant, patched |
|
||||
| **DeviceTree** | **VP (vphone600 release)** | `DeviceTree.vphone600ap.im4p` | Sets MKB dt=1 |
|
||||
| **SEP/RestoreSEP** | **VP** | `sep-firmware.vphone600.RELEASE.im4p` | Must match device tree |
|
||||
| **KernelCache** | **VPR (vphone600 research)** | `kernelcache.research.vphone600` | Patched by fw_patch.py |
|
||||
| **RestoreKernelCache** | **VP (vphone600 release)** | `kernelcache.release.vphone600` | Unpatched, restore-time only |
|
||||
| **RecoveryMode** | **VP** | `recoverymode@2556~iphone-USBc.im4p` | Only vphone600ap has it |
|
||||
| RestoreRamDisk | PROD | cloudOS erase ramdisk | PCC restore ramdisk |
|
||||
| OS / SVC / etc. | I_ERASE (iPhone) | iPhone OS image | iPhone system |
|
||||
|
||||
### Why Not All-vresearch101 or All-vphone600?
|
||||
|
||||
**Problem with all-vresearch101**: The vresearch101ap device tree sets MKB `dt=0`,
|
||||
causing `MKB_INIT: FATAL KEYBAG ERROR` on first boot (no system keybag exists yet).
|
||||
Also missing RecoveryMode entry.
|
||||
|
||||
**Problem with all-vphone600**: The DFU hardware identifies as BDID 0x90
|
||||
(vresearch101ap). Using vphone600ap identity (BDID 0x91) fails TSS/SHSH signing
|
||||
and idevicerestore identity matching (`Unable to find a matching build identity`).
|
||||
|
||||
**Solution**: vresearch101ap identity fields for DFU/TSS + vphone600 runtime
|
||||
components for a working boot environment.
|
||||
|
||||
---
|
||||
|
||||
## 3. idevicerestore Identity Selection Logic
|
||||
|
||||
Source: `idevicerestore/src/idevicerestore.c` lines 2195-2242
|
||||
|
||||
### Matching Algorithm
|
||||
|
||||
idevicerestore selects a Build Identity by iterating through all `BuildIdentities` and returning the **first match** based on two fields:
|
||||
|
||||
1. **`Info.DeviceClass`** — case-insensitive match against device `hardware_model`
|
||||
2. **`Info.Variant`** — substring match against the requested variant string
|
||||
|
||||
For DFU erase restore, the search variant is `"Erase Install (IPSW)"` (defined in `idevicerestore.h`).
|
||||
|
||||
### Matching Modes
|
||||
|
||||
```c
|
||||
// Exact match
|
||||
if (strcmp(str, variant) == 0) return ident;
|
||||
|
||||
// Partial match (when exact=0)
|
||||
if (strstr(str, variant) && !strstr(str, "Research")) return ident;
|
||||
```
|
||||
|
||||
**Critical**: Partial matching **excludes** variants containing `"Research"`. This means:
|
||||
- `"Darwin Cloud Customer Erase Install (IPSW)"` — matches (contains "Erase Install (IPSW)", no "Research")
|
||||
- `"Research Darwin Cloud Customer Erase Install (IPSW)"` — skipped (contains "Research")
|
||||
|
||||
### What idevicerestore Does NOT Check
|
||||
- ApBoardID / ApChipID (used after selection, not for matching)
|
||||
- Identity index or count (no hardcoded indices)
|
||||
|
||||
### Conclusion for Single Identity
|
||||
|
||||
A BuildManifest with **one identity** works fine. The loop iterates once, and if
|
||||
DeviceClass and Variant match, it's returned. No minimum identity count required.
|
||||
|
||||
---
|
||||
|
||||
## 4. TSS/SHSH Signing
|
||||
|
||||
The TSS request sent to `gs.apple.com` includes:
|
||||
- `ApBoardID = 144` (0x90) — must match vresearch101ap
|
||||
- `ApChipID = 65025` (0xFE01)
|
||||
- `Ap,ProductType = ComputeModule14,2`
|
||||
- `Ap,Target = VRESEARCH101AP`
|
||||
- Digests for all 21 manifest components
|
||||
|
||||
Apple's TSS server signs based on these identity fields + component digests.
|
||||
Using vphone600ap identity (BDID 0x91) would fail because the DFU device
|
||||
reports BDID 0x90.
|
||||
|
||||
---
|
||||
|
||||
## 5. Final Design: Single DFU Erase Identity
|
||||
|
||||
### Identity Metadata (fw_manifest.py)
|
||||
```
|
||||
DeviceClass = vresearch101ap (from C[PROD] deep copy)
|
||||
Variant = Darwin Cloud Customer Erase Install (IPSW)
|
||||
Ap,ProductType = ComputeModule14,2
|
||||
Ap,Target = VRESEARCH101AP
|
||||
Ap,TargetType = vresearch101
|
||||
ApBoardID = 0x90
|
||||
ApChipID = 0xFE01
|
||||
FDRSupport = False
|
||||
```
|
||||
|
||||
### Source Variable Map
|
||||
```
|
||||
PROD = C[vresearch101ap release] — boot chain, SPTM, ramdisk
|
||||
RES = C[vresearch101ap research] — iBoot, TXM research
|
||||
VP = C[vphone600ap release] — DeviceTree, SEP, RestoreKernelCache, RecoveryMode
|
||||
VPR = C[vphone600ap research] — KernelCache (patched by fw_patch.py)
|
||||
I_ERASE = I[iPhone erase] — OS, trust caches, system volume
|
||||
```
|
||||
|
||||
### All 21 Manifest Entries
|
||||
```
|
||||
Boot chain (PROD): LLB, iBSS, iBEC
|
||||
Research iBoot (RES): iBoot
|
||||
Security monitors (PROD): Ap,RestoreSPTM, Ap,RestoreTXM, Ap,SPTM
|
||||
Research TXM (RES): Ap,TXM
|
||||
Device tree (VP): DeviceTree, RestoreDeviceTree
|
||||
SEP (VP): SEP, RestoreSEP
|
||||
Kernel (VPR/VP): KernelCache (research), RestoreKernelCache (release)
|
||||
Recovery (VP): RecoveryMode
|
||||
Ramdisk (PROD): RestoreRamDisk, RestoreTrustCache
|
||||
iPhone OS (I_ERASE): OS, StaticTrustCache, SystemVolume, Ap,SVC Metadata
|
||||
```
|
||||
|
||||
### Restore.plist
|
||||
```
|
||||
DeviceMap: [d47ap (iPhone), vphone600ap, vresearch101ap]
|
||||
ProductTypes: [iPhone17,3, ComputeModule14,1, ComputeModule14,2, Mac14,14, iPhone99,11]
|
||||
```
|
||||
@@ -1,179 +0,0 @@
|
||||
# Erase Install — Component Origins
|
||||
|
||||
The erase install firmware is a **hybrid** of three source sets:
|
||||
|
||||
1. **PCC vresearch101ap** — boot chain (LLB/iBSS/iBEC/iBoot) and security monitors (SPTM/TXM)
|
||||
2. **PCC vphone600ap** — runtime components (DeviceTree, SEP, KernelCache, RecoveryMode)
|
||||
3. **iPhone 17,3** — OS image, trust caches, filesystem
|
||||
|
||||
The VM hardware identifies as **vresearch101ap** (BDID 0x90) in DFU mode, so the
|
||||
BuildManifest identity must use vresearch101ap fields for TSS/SHSH signing. However,
|
||||
runtime components use the **vphone600** variant because:
|
||||
- Its DeviceTree sets MKB `dt=1` (allows boot without system keybag)
|
||||
- Its SEP firmware matches the vphone600 device tree
|
||||
- `hardware target` reports as `vphone600ap` → proper iPhone emulation
|
||||
|
||||
`fw_prepare.sh` downloads both IPSWs, merges cloudOS firmware into the iPhone
|
||||
restore directory, then `fw_manifest.py` generates the hybrid BuildManifest.
|
||||
|
||||
---
|
||||
|
||||
## Component Source Table
|
||||
|
||||
### Boot Chain (from PCC vresearch101ap)
|
||||
|
||||
| Component | Source Identity | File | Patches Applied |
|
||||
|-----------|---------------|------|-----------------|
|
||||
| **AVPBooter** | PCC vresearch1 | `AVPBooter*.bin` (vm dir) | DGST validation bypass (`mov x0, #0`) |
|
||||
| **iBSS** | PROD (vresearch101ap release) | `Firmware/dfu/iBSS.vresearch101.RELEASE.im4p` | Serial labels + image4 callback bypass |
|
||||
| **iBEC** | PROD (vresearch101ap release) | `Firmware/dfu/iBEC.vresearch101.RELEASE.im4p` | Serial labels + image4 callback + boot-args |
|
||||
| **LLB** | PROD (vresearch101ap release) | `Firmware/all_flash/LLB.vresearch101.RELEASE.im4p` | Serial labels + image4 callback + boot-args + rootfs + panic (6 patches) |
|
||||
| **iBoot** | RES (vresearch101ap research) | `Firmware/all_flash/iBoot.vresearch101.RESEARCH_RELEASE.im4p` | Not patched (only research identity carries iBoot) |
|
||||
|
||||
### Security Monitors (from PCC, shared across board configs)
|
||||
|
||||
| Component | Source Identity | File | Patches Applied |
|
||||
|-----------|---------------|------|-----------------|
|
||||
| **Ap,RestoreSecurePageTableMonitor** | PROD | `Firmware/sptm.vresearch1.release.im4p` | Not patched |
|
||||
| **Ap,RestoreTrustedExecutionMonitor** | PROD | `Firmware/txm.iphoneos.release.im4p` | Not patched |
|
||||
| **Ap,SecurePageTableMonitor** | PROD | `Firmware/sptm.vresearch1.release.im4p` | Not patched |
|
||||
| **Ap,TrustedExecutionMonitor** | RES (research) | `Firmware/txm.iphoneos.research.im4p` | Trustcache bypass (`mov x0, #0` at 0x2C1F8) |
|
||||
|
||||
### Runtime Components (from PCC vphone600ap)
|
||||
|
||||
| Component | Source Identity | File | Patches Applied |
|
||||
|-----------|---------------|------|-----------------|
|
||||
| **DeviceTree** | VP (vphone600ap release) | `Firmware/all_flash/DeviceTree.vphone600ap.im4p` | Not patched |
|
||||
| **RestoreDeviceTree** | VP | `Firmware/all_flash/DeviceTree.vphone600ap.im4p` | Not patched |
|
||||
| **SEP** | VP | `Firmware/all_flash/sep-firmware.vphone600.RELEASE.im4p` | Not patched |
|
||||
| **RestoreSEP** | VP | `Firmware/all_flash/sep-firmware.vphone600.RELEASE.im4p` | Not patched |
|
||||
| **KernelCache** | VPR (vphone600ap research) | `kernelcache.research.vphone600` | 25 dynamic patches via KernelPatcher |
|
||||
| **RestoreKernelCache** | VP (vphone600ap release) | `kernelcache.release.vphone600` | Not patched (used during restore only) |
|
||||
| **RecoveryMode** | VP | `Firmware/all_flash/recoverymode@2556~iphone-USBc.im4p` | Not patched |
|
||||
|
||||
> **Important**: KernelCache (installed to disk, patched) uses the **research** variant.
|
||||
> RestoreKernelCache (used during restore process only) uses the **release** variant.
|
||||
> Only vphone600ap identities carry RecoveryMode — vresearch101ap does not.
|
||||
|
||||
### OS / Filesystem (from iPhone)
|
||||
|
||||
| Component | Source | Notes |
|
||||
|-----------|--------|-------|
|
||||
| **OS** | iPhone `iPhone17,3` erase identity | iPhone OS image |
|
||||
| **SystemVolume** | iPhone erase | Root hash |
|
||||
| **StaticTrustCache** | iPhone erase | Static trust cache |
|
||||
| **Ap,SystemVolumeCanonicalMetadata** | iPhone erase | Metadata / mtree |
|
||||
|
||||
### Ramdisk (from PCC)
|
||||
|
||||
| Component | Source | Notes |
|
||||
|-----------|--------|-------|
|
||||
| **RestoreRamDisk** | PROD (vresearch101ap release) | CloudOS erase ramdisk |
|
||||
| **RestoreTrustCache** | PROD | Ramdisk trust cache |
|
||||
|
||||
---
|
||||
|
||||
## Patched Components Summary
|
||||
|
||||
All 6 patched components in `fw_patch.py` come from **PCC (cloudOS)**:
|
||||
|
||||
| # | Component | Source Board | Patch Count | Purpose |
|
||||
|---|-----------|-------------|-------------|---------|
|
||||
| 1 | AVPBooter | vresearch1 | 1 | Bypass DGST signature validation |
|
||||
| 2 | iBSS | vresearch101 | 2 | Enable serial output + bypass image4 verification |
|
||||
| 3 | iBEC | vresearch101 | 3 | Enable serial + bypass image4 + inject boot-args |
|
||||
| 4 | LLB | vresearch101 | 6 | Serial + image4 + boot-args + rootfs mount + panic handler |
|
||||
| 5 | TXM | shared (iphoneos) | 1 | Bypass trustcache validation |
|
||||
| 6 | KernelCache | vphone600 | 25 | APFS seal, MAC policy, debugger, launch constraints, etc. |
|
||||
|
||||
All 4 CFW-patched binaries in `patch_cfw.py` / `install_cfw.sh` come from **iPhone**:
|
||||
|
||||
| # | Binary | Source | Purpose |
|
||||
|---|--------|--------|---------|
|
||||
| 1 | seputil | iPhone (Cryptex SystemOS) | Gigalocker UUID patch (`/%s.gl` → `/AA.gl`) |
|
||||
| 2 | launchd_cache_loader | iPhone (Cryptex SystemOS) | NOP cache validation check |
|
||||
| 3 | mobileactivationd | iPhone (Cryptex SystemOS) | Force `should_hactivate` to return true |
|
||||
| 4 | launchd.plist | iPhone (Cryptex SystemOS) | Inject bash/dropbear/trollvnc daemons |
|
||||
|
||||
---
|
||||
|
||||
## Why vphone600 Runtime Components?
|
||||
|
||||
The vresearch101ap device tree causes a **fatal keybag error** during boot:
|
||||
```
|
||||
MKB_INIT: dt = 0, bootarg = 0
|
||||
MKB_INIT: FATAL KEYBAG ERROR: failed to load system bag
|
||||
REBOOTING INTO RECOVERY MODE.
|
||||
```
|
||||
|
||||
The vphone600ap device tree sets `dt=1`, allowing boot without a pre-existing
|
||||
system keybag:
|
||||
```
|
||||
MKB_INIT: dt = 1, bootarg = 0
|
||||
MKB_INIT: No system keybag loaded.
|
||||
```
|
||||
|
||||
The SEP firmware must match the device tree (vphone600 SEP with vphone600 DT).
|
||||
|
||||
---
|
||||
|
||||
## Build Identity (Single DFU Erase)
|
||||
|
||||
Since vphone-cli always boots via DFU restore, only one Build Identity is needed.
|
||||
|
||||
### Identity Metadata (must match DFU hardware for TSS)
|
||||
```
|
||||
DeviceClass = vresearch101ap
|
||||
Variant = Darwin Cloud Customer Erase Install (IPSW)
|
||||
Ap,ProductType = ComputeModule14,2
|
||||
Ap,Target = VRESEARCH101AP
|
||||
Ap,TargetType = vresearch101
|
||||
ApBoardID = 0x90
|
||||
ApChipID = 0xFE01
|
||||
FDRSupport = False
|
||||
```
|
||||
|
||||
### Identity Source Map (fw_manifest.py variables)
|
||||
```
|
||||
PROD = vresearch101ap release — boot chain, SPTM, ramdisk
|
||||
RES = vresearch101ap research — iBoot, TXM (research)
|
||||
VP = vphone600ap release — DeviceTree, SEP, RestoreKernelCache, RecoveryMode
|
||||
VPR = vphone600ap research — KernelCache (research, patched by fw_patch.py)
|
||||
I_ERASE = iPhone erase identity — OS image, trust caches, system volume
|
||||
```
|
||||
|
||||
### Manifest Components (21 total)
|
||||
```
|
||||
LLB ← PROD
|
||||
iBSS ← PROD
|
||||
iBEC ← PROD
|
||||
iBoot ← RES
|
||||
Ap,RestoreSecurePageTableMonitor ← PROD
|
||||
Ap,RestoreTrustedExecutionMonitor← PROD
|
||||
Ap,SecurePageTableMonitor ← PROD
|
||||
Ap,TrustedExecutionMonitor ← RES
|
||||
DeviceTree ← VP
|
||||
RestoreDeviceTree ← VP
|
||||
SEP ← VP
|
||||
RestoreSEP ← VP
|
||||
KernelCache ← VPR (research, patched)
|
||||
RestoreKernelCache ← VP (release, unpatched)
|
||||
RecoveryMode ← VP
|
||||
RestoreRamDisk ← PROD
|
||||
RestoreTrustCache ← PROD
|
||||
Ap,SystemVolumeCanonicalMetadata ← I_ERASE
|
||||
OS ← I_ERASE
|
||||
StaticTrustCache ← I_ERASE
|
||||
SystemVolume ← I_ERASE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
**Boot chain = vresearch101 (matches DFU hardware); runtime = vphone600 (keybag-less boot); OS = iPhone.**
|
||||
|
||||
The firmware is a PCC shell wrapping an iPhone core. The vresearch101 boot chain
|
||||
handles DFU/TSS signing. The vphone600 device tree + SEP + kernel provide the
|
||||
runtime environment. The iPhone userland is patched post-install for activation
|
||||
bypass, jailbreak tools, and persistent SSH/VNC.
|
||||
@@ -1,203 +0,0 @@
|
||||
# Jailbreak Patches vs Base Patches
|
||||
|
||||
Comparison of base boot-chain patches (`make fw_patch`) vs jailbreak-extended patches (`make fw_patch_jb`).
|
||||
|
||||
Base patches enable VM boot with signature bypass and SSV override.
|
||||
Jailbreak patches add code signing bypass, entitlement spoofing, task/VM security bypass,
|
||||
sandbox hook neutralization, and kernel arbitrary call (kcall10).
|
||||
|
||||
## iBSS
|
||||
|
||||
| # | Patch | Purpose | Base | JB |
|
||||
| --- | --------------------------------- | --------------------------------------- | :--: | :-: |
|
||||
| 1 | Serial labels (2x) | "Loaded iBSS" in serial log | Y | Y |
|
||||
| 2 | image4_validate_property_callback | Signature bypass (nop b.ne + mov x0,#0) | Y | Y |
|
||||
| 3 | Skip generate_nonce | Keep apnonce stable for SHSH | — | Y |
|
||||
|
||||
## iBEC
|
||||
|
||||
| # | Patch | Purpose | Base | JB |
|
||||
| --- | --------------------------------- | ------------------------------ | :--: | :-: |
|
||||
| 1 | Serial labels (2x) | "Loaded iBEC" in serial log | Y | Y |
|
||||
| 2 | image4_validate_property_callback | Signature bypass | Y | Y |
|
||||
| 3 | Boot-args redirect | `serial=3 -v debug=0x2014e %s` | Y | Y |
|
||||
|
||||
No additional JB patches for iBEC.
|
||||
|
||||
## LLB
|
||||
|
||||
| # | Patch | Purpose | Base | JB |
|
||||
| --- | --------------------------------- | ---------------------------------- | :--: | :-: |
|
||||
| 1 | Serial labels (2x) | "Loaded LLB" in serial log | Y | Y |
|
||||
| 2 | image4_validate_property_callback | Signature bypass | Y | Y |
|
||||
| 3 | Boot-args redirect | `serial=3 -v debug=0x2014e %s` | Y | Y |
|
||||
| 4 | Rootfs bypass (5 patches) | Allow edited rootfs loading | Y | Y |
|
||||
| 5 | Panic bypass | NOP cbnz after mov w8,#0x328 check | Y | Y |
|
||||
|
||||
No additional JB patches for LLB.
|
||||
|
||||
## TXM
|
||||
|
||||
| # | Patch | Purpose | Base | JB |
|
||||
| --- | ------------------------------------------ | --------------------------------- | :------------------------------------------: | :-: | --- |
|
||||
| 1 | Trustcache binary search bypass | `bl hash_cmp → mov x0,#0` | Y | Y |
|
||||
| 2 | CodeSignature selector 24 (3x mov x0,#0) | Bypass CS validation return paths | — | Y |
|
||||
| 3 | CodeSignature selector 24 | 0xA1 (2x nop) | Bypass CS error path | — | Y |
|
||||
| 4 | get-task-allow (selector 41 | 29) | `mov x0,#1` — allow get-task-allow | — | Y |
|
||||
| 5 | Selector 42 | 29 + shellcode | Branch to shellcode that sets flag + returns | — | Y |
|
||||
| 6 | com.apple.private.cs.debugger (selector 42 | 37) | `mov w0,#1` — allow debugger entitlement | — | Y |
|
||||
| 7 | Developer mode bypass | NOP developer mode enforcement | — | Y |
|
||||
|
||||
## Kernelcache
|
||||
|
||||
### Base patches (SSV + basic AMFI + sandbox)
|
||||
|
||||
| # | Patch | Function | Purpose | Base | JB |
|
||||
| ----- | ------------------------ | -------------------------------- | ------------------------------------- | :--: | :-: |
|
||||
| 1 | NOP panic | `_apfs_vfsop_mount` | Skip "root snapshot" panic | Y | Y |
|
||||
| 2 | NOP panic | `_authapfs_seal_is_broken` | Skip "root volume seal" panic | Y | Y |
|
||||
| 3 | NOP panic | `_bsd_init` | Skip "rootvp not authenticated" panic | Y | Y |
|
||||
| 4-5 | mov w0,#0; ret | `_proc_check_launch_constraints` | Bypass launch constraints | Y | Y |
|
||||
| 6-7 | mov x0,#1 (2x) | `PE_i_can_has_debugger` | Enable kernel debugger | Y | Y |
|
||||
| 8 | NOP | `_postValidation` | Skip AMFI post-validation | Y | Y |
|
||||
| 9 | cmp w0,w0 | `_postValidation` | Force comparison true | Y | Y |
|
||||
| 10-11 | mov w0,#1 (2x) | `_check_dyld_policy_internal` | Allow dyld loading | Y | Y |
|
||||
| 12 | mov w0,#0 | `_apfs_graft` | Allow APFS graft | Y | Y |
|
||||
| 13 | cmp x0,x0 | `_apfs_vfsop_mount` | Skip mount check | Y | Y |
|
||||
| 14 | mov w0,#0 | `_apfs_mount_upgrade_checks` | Allow mount upgrade | Y | Y |
|
||||
| 15 | mov w0,#0 | `_handle_fsioc_graft` | Allow fsioc graft | Y | Y |
|
||||
| 16-25 | mov x0,#0; ret (5 hooks) | Sandbox MACF ops table | Stub 5 sandbox hooks | Y | Y |
|
||||
|
||||
### Jailbreak-only kernel patches
|
||||
|
||||
| # | Patch | Function | Purpose | Base | JB |
|
||||
| --- | -------------------------- | ------------------------------------ | ------------------------------------------ | :--: | :-: |
|
||||
| 26 | Rewrite function | `AMFIIsCDHashInTrustCache` | Always return true + store hash | — | Y |
|
||||
| 27 | Shellcode + branch | `_cred_label_update_execve` | Set cs_flags (platform+entitlements) | — | Y |
|
||||
| 28 | cmp w0,w0 | `_postValidation` (additional) | Force validation pass | — | Y |
|
||||
| 29 | Shellcode + branch | `_syscallmask_apply_to_proc` | Patch zalloc_ro_mut for syscall mask | — | Y |
|
||||
| 30 | Shellcode + ops redirect | `_hook_cred_label_update_execve` | vnode_getattr ownership propagation + suid | — | Y |
|
||||
| 31 | mov x0,#0; ret (20+ hooks) | Sandbox MACF ops table (extended) | Stub remaining 20+ sandbox hooks | — | Y |
|
||||
| 32 | cmp xzr,xzr | `_task_conversion_eval_internal` | Allow task conversion | — | Y |
|
||||
| 33 | mov x0,#0; ret | `_proc_security_policy` | Bypass security policy | — | Y |
|
||||
| 34 | NOP (2x) | `_proc_pidinfo` | Allow pid 0 info | — | Y |
|
||||
| 35 | b (skip panic) | `_convert_port_to_map_with_flavor` | Skip kernel map panic | — | Y |
|
||||
| 36 | NOP | `_vm_fault_enter_prepare` | Skip fault check | — | Y |
|
||||
| 37 | b (skip check) | `_vm_map_protect` | Allow VM protect | — | Y |
|
||||
| 38 | NOP + mov x8,xzr | `___mac_mount` | Bypass MAC mount check | — | Y |
|
||||
| 39 | NOP | `_dounmount` | Allow unmount | — | Y |
|
||||
| 40 | mov x0,#0 | `_bsd_init` (2nd) | Skip auth at @%s:%d | — | Y |
|
||||
| 41 | NOP (2x) | `_spawn_validate_persona` | Skip persona validation | — | Y |
|
||||
| 42 | NOP | `_task_for_pid` | Allow task_for_pid | — | Y |
|
||||
| 43 | b (skip check) | `_load_dylinker` | Allow dylinker loading | — | Y |
|
||||
| 44 | cmp x0,x0 | `_shared_region_map_and_slide_setup` | Force shared region | — | Y |
|
||||
| 45 | NOP | `_verifyPermission` (NVRAM) | Allow NVRAM writes | — | Y |
|
||||
| 46 | b (skip check) | `_IOSecureBSDRoot` | Skip secure root check | — | Y |
|
||||
| 47 | Syscall 439 + shellcode | kcall10 (SYS_kas_info replacement) | Kernel arbitrary call from userspace | — | Y |
|
||||
| 48 | Zero out | `_thid_should_crash` | Prevent GUARD_TYPE_MACH_PORT crash | — | Y |
|
||||
|
||||
## CFW (cfw_install)
|
||||
|
||||
| # | Patch | Binary | Purpose | Base | JB |
|
||||
| --- | -------------------- | -------------------- | ------------------------------ | :--: | :-: |
|
||||
| 1 | /%s.gl → /AA.gl | seputil | Gigalocker UUID fix | Y | Y |
|
||||
| 2 | NOP cache validation | launchd_cache_loader | Allow modified launchd.plist | Y | Y |
|
||||
| 3 | mov x0,#1; ret | mobileactivationd | Activation bypass | Y | Y |
|
||||
| 4 | Plist injection | launchd.plist | bash/dropbear/trollvnc daemons | Y | Y |
|
||||
| 5 | b (skip jetsam) | launchd | Prevent jetsam panic on boot | — | Y |
|
||||
| 6 | procursus bootstrap | `/mnt5/<hash>/jb-vphone` | Install procursus userspace + optional Sileo payload | — | Y |
|
||||
|
||||
### JB Install Flow (`make cfw_install_jb`)
|
||||
|
||||
- Entry: `scripts/cfw_install_jb.sh` (wrapper) -> `scripts/cfw_install.sh` with `CFW_JB_MODE=1`.
|
||||
- Added JB phases in install pipeline:
|
||||
- `JB-1`: patch `/mnt1/sbin/launchd` via `patch-launchd-jetsam` (dynamic string+xref).
|
||||
- `JB-2`: unpack procursus bootstrap (`bootstrap-iphoneos-arm64.tar.zst`) into `/mnt5/<bootManifestHash>/jb-vphone/procursus`.
|
||||
- JB resources now packaged in:
|
||||
- `scripts/resources/cfw_jb_input.tar.zst`
|
||||
- contains:
|
||||
- `jb/bootstrap-iphoneos-arm64.tar.zst`
|
||||
- `jb/org.coolstar.sileo_2.5.1_iphoneos-arm64.deb`
|
||||
|
||||
## Summary
|
||||
|
||||
| Binary | Base | JB-only | Total |
|
||||
| ----------- | :----: | :------: | :------: |
|
||||
| iBSS | 2 | 1 | 3 |
|
||||
| iBEC | 3 | 0 | 3 |
|
||||
| LLB | 6 | 0 | 6 |
|
||||
| TXM | 1 | ~13 | ~14 |
|
||||
| Kernelcache | 25 | ~23+ | ~48+ |
|
||||
| CFW | 4 | 1 | 5 |
|
||||
| **Total** | **41** | **~38+** | **~79+** |
|
||||
|
||||
## Dynamic Implementation Log (fw_patch_jb)
|
||||
|
||||
### TXM (Completed)
|
||||
|
||||
All TXM JB patches are now implemented with dynamic binary analysis and
|
||||
keystone/capstone-encoded instructions only.
|
||||
|
||||
1. `selector24 hashcmp` (`bl -> mov x0,#0`, 2 residual sites in JB stage)
|
||||
- Locator: global instruction motif `mov w2,#0x14 ; bl ; cbz w0`.
|
||||
- Patch bytes: keystone `mov x0, #0`.
|
||||
2. `selector24 A1` (`b.lo/cbz -> nop`)
|
||||
- Locator: unique guarded `mov w0,#0xa1` site with nearby `b.lo` and `cbz x9`.
|
||||
- Patch bytes: keystone `nop`.
|
||||
3. `selector41|29 get-task-allow`
|
||||
- Locator: xref to `"get-task-allow"` + nearby `bl` followed by `tbnz w0,#0`.
|
||||
- Patch bytes: keystone `mov x0, #1`.
|
||||
4. `selector42|29 shellcode trampoline`
|
||||
- Locator:
|
||||
- Find dispatch stub pattern `bti j ; mov x0,x20 ; bl ; mov x1,x21 ; mov x2,x22 ; bl ; b`.
|
||||
- Select stub whose second `bl` target is the debugger-gate function (pattern verified by string-xref + call-shape).
|
||||
- Find executable UDF cave dynamically.
|
||||
- Patch bytes:
|
||||
- Stub head -> keystone `b #cave`.
|
||||
- Cave payload -> `nop ; mov x0,#1 ; strb w0,[x20,#0x30] ; mov x0,x20 ; b #return`.
|
||||
5. `selector42|37 debugger entitlement`
|
||||
- Locator: xref to `"com.apple.private.cs.debugger"` + strict nearby call-shape
|
||||
(`mov x0,#0 ; mov x2,#0 ; bl ; tbnz w0,#0`).
|
||||
- Patch bytes: keystone `mov w0, #1`.
|
||||
6. `developer mode bypass`
|
||||
- Locator: xref to `"developer mode enabled due to system policy configuration"`
|
||||
+ nearest guard branch on `w9`.
|
||||
- Patch bytes: keystone `nop`.
|
||||
|
||||
#### TXM Binary-Alignment Validation
|
||||
|
||||
- `patch.upstream.raw` generated from upstream-equivalent TXM static patch semantics.
|
||||
- `patch.dyn.raw` generated by `TXMJBPatcher` on the same input.
|
||||
- Result: byte-identical (`cmp -s` success, SHA-256 matched).
|
||||
|
||||
### Kernelcache (In Progress, Dynamic Ports Added)
|
||||
|
||||
Implemented in `scripts/patchers/kernel_jb.py` with capstone semantic matching
|
||||
and keystone-generated patch bytes only:
|
||||
|
||||
1. `AMFIIsCDHashInTrustCache` function rewrite
|
||||
- Locator: semantic function-body matcher in AMFI text.
|
||||
- Patch: `mov x0,#1 ; cbz x2,+8 ; str x0,[x2] ; ret`.
|
||||
2. AMFI execve kill path bypass (2 BL sites)
|
||||
- Locator: string xref to `"AMFI: hook..execve() killing"` (fallback `"execve() killing"`),
|
||||
then function-local early `bl` + `cbz/cbnz w0` pair matcher.
|
||||
- Patch: `bl -> mov x0,#0` at two helper callsites.
|
||||
3. `task_conversion_eval_internal` guard bypass
|
||||
- Locator: unique cmp/branch motif:
|
||||
`ldr xN,[xN,#imm] ; cmp xN,x0 ; b.eq ; cmp xN,x1 ; b.eq`.
|
||||
- Patch: `cmp xN,x0 -> cmp xzr,xzr`.
|
||||
4. Extended sandbox MACF hook stubs (JB-only set)
|
||||
- Locator: dynamic `mac_policy_conf -> mpc_ops` discovery, then hook-index resolution.
|
||||
- Patch per hook function: `mov x0,#0 ; ret`.
|
||||
- JB extended indices include vnode/proc hooks beyond base 5 hooks.
|
||||
|
||||
#### Cross-Version Dynamic Snapshot
|
||||
|
||||
Validated using pristine inputs from `updates-cdn/`:
|
||||
|
||||
| Case | TXM_JB_PATCHES | KERNEL_JB_PATCHES |
|
||||
|------|----------------:|------------------:|
|
||||
| PCC 26.1 (`23B85`) | 14 | 59 |
|
||||
| PCC 26.3 (`23D128`) | 14 | 59 |
|
||||
| iOS 26.1 (`23B85`) | 14 | 59 |
|
||||
| iOS 26.3 (`23D127`) | 14 | 59 |
|
||||
@@ -1,480 +0,0 @@
|
||||
# TXM Jailbreak Patch Analysis
|
||||
|
||||
Analysis of 13 TXM jailbreak patches applied by `txm_jb.py` on the RESEARCH variant
|
||||
of TXM from iPhone17,3 / PCC-CloudOS 26.x.
|
||||
|
||||
## Address Mapping
|
||||
|
||||
| Segment | VM Address | File Offset | Size |
|
||||
|---------|------------|-------------|------|
|
||||
| `__TEXT_EXEC` | `0xFFFFFFF017020000` | `0x1c000` | `0x44000` |
|
||||
| `__TEXT_BOOT_EXEC` | `0xFFFFFFF017064000` | `0x60000` | `0xc000` |
|
||||
|
||||
Conversion: `VA = file_offset - 0x1c000 + 0xFFFFFFF017020000` (for `__TEXT_EXEC`)
|
||||
|
||||
---
|
||||
|
||||
## TXM Selector Dispatch
|
||||
|
||||
All TXM operations enter through a single dispatch function (`sub_FFFFFFF01702AE80`),
|
||||
a large switch on the selector number (1–51). Each case validates arguments and calls
|
||||
a dedicated handler. Relevant selectors:
|
||||
|
||||
| Selector | Handler | Purpose |
|
||||
|----------|---------|---------|
|
||||
| 24 | `sub_FFFFFFF017024834` → validation chain | CodeSignature validation |
|
||||
| 41 | `sub_FFFFFFF017023558` | Process entitlement setup (get-task-allow) |
|
||||
| 42 | `sub_FFFFFFF017023368` | Debug memory mapping |
|
||||
| — | `sub_FFFFFFF017023A20` | Developer mode configuration (called during init) |
|
||||
|
||||
The dispatcher passes raw page pointers through `sub_FFFFFFF0170280A4` (a bounds
|
||||
validator that returns the input pointer unchanged) before calling handlers.
|
||||
|
||||
---
|
||||
|
||||
## Patch 1–2: CodeSignature Hash Comparison Bypass (selector 24)
|
||||
|
||||
**Error**: `TXM [Error]: CodeSignature: selector: 24 | 0xA1 | 0x30 | 1`
|
||||
|
||||
### Addresses
|
||||
|
||||
| File Offset | VA | Original Instruction | Patch |
|
||||
|---|---|---|---|
|
||||
| `0x313ec` | `0xFFFFFFF0170353EC` | `LDR X1, [X20, #0x38]` | NOP |
|
||||
| `0x313f4` | `0xFFFFFFF0170353F4` | `BL sub_FFFFFFF0170335F8` | NOP |
|
||||
|
||||
### Function: `sub_FFFFFFF0170353B8` — CS hash flags validator
|
||||
|
||||
**Call chain**: selector 24 → `sub_FFFFFFF017024834` (CS handler) →
|
||||
`sub_FFFFFFF0170356F8` (CS validation pipeline) → `sub_FFFFFFF017035A00`
|
||||
(multi-step validation, step 4 of 8) → `sub_FFFFFFF0170353B8`
|
||||
|
||||
### Decompiled (pre-patch)
|
||||
|
||||
```c
|
||||
// sub_FFFFFFF0170353B8(manifest_ptr, version)
|
||||
__int64 __fastcall sub_FFFFFFF0170353B8(__int64 **a1, unsigned int a2)
|
||||
{
|
||||
__int64 v4 = **a1;
|
||||
__int64 v7 = 0; // hash data pointer
|
||||
int v6 = 0; // hash flags
|
||||
|
||||
// Patch 1: NOP removes arg load (LDR X1, [X20, #0x38])
|
||||
// Patch 2: NOP removes this call entirely:
|
||||
sub_FFFFFFF0170335F8(a1[6], a1[7], &v6); // extract hash flags from CS blob
|
||||
sub_FFFFFFF017033718(a1[6], a1[7], &v7); // extract hash data pointer
|
||||
|
||||
if ( a2 >= 6 && *(v4 + 8) )
|
||||
return 0xA1; // 161
|
||||
|
||||
// Critical comparison: does hash presence match flags?
|
||||
if ( (v7 != 0) == ((v6 & 2) >> 1) )
|
||||
return 0x130A1; // 77985 — hash mismatch
|
||||
|
||||
// ... further version-dependent checks return 0xA1 or 0x22DA1
|
||||
}
|
||||
```
|
||||
|
||||
### What `sub_FFFFFFF0170335F8` does
|
||||
|
||||
Extracts hash flags from the CodeSignature blob header. Reads `bswap32(*(blob + 12))`
|
||||
into the output parameter (the flags bitmask). Bit 1 of the flags indicates whether
|
||||
a code hash is present.
|
||||
|
||||
### What `sub_FFFFFFF017033718` does
|
||||
|
||||
Locates the hash data within the CodeSignature blob. Validates blob header version
|
||||
(`bswap32(*(blob+8)) >> 9 >= 0x101`), then follows a length-prefixed string pointer
|
||||
at offset 48 to find the hash data. Returns the hash data pointer via output param.
|
||||
|
||||
### Effect of NOP
|
||||
|
||||
With `sub_FFFFFFF0170335F8` NOPed, `v6` stays at its initialized value of **0**.
|
||||
This means `(v6 & 2) >> 1 = 0` (hash-present flag is cleared). As long as
|
||||
`sub_FFFFFFF017033718` returns a non-null hash pointer (`v7 != 0`), the comparison
|
||||
becomes `(1 == 0)` → **false**, so the `0x130A1` error is skipped. The function
|
||||
falls through to the version checks which return success for version ≤ 5.
|
||||
|
||||
This effectively bypasses CodeSignature hash validation — the hash data exists
|
||||
in the blob but the hash-present flag is suppressed, so the consistency check passes.
|
||||
|
||||
### `txm_jb.py` dynamic finder: `patch_selector24_hashcmp_calls()`
|
||||
|
||||
Scans for the instruction pattern `mov w2, #0x14 / bl X / cbz w0, Y` to find
|
||||
hashcmp BL callsites, then patches the BL to `mov x0, #0`. This also includes
|
||||
`patch_selector24_a1_path()` which NOPs the `b.lo` and `cbz x9` guards around
|
||||
the `mov w0, #0xa1` error path.
|
||||
|
||||
---
|
||||
|
||||
## Patch 3: get-task-allow Force True (selector 41)
|
||||
|
||||
**Error**: `TXM [Error]: selector: 41 | 29`
|
||||
|
||||
### Address
|
||||
|
||||
| File Offset | VA | Original Instruction | Patch |
|
||||
|---|---|---|---|
|
||||
| `0x1f5d4` | `0xFFFFFFF0170235D4` | `BL sub_FFFFFFF017022A30` | `MOV X0, #1` |
|
||||
|
||||
### Function: `sub_FFFFFFF017023558` — selector 41 handler
|
||||
|
||||
**Call chain**: selector 41 → `sub_FFFFFFF0170280A4` (ptr validation) →
|
||||
`sub_FFFFFFF017023558`
|
||||
|
||||
### Decompiled (pre-patch)
|
||||
|
||||
```c
|
||||
// sub_FFFFFFF017023558(manifest)
|
||||
__int64 __fastcall sub_FFFFFFF017023558(__int64 a1)
|
||||
{
|
||||
// Check developer mode is enabled (byte_FFFFFFF017070F24)
|
||||
if ( (byte_FFFFFFF017070F24 & 1) == 0 )
|
||||
return 27; // developer mode not enabled
|
||||
|
||||
// Check license-to-operate entitlement (always first)
|
||||
sub_FFFFFFF017022A30(0, "research.com.apple.license-to-operate", 0);
|
||||
|
||||
// Lock manifest
|
||||
sub_FFFFFFF017027074(a1, 0, 0);
|
||||
|
||||
if ( *(a1 + 36) == 1 ) // special manifest type
|
||||
goto error_path; // return via panic(0x81)
|
||||
|
||||
// === PATCHED INSTRUCTION ===
|
||||
// Original: BL sub_FFFFFFF017022A30 — entitlement_lookup(manifest, "get-task-allow", 0)
|
||||
// Patched: MOV X0, #1
|
||||
if ( (sub_FFFFFFF017022A30(a1, "get-task-allow", 0) & 1) != 0 ) // TBNZ w0, #0
|
||||
{
|
||||
v3 = 0; // success
|
||||
*(a1 + 0x30) = 1; // set get-task-allow flag on manifest
|
||||
}
|
||||
else
|
||||
{
|
||||
v3 = 29; // ERROR 29: no get-task-allow entitlement
|
||||
}
|
||||
|
||||
sub_FFFFFFF01702717C(a1, 0); // unlock manifest
|
||||
return v3;
|
||||
}
|
||||
```
|
||||
|
||||
### Assembly at patch site
|
||||
|
||||
```asm
|
||||
FFFFFFF0170235C4 ADRL X1, "get-task-allow"
|
||||
FFFFFFF0170235CC MOV X0, X19 ; manifest object
|
||||
FFFFFFF0170235D0 MOV X2, #0
|
||||
FFFFFFF0170235D4 BL sub_FFFFFFF017022A30 ; <-- PATCHED to MOV X0, #1
|
||||
FFFFFFF0170235D8 TBNZ W0, #0, loc_... ; always taken when x0=1
|
||||
```
|
||||
|
||||
### Effect
|
||||
|
||||
Replaces the entitlement lookup call with a constant `1`. The subsequent `TBNZ W0, #0`
|
||||
always takes the branch to the success path, which sets `*(manifest + 0x30) = 1`
|
||||
(the get-task-allow flag byte). Every process now has get-task-allow, enabling
|
||||
debugging via `task_for_pid` and LLDB attach.
|
||||
|
||||
### What `sub_FFFFFFF017022A30` does
|
||||
|
||||
Universal entitlement lookup function. When `a1 != 0`, it resolves the manifest's
|
||||
entitlement dictionary and searches for the named key via `sub_FFFFFFF017036294`.
|
||||
Returns a composite status word where bit 0 indicates the entitlement was found.
|
||||
|
||||
### `txm_jb.py` dynamic finder: `patch_get_task_allow_force_true()`
|
||||
|
||||
Searches for string refs to `"get-task-allow"`, then scans forward for the pattern
|
||||
`BL X / TBNZ w0, #0, Y`. Patches the BL to `MOV X0, #1`.
|
||||
|
||||
---
|
||||
|
||||
## Patch 4: selector 42|29 Shellcode (Debug Mapping Gate)
|
||||
|
||||
**Error**: `TXM [Error]: selector: 42 | 29`
|
||||
|
||||
### Addresses
|
||||
|
||||
| File Offset | VA | Patch |
|
||||
|---|---|---|
|
||||
| `0x2717c` | `0xFFFFFFF01702B17C` | `B #0x36238` (→ shellcode) |
|
||||
| `0x5d3b4` | `0xFFFFFFF0170613B4` | `NOP` (pad) |
|
||||
| `0x5d3b8` | `0xFFFFFFF0170613B8` | `MOV X0, #1` |
|
||||
| `0x5d3bc` | `0xFFFFFFF0170613BC` | `STRB W0, [X20, #0x30]` |
|
||||
| `0x5d3c0` | `0xFFFFFFF0170613C0` | `MOV X0, X20` |
|
||||
| `0x5d3c4` | `0xFFFFFFF0170613C4` | `B #-0x36244` (→ 0xB180) |
|
||||
|
||||
### Context: Dispatcher case 42
|
||||
|
||||
```asm
|
||||
; jumptable case 42 entry in sub_FFFFFFF01702AE80:
|
||||
FFFFFFF01702B178 BTI j
|
||||
FFFFFFF01702B17C MOV X0, X20 ; <-- PATCHED to B shellcode
|
||||
FFFFFFF01702B180 BL sub_FFFFFFF0170280A4 ; validate pointer
|
||||
FFFFFFF01702B184 MOV X1, X21
|
||||
FFFFFFF01702B188 MOV X2, X22
|
||||
FFFFFFF01702B18C BL sub_FFFFFFF017023368 ; selector 42 handler
|
||||
FFFFFFF01702B190 B loc_FFFFFFF01702B344 ; return result
|
||||
```
|
||||
|
||||
### Shellcode (at zero-filled code cave in `__TEXT_EXEC`)
|
||||
|
||||
```asm
|
||||
; 0xFFFFFFF0170613B4 — cave was all zeros
|
||||
NOP ; pad (original 0x00000000)
|
||||
MOV X0, #1 ; value to store
|
||||
STRB W0, [X20, #0x30] ; force manifest->get_task_allow = 1
|
||||
MOV X0, X20 ; restore original instruction (was at 0xB17C)
|
||||
B #-0x36244 ; jump back to 0xFFFFFFF01702B180 (BL validate)
|
||||
```
|
||||
|
||||
### Why this is needed
|
||||
|
||||
Selector 42's handler `sub_FFFFFFF017023368` checks the get-task-allow byte early:
|
||||
|
||||
```c
|
||||
// sub_FFFFFFF017023368(manifest, addr, size)
|
||||
// ... after debugger entitlement check ...
|
||||
v8 = atomic_load((unsigned __int8 *)(a1 + 48)); // offset 0x30
|
||||
if ( (v8 & 1) == 0 )
|
||||
{
|
||||
v6 = 29; // ERROR 29: get-task-allow not set
|
||||
goto unlock_and_return;
|
||||
}
|
||||
// ... proceed with debug memory mapping ...
|
||||
```
|
||||
|
||||
Selector 41 (patch 3) sets this byte during entitlement validation, but
|
||||
there are code paths where selector 42 can be called before selector 41 has run
|
||||
for a given manifest. The shellcode ensures the flag is always set at the dispatch
|
||||
level before the handler even sees it.
|
||||
|
||||
### `sub_FFFFFFF0170280A4` — pointer validator
|
||||
|
||||
```c
|
||||
// Validates page alignment and bounds, returns input pointer unchanged
|
||||
unsigned __int64 sub_FFFFFFF0170280A4(unsigned __int64 a1) {
|
||||
if ( (a1 & ~0x3FFF) == 0 ) panic(64);
|
||||
if ( a1 >= 0xFFFFFFFFFFFFC000 ) panic(66);
|
||||
// ... bounds checks ...
|
||||
return (a1 & ~0x3FFF) + (a1 & 0x3FFF); // == a1
|
||||
}
|
||||
```
|
||||
|
||||
Since the validator returns the pointer unchanged, `x20` (raw arg) and the validated
|
||||
pointer both refer to the same object. The shellcode's `STRB W0, [X20, #0x30]`
|
||||
writes to the correct location.
|
||||
|
||||
### `txm_jb.py` dynamic finder: `patch_selector42_29_shellcode()`
|
||||
|
||||
1. Finds the "debugger gate function" via string refs to `"com.apple.private.cs.debugger"`
|
||||
2. Locates the dispatch stub by matching `BTI j / MOV X0, X20 / BL / MOV X1, X21 / MOV X2, X22 / BL debugger_gate / B`
|
||||
3. Finds a zero-filled code cave via `_find_udf_cave()` near the stub
|
||||
4. Emits the branch + shellcode + branch-back
|
||||
|
||||
---
|
||||
|
||||
## Patch 5: Debugger Entitlement Force True (selector 42)
|
||||
|
||||
**Error**: `TXM [Error]: selector: 42 | 37`
|
||||
|
||||
### Address
|
||||
|
||||
| File Offset | VA | Original Instruction | Patch |
|
||||
|---|---|---|---|
|
||||
| `0x1f3b8` | `0xFFFFFFF0170233B8` | `BL sub_FFFFFFF017022A30` | `MOV W0, #1` |
|
||||
|
||||
### Function: `sub_FFFFFFF017023368` — selector 42 handler (debug memory mapping)
|
||||
|
||||
### Assembly at patch site
|
||||
|
||||
```asm
|
||||
; Check com.apple.private.cs.debugger entitlement
|
||||
FFFFFFF0170233A8 ADRL X1, "com.apple.private.cs.debugger"
|
||||
FFFFFFF0170233B0 MOV X0, #0 ; check global manifest (a1=0)
|
||||
FFFFFFF0170233B4 MOV X2, #0
|
||||
FFFFFFF0170233B8 BL sub_FFFFFFF017022A30 ; <-- PATCHED to MOV W0, #1
|
||||
FFFFFFF0170233BC TBNZ W0, #0, loc_... ; always taken when w0=1
|
||||
FFFFFFF0170233C0 ADRL X8, fallback_flag ; secondary check (also bypassed)
|
||||
FFFFFFF0170233C8 LDRB W8, [X8, #offset]
|
||||
FFFFFFF0170233CC TBNZ W8, #0, loc_... ; secondary bypass path
|
||||
FFFFFFF0170233D0 ADRL X0, "disallowed non-debugger initiated debug mapping"
|
||||
FFFFFFF0170233D8 BL sub_FFFFFFF017025B7C ; log error
|
||||
FFFFFFF0170233DC MOV W20, #0x25 ; error 37
|
||||
FFFFFFF0170233E0 B unlock_return
|
||||
```
|
||||
|
||||
### Decompiled (pre-patch)
|
||||
|
||||
```c
|
||||
// First check in sub_FFFFFFF017023368 after input validation:
|
||||
if ( (sub_FFFFFFF017022A30(0, "com.apple.private.cs.debugger", 0) & 1) == 0 )
|
||||
{
|
||||
// Fallback: check a static byte flag
|
||||
if ( (fallback_flag & 1) == 0 )
|
||||
{
|
||||
log("disallowed non-debugger initiated debug mapping");
|
||||
return 37; // 0x25
|
||||
}
|
||||
}
|
||||
// Continue with debug mapping...
|
||||
```
|
||||
|
||||
### Effect
|
||||
|
||||
Replaces the entitlement lookup with `MOV W0, #1`. The `TBNZ W0, #0` always
|
||||
branches to the success path, bypassing both the entitlement check and the
|
||||
fallback flag check. This allows any process to create debug memory mappings
|
||||
regardless of whether it has `com.apple.private.cs.debugger`.
|
||||
|
||||
### `txm_jb.py` dynamic finder: `patch_debugger_entitlement_force_true()`
|
||||
|
||||
Searches for string refs to `"com.apple.private.cs.debugger"`, then matches
|
||||
the pattern: `mov x0, #0 / mov x2, #0 / bl X / tbnz w0, #0, Y`. Patches the BL
|
||||
to `MOV W0, #1`.
|
||||
|
||||
---
|
||||
|
||||
## Patch 6: Developer Mode Bypass
|
||||
|
||||
### Address
|
||||
|
||||
| File Offset | VA | Original Instruction | Patch |
|
||||
|---|---|---|---|
|
||||
| `0x1FA58` | `0xFFFFFFF017023A58` | `TBNZ W9, #0, loc_FFFFFFF017023A6C` | NOP |
|
||||
|
||||
### Function: `sub_FFFFFFF017023A20` — developer mode configuration
|
||||
|
||||
Called during TXM initialization to determine and store the developer mode state.
|
||||
The result is stored in `byte_FFFFFFF017070F24`, which is the gate flag checked by
|
||||
selector 41 (`sub_FFFFFFF017023558`).
|
||||
|
||||
### Assembly at patch site
|
||||
|
||||
```asm
|
||||
; Check system policy configuration
|
||||
FFFFFFF017023A50 LDR X9, [X8, #off_FFFFFFF0170146C0]
|
||||
FFFFFFF017023A54 LDRB W9, [X9, #0x4D] ; load system policy byte
|
||||
FFFFFFF017023A58 TBNZ W9, #0, loc_FFFFFFF017023A6C ; <-- PATCHED to NOP
|
||||
; Fall through to force-enable:
|
||||
FFFFFFF017023A5C MOV W20, #1 ; developer_mode = ENABLED
|
||||
FFFFFFF017023A60 ADRL X0, "developer mode enabled due to system policy configuration"
|
||||
FFFFFFF017023A68 B log_and_store
|
||||
```
|
||||
|
||||
### Decompiled (pre-patch)
|
||||
|
||||
```c
|
||||
__int64 sub_FFFFFFF017023A20(__int64 manifest)
|
||||
{
|
||||
char devmode;
|
||||
|
||||
// Check 1: PCC research variant flag
|
||||
if ( pcc_research_flag )
|
||||
{
|
||||
devmode = 1;
|
||||
goto apply;
|
||||
}
|
||||
|
||||
// Check 2: System policy (patched here)
|
||||
byte policy = *(system_config_ptr + 0x4D);
|
||||
if ( (policy & 1) != 0 ) // <-- TBNZ jumps past force-enable
|
||||
goto normal_path; // to xART / user-config checks
|
||||
|
||||
// Force-enable path (reached by NOPing the TBNZ):
|
||||
devmode = 1;
|
||||
log("developer mode enabled due to system policy configuration");
|
||||
goto apply;
|
||||
|
||||
normal_path:
|
||||
// ... xART availability check ...
|
||||
// ... user configuration check ...
|
||||
// May set devmode = 0 (disabled) based on config
|
||||
|
||||
apply:
|
||||
byte_FFFFFFF017070F24 = devmode; // global developer mode state
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Effect
|
||||
|
||||
NOPing the `TBNZ` makes execution always fall through to `MOV W20, #1`, forcing
|
||||
developer mode enabled regardless of the system policy byte. Without this:
|
||||
|
||||
- The `TBNZ` would jump to `loc_FFFFFFF017023A6C` (the normal path)
|
||||
- The normal path checks xART availability, device tree flags, and user configuration
|
||||
- On PCC VMs, this can result in developer mode being **disabled**
|
||||
|
||||
Developer mode is a **prerequisite** for selectors 41 and 42 — the selector 41
|
||||
handler returns error 27 immediately if `byte_FFFFFFF017070F24` is not set:
|
||||
|
||||
```c
|
||||
// In sub_FFFFFFF017023558 (selector 41):
|
||||
if ( (byte_FFFFFFF017070F24 & 1) == 0 )
|
||||
return 27; // developer mode not enabled
|
||||
```
|
||||
|
||||
### `txm_jb.py` dynamic finder: `patch_developer_mode_bypass()`
|
||||
|
||||
Searches for string refs to `"developer mode enabled due to system policy
|
||||
configuration"`, then scans backwards for a `tbz/tbnz/cbz/cbnz` instruction
|
||||
matching `w9, #0`. NOPs it.
|
||||
|
||||
---
|
||||
|
||||
## Patch Dependency Chain
|
||||
|
||||
The patches have a logical ordering — later patches depend on earlier ones:
|
||||
|
||||
```
|
||||
Patch 6: Developer Mode Bypass
|
||||
│ Forces byte_FFFFFFF017070F24 = 1
|
||||
│
|
||||
├──► Patch 3: get-task-allow Force True (selector 41)
|
||||
│ Requires developer mode (checks byte_FFFFFFF017070F24)
|
||||
│ Forces manifest[0x30] = 1
|
||||
│
|
||||
├──► Patch 4: selector 42|29 Shellcode
|
||||
│ Forces manifest[0x30] = 1 at dispatch level
|
||||
│ Safety net for Patch 3 (covers cases where sel 42 runs before sel 41)
|
||||
│
|
||||
├──► Patch 5: Debugger Entitlement Force True (selector 42)
|
||||
│ Bypasses com.apple.private.cs.debugger check
|
||||
│ Allows debug memory mapping for all processes
|
||||
│
|
||||
└──► Patches 1–2: CodeSignature Hash Bypass (selector 24)
|
||||
Independent — bypasses CS hash validation in the signature chain
|
||||
```
|
||||
|
||||
### Boot-time flow
|
||||
|
||||
1. TXM initializes → `sub_FFFFFFF017023A20` runs → **Patch 6** forces devmode ON
|
||||
2. Process loads → selector 24 validates CodeSignature → **Patches 1–2** skip hash check
|
||||
3. Process requests entitlements → selector 41 → **Patch 3** grants get-task-allow
|
||||
4. Debugger attaches → selector 42 → **Patch 4** pre-sets flag + **Patch 5** grants debugger ent
|
||||
5. Debug mapping succeeds → LLDB can attach to any process
|
||||
|
||||
---
|
||||
|
||||
## Summary Table
|
||||
|
||||
| # | File Offset | VA | Function | Patch | Purpose |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | `0x313ec` | `0xFFFFFFF0170353EC` | `sub_FFFFFFF0170353B8` (CS hash validator) | NOP | Remove hash flag load |
|
||||
| 2 | `0x313f4` | `0xFFFFFFF0170353F4` | `sub_FFFFFFF0170353B8` (CS hash validator) | NOP | Skip hash flag extraction call |
|
||||
| 3 | `0x1f5d4` | `0xFFFFFFF0170235D4` | `sub_FFFFFFF017023558` (selector 41) | `MOV X0, #1` | Force get-task-allow = true |
|
||||
| 4 | `0x2717c` | `0xFFFFFFF01702B17C` | `sub_FFFFFFF01702AE80` (dispatcher, case 42) | `B shellcode` | Redirect to shellcode cave |
|
||||
| 4a | `0x5d3b4` | `0xFFFFFFF0170613B4` | code cave (zeros) | `NOP` | Shellcode padding |
|
||||
| 4b | `0x5d3b8` | `0xFFFFFFF0170613B8` | code cave | `MOV X0, #1` | Set value for flag |
|
||||
| 4c | `0x5d3bc` | `0xFFFFFFF0170613BC` | code cave | `STRB W0, [X20,#0x30]` | Force get-task-allow flag |
|
||||
| 4d | `0x5d3c0` | `0xFFFFFFF0170613C0` | code cave | `MOV X0, X20` | Restore original instruction |
|
||||
| 4e | `0x5d3c4` | `0xFFFFFFF0170613C4` | code cave | `B back` | Return to dispatcher |
|
||||
| 5 | `0x1f3b8` | `0xFFFFFFF0170233B8` | `sub_FFFFFFF017023368` (selector 42) | `MOV W0, #1` | Force debugger entitlement = true |
|
||||
| 6 | `0x1FA58` | `0xFFFFFFF017023A58` | `sub_FFFFFFF017023A20` (devmode init) | NOP | Force developer mode ON |
|
||||
|
||||
**Total**: 6 logical patches, 10 instruction modifications (counting shellcode), enabling:
|
||||
- CodeSignature bypass (patches 1–2)
|
||||
- Universal get-task-allow (patches 3–4)
|
||||
- Universal debugger entitlement (patch 5)
|
||||
- Forced developer mode (patch 6)
|
||||
@@ -12,7 +12,7 @@
|
||||
# - `ipsw` tool installed (brew install blacktop/tap/ipsw)
|
||||
# - `aea` tool available (macOS 12+)
|
||||
# - Python: make setup_venv && source .venv/bin/activate
|
||||
# - cfw_input/ or resources/cfw_dev_input.tar.zst present
|
||||
# - cfw_input/ or resources/cfw_input.tar.zst + resources/cfw_dev/rpcserver_ios present
|
||||
#
|
||||
# Usage: make cfw_install_dev
|
||||
set -euo pipefail
|
||||
@@ -26,7 +26,7 @@ VM_DIR="$(cd "$VM_DIR" && pwd)"
|
||||
|
||||
# ── Configuration ───────────────────────────────────────────────
|
||||
CFW_INPUT="cfw_input"
|
||||
CFW_ARCHIVE="cfw_dev_input.tar.zst"
|
||||
CFW_ARCHIVE="cfw_input.tar.zst"
|
||||
TEMP_DIR="$VM_DIR/.cfw_temp"
|
||||
|
||||
SSH_PORT=2222
|
||||
@@ -134,6 +134,26 @@ setup_cfw_input() {
|
||||
die "Neither $CFW_INPUT/ nor $CFW_ARCHIVE found"
|
||||
}
|
||||
|
||||
# ── Apply dev overlay (replace rpcserver_ios in iosbinpack64) ──
|
||||
apply_dev_overlay() {
|
||||
local dev_bin
|
||||
for search_dir in "$SCRIPT_DIR/resources/cfw_dev" "$SCRIPT_DIR/cfw_dev"; do
|
||||
dev_bin="$search_dir/rpcserver_ios"
|
||||
if [[ -f "$dev_bin" ]]; then
|
||||
echo " Applying dev overlay (rpcserver_ios)..."
|
||||
local iosbinpack="$VM_DIR/$CFW_INPUT/jb/iosbinpack64.tar"
|
||||
local tmpdir="$VM_DIR/.iosbinpack_tmp"
|
||||
mkdir -p "$tmpdir"
|
||||
tar -xf "$iosbinpack" -C "$tmpdir"
|
||||
cp "$dev_bin" "$tmpdir/iosbinpack64/usr/local/bin/rpcserver_ios"
|
||||
(cd "$tmpdir" && tar -cf "$iosbinpack" iosbinpack64)
|
||||
rm -rf "$tmpdir"
|
||||
return
|
||||
fi
|
||||
done
|
||||
die "Dev overlay not found (cfw_dev/rpcserver_ios)"
|
||||
}
|
||||
|
||||
# ── Check prerequisites ────────────────────────────────────────
|
||||
check_prereqs() {
|
||||
command -v ipsw >/dev/null 2>&1 || die "'ipsw' not found. Install: brew install blacktop/tap/ipsw"
|
||||
@@ -161,6 +181,7 @@ RESTORE_DIR=$(find_restore_dir)
|
||||
echo "[+] Restore directory: $RESTORE_DIR"
|
||||
|
||||
setup_cfw_input
|
||||
apply_dev_overlay
|
||||
INPUT_DIR="$VM_DIR/$CFW_INPUT"
|
||||
echo "[+] Input resources: $INPUT_DIR"
|
||||
check_prerequisites
|
||||
|
||||
1
scripts/resources
Submodule
1
scripts/resources
Submodule
Submodule scripts/resources added at 0e371ec870
Reference in New Issue
Block a user