Merge branch 'main' into feature/private-display-recorder

This commit is contained in:
Lakr
2026-03-07 19:30:15 +08:00
39 changed files with 604 additions and 2526 deletions

View File

@@ -74,12 +74,10 @@
### JB-Only Kernel Methods (Reference List)
Current default schedule note (2026-03-06): `patch_cred_label_update_execve` remains temporarily excluded from `_PATCH_METHODS` pending staged re-validation. `patch_syscallmask_apply_to_proc` has been rebuilt around the real syscallmask apply wrapper and is re-enabled after focused PCC 26.1 dry-run validation plus user-side boot confirmation; refreshed XNU/IDA review also confirms historical C22 was the all-ones-mask variant, not a `NULL`-mask install. `patch_hook_cred_label_update_execve` has also been rebuilt as a faithful upstream C23 wrapper trampoline: it retargets sandbox `mac_policy_ops[18]` to a cave that copies `VSUID`/`VSGID` owner state into the pending credential, sets `P_SUGID`, and branches back to the original wrapper. `patch_iouc_failed_macf` has been rebuilt as a narrow branch-level gate patch: the old repo-only entry early-return on `0xFFFFFE000825B0C0` was discarded, and A5-v2 now patches the post-`mac_iokit_check_open` `CBZ W0, allow` gate at `0xFFFFFE000825BA98` to unconditional allow while preserving the surrounding IOUserClient setup flow. `patch_vm_fault_enter_prepare` was retargeted to the upstream PCC 26.1 research `cs_bypass` gate and re-enabled for dry-run validation. `patch_bsd_init_auth` has been retargeted to the real `_bsd_init` rootauth failure branch and re-enabled for staged validation. Fresh IDA re-analysis shows JB-14 previously used a false-positive matcher; it now targets the real `_bsd_init` rootauth failure branch using in-function Capstone-decoded control-flow semantics and is semantically redundant with base patch #3 when JB is layered on top of `fw_patch`. For JB-16, the historical hit at `0xFFFFFE000836E1F0` is now treated as semantically wrong: it patches the `"SecureRoot"` name-check gate inside `AppleARMPE::callPlatformFunction`, not the `"SecureRootName"` deny return consumed by `IOSecureBSDRoot()`. The implementation was retargeted on 2026-03-06 to `0xFFFFFE000836E464` (`CSEL W22, WZR, W9, NE -> MOV W22, #0`) and re-enabled in `KernelJBPatcher._GROUP_B_METHODS` pending restore/boot validation.
| # | Group | Method | Function | Purpose | JB Enabled |
| ----- | ----- | ------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :--------: |
| JB-01 | A | `patch_amfi_cdhash_in_trustcache` | `AMFIIsCDHashInTrustCache` | Always return true + store hash | Y |
| JB-02 | A | `patch_amfi_execve_kill_path` | AMFI execve kill return site | Convert shared kill return from deny to allow | Y |
| JB-02 | A | `patch_amfi_execve_kill_path` | AMFI execve kill return site | Convert shared kill return from deny to allow (superseded by C21; standalone only) | N |
| JB-03 | C | `patch_cred_label_update_execve` | `_cred_label_update_execve` | Reworked C21-v3: C21-v1 already boots; v3 keeps split late exits and additionally ORs success-only helper bits `0xC` after clearing `0x3F00`; still disabled pending boot validation | N |
| JB-04 | C | `patch_hook_cred_label_update_execve` | sandbox `mpo_cred_label_update_execve` wrapper (`ops[18]` -> `sub_FFFFFE00093BDB64`) | Faithful upstream C23 trampoline: copy `VSUID`/`VSGID` owner state into pending cred, set `P_SUGID`, then branch back to wrapper | Y |
| JB-05 | C | `patch_kcall10` | `sysent[439]` (`SYS_kas_info` replacement) | Rebuilt ABI-correct kcall cave: `target + 7 args -> uint64 x0`; re-enabled after focused dry-run validation | Y |
@@ -104,12 +102,6 @@ Current default schedule note (2026-03-06): `patch_cred_label_update_execve` rem
| JB-24 | B | `patch_vm_fault_enter_prepare` | `_vm_fault_enter_prepare` | Force `cs_bypass` fast path in runtime fault validation | Y |
| JB-25 | B | `patch_vm_map_protect` | `_vm_map_protect` | Skip upstream write-downgrade gate in `vm_map_protect` | Y |
JB rework note (2026-03-06, remaining active methods): `JB-01`, `JB-08`, `JB-09`, `JB-06`, `JB-11`, `JB-12`, `JB-13`, `JB-17`, `JB-19`, and `JB-23` have now also been rechecked against `/Users/qaq/Desktop/patch_fw.py`, IDA PCC 26.1 research, `research/reference/xnu`, and focused dry-runs on both PCC 26.1 research/release. Of these, `JB-09` was materially pulled back to the upstream `mac_policy_ops` table-entry rewrite model (common allow stub retarget, matching `patch_fw.py` offsets) instead of per-hook body stubs; `JB-06` dropped its broad AMFI-text fallback; `JB-12` tightened to the exact early `ldr/cbz/bl/cbz` guard pair; and `JB-19` now requires a unique `krn.`-anchored verifyPermission gate across all string refs. The remaining six (`JB-01`, `JB-08`, `JB-11`, `JB-13`, `JB-17`, `JB-23`) matched upstream offsets and semantics without further retarget.
JB retarget note (2026-03-06): `JB-15`, `JB-18`, `JB-20`, `JB-21`, `JB-22`, and `JB-25` were rechecked against `/Users/qaq/Desktop/patch_fw.py`, IDA PCC 26.1 research, and `research/reference/xnu`. Current preferred runtime behavior is to match the known-good upstream semantic gate unless binary+source evidence clearly disproves it. In this pass, `JB-22` was pulled back from a helper-return rewrite to the upstream early `pid == 0` gate, and `JB-20` was pulled back from the later preboot-fallback compare to the upstream first root-mount compare.
JB-24 note (2026-03-06): the old derived matcher hit the `VM_PAGE_CONSUME_CLUSTERED()` lock/unlock sequence inside `vm_fault_enter_prepare`, i.e. `pmap_lock_phys_page()` / `pmap_unlock_phys_page()`. The implementation is now retargeted to the upstream PCC 26.1 research `cs_bypass` gate at `0x00BA9E1C` / `0xFFFFFE0007BADE1C`.
## CFW Installation Patches
### Binary Patches Applied Over SSH Ramdisk
@@ -138,18 +130,19 @@ JB-24 note (2026-03-06): the old derived matcher hit the `VM_PAGE_CONSUME_CLUSTE
### CFW Installer Flow Matrix (Script-Level)
| Flow Item | Regular (`cfw_install.sh`) | Dev (`cfw_install_dev.sh`) | JB (`cfw_install_jb.sh`) |
| ---------------------------------------------------- | ------------------------------- | ----------------------------------------------- | --------------------------------------------- |
| Base CFW phases (1/7 -> 7/7) | Runs directly | Runs directly | Runs via `CFW_SKIP_HALT=1 zsh cfw_install.sh` |
| Dev overlay (`rpcserver_ios` replacement) | - | Y (`apply_dev_overlay`) | - |
| SSH readiness wait before install | Y (`wait_for_device_ssh_ready`) | - | Y (inherited from base run) |
| launchd jetsam patch (`patch-launchd-jetsam`) | - | Y (base-flow injection) | Y (JB-1) |
| launchd dylib injection (`inject-dylib /b`) | - | - | Y (JB-1) |
| Procursus bootstrap deployment | - | - | Y (JB-2) |
| BaseBin hook deployment (`*.dylib` -> `/mnt1/cores`) | - | - | Y (JB-3) |
| Additional input resources | `cfw_input` | `cfw_input` + `resources/cfw_dev/rpcserver_ios` | `cfw_input` + `cfw_jb_input` |
| Extra tool requirement beyond base | - | - | `zstd` |
| Halt behavior | Halts unless `CFW_SKIP_HALT=1` | Halts unless `CFW_SKIP_HALT=1` | Always halts after JB phases |
| Flow Item | Regular (`cfw_install.sh`) | Dev (`cfw_install_dev.sh`) | JB (`cfw_install_jb.sh`) |
| --------------------------------------------- | ------------------------------- | -------------------------- | --------------------------------------------- |
| Base CFW phases (1/7 -> 7/7) | Runs directly | Runs directly | Runs via `CFW_SKIP_HALT=1 zsh cfw_install.sh` |
| Dev overlay (`rpcserver_ios` replacement) | - | Y (`apply_dev_overlay`) | - |
| SSH readiness wait before install | Y (`wait_for_device_ssh_ready`) | - | Y (inherited from base run) |
| launchd jetsam patch (`patch-launchd-jetsam`) | - | Y (base-flow injection) | Y (JB-1) |
| launchd dylib injection (`inject-dylib /b`) | - | - | Y (JB-1) |
| Procursus bootstrap deployment | - | - | Y (JB-2) |
| BaseBin hook deployment (`*.dylib` -> `/mnt1/cores`) | - | - | Y (JB-3) |
| Additional input resources | `cfw_input` | `cfw_input` + `resources/cfw_dev/rpcserver_ios` | `cfw_input` + `cfw_jb_input` |
| Extra tool requirement beyond base | - | - | `zstd` |
| Halt behavior | Halts unless `CFW_SKIP_HALT=1` | Halts unless `CFW_SKIP_HALT=1` | Always halts after JB phases |
## Summary
@@ -160,20 +153,21 @@ JB-24 note (2026-03-06): the old derived matcher hit the `VM_PAGE_CONSUME_CLUSTE
| iBEC | 3 | 3 | 3 |
| LLB | 6 | 6 | 6 |
| TXM | 1 | 12 | 12 |
| Kernel | 28 | 28 | 53 |
| Boot chain total | 41 | 52 | 78 |
| Kernel (base) | 28 | 28 | 28 |
| Kernel (JB methods) | - | - | 59 |
| Boot chain total | 41 | 52 | 112 |
| CFW binary patches | 4 | 5 | 6 |
| CFW installed components | 6 | 7 | 8 |
| CFW total | 10 | 12 | 14 |
| Grand total | 51 | 64 | 92 |
| Grand total | 51 | 64 | 126 |
## Ramdisk Variant Matrix
| Variant | Pre-step | `Ramdisk/txm.img4` | `Ramdisk/krnl.ramdisk.img4` | `Ramdisk/krnl.img4` | Effective kernel used by `ramdisk_send.sh` |
| ------------- | ------------------- | -------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------- |
| `RAMDISK` | `make fw_patch` | release TXM + base TXM patch (1) | base kernel (28), legacy `*.ramdisk` preferred else derive from pristine CloudOS | restore kernel from `fw_patch` (28) | `krnl.ramdisk.img4` preferred, fallback `krnl.img4` |
| `DEV+RAMDISK` | `make fw_patch_dev` | release TXM + base TXM patch (1) | base kernel (28), same derivation rule | restore kernel from `fw_patch_dev` (28) | `krnl.ramdisk.img4` preferred, fallback `krnl.img4` |
| `JB+RAMDISK` | `make fw_patch_jb` | release TXM + base TXM patch (1) | base kernel (28), same derivation rule | restore kernel from `fw_patch_jb` (53) | `krnl.ramdisk.img4` preferred, fallback `krnl.img4` |
| Variant | Pre-step | `Ramdisk/txm.img4` | `Ramdisk/krnl.ramdisk.img4` | `Ramdisk/krnl.img4` | Effective kernel used by `ramdisk_send.sh` |
| ------------- | ------------------- | -------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------------- | --------------------------------------------------- |
| `RAMDISK` | `make fw_patch` | release TXM + base TXM patch (1) | base kernel (28), legacy `*.ramdisk` preferred else derive from pristine CloudOS | restore kernel from `fw_patch` (28) | `krnl.ramdisk.img4` preferred, fallback `krnl.img4` |
| `DEV+RAMDISK` | `make fw_patch_dev` | release TXM + base TXM patch (1) | base kernel (28), same derivation rule | restore kernel from `fw_patch_dev` (28) | `krnl.ramdisk.img4` preferred, fallback `krnl.img4` |
| `JB+RAMDISK` | `make fw_patch_jb` | release TXM + base TXM patch (1) | base kernel (28), same derivation rule | restore kernel from `fw_patch_jb` (28+59) | `krnl.ramdisk.img4` preferred, fallback `krnl.img4` |
## Cross-Version Dynamic Snapshot

View File

@@ -1,115 +0,0 @@
# Data-Protection Panic Investigation (2026-03-05)
## Symptom
On JB boot, system reaches `launchd` boot tasks but then panics with:
- `Boot task failed: data-protection - exited due to exit(60)`
- repeated host-side retries:
- `[control] vsock: ... Connection reset by peer, retrying...`
- and critical kernel log:
- `IOUC AppleSEPUserClient failed MACF in process pid 6, seputil`
## What This Confirms
This is not an early-kernel boot hang. The failure occurs in userspace boot task `data-protection`, where `seputil` cannot pass MACF/IOKit authorization to open SEP user client paths.
## Static Root-Cause Trace (kernelcache.research.vphone600)
Using local disassembly on the current vphone600 research kernel:
1. `failed MACF` log string xref resolves to IOKit MAC dispatch function at:
- function start VA: `0xFFFFFE000825B0C0`
2. The deny path emits `failed MACF` after policy callback dispatch.
3. Callback slot used in this path is:
- `policy->ops + 0x648` => index `201`
4. Current sandbox-extended patch set (before this fix) did not include `ops[201..210]`.
5. Sandbox ops table for this kernel has non-null handlers at:
- `201..210` (`0xFFFFFE00093A654C` ... `0xFFFFFE00093A598C`)
Interpretation: IOKit MAC hooks remained active and could deny `AppleSEPUserClient` access, matching runtime `IOUC ... failed MACF`.
## Mitigation Implemented
### 1) Kernel patcher extension
Updated:
- `scripts/patchers/kernel_jb_patch_sandbox_extended.py`
Added hook indices:
- `201..210` as `iokit_check_201` ... `iokit_check_210`
Patch action per entry remains:
- `mov x0,#0`
- `ret`
### 2) Documentation updates
Updated:
- `research/kernel_patch_jb/patch_sandbox_hooks_extended.md`
- `research/00_patch_comparison_all_variants.md`
## Local Validation (static)
Ran `patch_sandbox_hooks_extended()` against current `kernelcache.research.vphone600`:
- before extension: `52` writes (`26` hooks)
- after extension: `72` writes (`36` hooks)
New emitted entries include:
- `_hook_iokit_check_201` ... `_hook_iokit_check_210`
## Runtime Validation Pending
Not yet executed in this turn. Required E2E confirmation:
1. `make fw_patch_jb`
2. restore flow (so patched kernel is installed)
3. `make cfw_install_jb`
4. `make boot`
5. verify disappearance of:
- `IOUC AppleSEPUserClient failed MACF ... seputil`
- `Boot task failed: data-protection - exited due to exit(60)`
## Notes
- Canonical `mpo_iokit_*` names for these indices are not fully symbol-resolved in local KC symbols; index-based labeling is used intentionally to avoid incorrect naming.
## 2026-03-06 Follow-up (still failing after ops[201..210] extension)
Observed runtime still reports:
- `IOUC AppleAPFSUserClient failed MACF in process pid 4, mount`
- `IOUC AppleSEPUserClient failed MACF in process pid 6, seputil`
So per-policy sandbox hook stubs alone are insufficient on this path.
### Additional Mitigation Added
Introduced a dedicated JB patch:
- `patch_iouc_failed_macf` in `scripts/patchers/kernel_jb_patch_iouc_macf.py`
Method:
- Anchor on `"failed MACF"` xref.
- Resolve centralized IOUC MACF gate function.
- Apply low-risk early return at `fn+4/fn+8`:
- `mov x0, xzr`
- `retab`
Current static hit on this kernel:
- function start: `0xfffffe000825b0c0`
- patched:
- `0xfffffe000825b0c4`
- `0xfffffe000825b0c8`
Related doc:
- `research/kernel_patch_jb/patch_iouc_failed_macf.md`

View File

@@ -1,628 +0,0 @@
# Boot Hang Focus: B19 + (B11/B12) Strategy Comparison
Date: 2026-03-05
Target binary family: `kernelcache.research.vphone600` (iOS 26.1 / 23B85)
## Final Outcome (2026-03-05)
This investigation is complete for the current delivery gate (bootability).
- B19/MNT strategy switching did **not** recover boot:
- matrix artifact: `vm/ab_matrix_b19_mnt_20260305_034127.csv`
- result: 9/9 combinations failed (`code=2`, watchdog timeout)
- JB-only bisect isolated a stable bootable subset:
- PASS: `A1-A4`
- PASS: `A1-A4 + B5-B8`
- FAIL: tested combinations including `B9+`
- Current boot-safe default in `kernel_jb.py`:
- enabled: `A1-A4 + B5-B8`
- disabled: `B9-B20`, `C21-C24`
- E2E success evidence:
- `vm/testing_exec_watch_20260305_050051.log`
- `vm/testing_exec_watch_20260305_050146.log`
- both reached restore-ready markers (USB mux activated + waiting-for-host gate)
## Scope
This note compares two patching styles for boot-hang triage:
1. B19 (`_IOSecureBSDRoot`) patch style mismatch:
- upstream known-to-work fixed site
- current dynamic patcher site
2. B11/B12 (`___mac_mount` / `_dounmount`) patch style mismatch:
- upstream fixed-site patches
- current dynamic strict-shape patches
The goal is to make A/B testing reproducible with concrete trigger points, pseudocode, and expected runtime effects.
---
## 1) B19 `_IOSecureBSDRoot` mismatch
### 1.1 Trigger points (clean kernel)
- `0x0128B598` (`VA 0xFFFFFE000828F598`) in `sub_FFFFFE000828F42C`
- before: `b.ne #0x128b5bc`
- upstream patch: `b #0x128b5bc` (`0x14000009`)
- `0x01362090` (`VA 0xFFFFFE0008366090`) in `sub_FFFFFE0008366008`
- before: `cbz w0, #0x1362234`
- current dynamic patch: `b #0x1362234` (`0x14000069`)
Current patched checkpoint confirms:
- `0x01362090` is patched (`b`)
- `0x0128B598` remains unpatched (`b.ne`)
### 1.2 Function logic and pseudocode
#### A) Upstream site function (`sub_FFFFFE000828F42C`)
High-level logic:
1. query `"SecureRootName"` from `IOPlatformExpert`
2. run provider call
3. release objects
4. if return code equals `0xE00002C1`, branch to fallback path (`sub_FFFFFE0007C6AA58`)
Pseudocode:
```c
ret = IOPlatformExpert->call("SecureRootName", ...);
release(...);
if (ret == 0xE00002C1) {
return fallback_path();
}
return ret;
```
Patch effect at `0x128B598` (`b.ne -> b`):
- always take fallback path, regardless whether `ret == 0xE00002C1`.
#### B) Dynamic site function (`sub_FFFFFE0008366008`)
Key branch:
- `0x136608C`: callback for `"SecureRoot"`
- `0x1366090`: `cbz w0, #0x1362234` (branch into `"SecureRootName"` block)
- `0x1366234` onward: `"SecureRootName"` handling block
Pseudocode:
```c
if (matches("SecureRoot")) {
ok = callback("SecureRoot");
if (ok == 0) goto SecureRootNameBlock; // cbz w0
// SecureRoot success/failure handling path
}
SecureRootNameBlock:
if (matches("SecureRootName")) {
// name-based validation + state sync
}
```
Patch effect at `0x1362090` (`cbz -> b`):
- always jump into `SecureRootNameBlock`, regardless `ok`.
### 1.3 A/B variants to test
1. `B19-A` (upstream helper only):
- patch only `0x128B598`
- keep `0x1362090` original
2. `B19-B` (dynamic main only):
- patch only `0x1362090`
- keep `0x128B598` original
3. `B19-C` (both):
- patch both sites
### 1.4 Expected observables
- Boot logs around:
- `apfs_find_named_root_snapshot_xid`
- `Need authenticator (81)`
- transition into init / panic frame
- Panic signatures:
- null-deref style FAR near low address (for current failure class)
- stack path involving mount/security callback chain
---
## 2) B11/B12 (`___mac_mount` / `_dounmount`) mismatch
### 2.1 Trigger points (clean kernel)
#### Upstream fixed-offset style
- `0x00CA5D54`: `tbnz w28, #5, #0xca5f18` -> `nop`
- `0x00CA5D88`: `ldrb w8, [x8, #1]` -> `mov x8, xzr`
- `0x00CA8134`: `bl #0xc92ad8` -> `nop`
#### Current dynamic style (checkpoint)
- `0x00CA4EAC`: `cbnz w0, #0xca4ec8` -> `nop` (B11)
- `0x00CA81FC`: `bl #0xc9bdbc` -> `nop` (B12)
And in checkpoint:
- upstream sites remain original (`0xCA5D54`, `0xCA5D88`, `0xCA8134` unchanged)
- dynamic sites are patched (`0xCA4EAC`, `0xCA81FC` are `nop`)
### 2.2 Function logic and pseudocode
#### A) `___mac_mount`-related branch (dynamic site near `0xCA4EA8`)
Disassembly window:
- `0xCA4EA8`: `bl ...`
- `0xCA4EAC`: `cbnz w0, deny`
- deny target writes non-zero return (`mov w0, #1`)
Pseudocode:
```c
ret = mac_policy_check(...);
if (ret != 0) { // cbnz w0
return EPERM_like_error;
}
continue_mount();
```
Dynamic patch (`0xCA4EAC -> nop`) effect:
- ignore `ret != 0` branch and continue mount path.
#### B) Upstream `___mac_mount` two-site style (`0xCA5D54`, `0xCA5D88`)
Disassembly window:
- `0xCA5D54`: `tbnz w28, #5, ...`
- `0xCA5D88`: `ldrb w8, [x8, #1]`
Pseudocode (behavioral interpretation):
```c
if (flag_bit5_set(w28)) goto restricted_path;
w8 = *(u8 *)(x8 + 1);
...
```
Upstream patches:
- remove bit-5 gate branch (`tbnz -> nop`)
- force register state (`ldrb -> mov x8, xzr`)
This is broader state manipulation than dynamic deny-branch patching.
#### C) `_dounmount` path
Upstream site:
- `0xCA8134`: `bl #0xc92ad8` -> `nop`
Dynamic site:
- `0xCA81FC`: `bl #0xc9bdbc` -> `nop`
Pseudocode (generic):
```c
... prepare args ...
ret = mac_or_policy_call_X(...); // site differs between two strategies
...
ret2 = mac_or_policy_call_Y(...);
```
Difference:
- upstream and dynamic disable different call sites in unmount path;
- not equivalent by construction.
### 2.3 A/B variants to test
1. `MNT-A` (upstream-only style):
- apply `0xCA5D54`, `0xCA5D88`, `0xCA8134`
- keep `0xCA4EAC`, `0xCA81FC` original
2. `MNT-B` (dynamic-only style):
- apply `0xCA4EAC`, `0xCA81FC`
- keep `0xCA5D54`, `0xCA5D88`, `0xCA8134` original
3. `MNT-C` (both styles):
- apply all five sites
---
## 3) Combined test matrix (recommended)
For minimal triage noise, run a 3x3 matrix:
- B19 mode: `B19-A`, `B19-B`, `B19-C`
- mount mode: `MNT-A`, `MNT-B`, `MNT-C`
Total 9 combinations, each from the same clean baseline kernel.
Record per run:
1. last APFS logs before failure/success
2. whether `Need authenticator (81)` appears
3. panic presence and panic PC/FAR
4. whether init proceeds past current hang point
---
## 4) Historical A/B Knobs (used during triage, now removed)
During the triage phase, temporary runtime knobs were introduced to toggle
upstream-vs-dynamic strategies for B11/B12/B13/B14/B19 and execute the matrix.
Those knobs are no longer part of the default runtime path after stabilization;
the shipped default now hard-selects the boot-safe subset (`A1-A4 + B5-B8`).
The triage results from those knobs are preserved in this document and in:
- `vm/ab_matrix_b19_mnt_20260305_034127.csv`
- `TODO.md` (Boot Hang Research + Progress Update sections)
- `research/00_patch_comparison_all_variants.md` (Kernelcache section)
---
## 5) Practical note
Do not mix incremental patching across already-patched binaries when comparing these modes.
Always regenerate from clean baseline before each combination, otherwise branch-site interactions can mask true causality.
---
## 6) Additional non-equivalent points (beyond B19/B11/B12)
This section answers "还有没有别的不一样的" with boot-impact-focused mismatches.
### 6.1 B13 `_bsd_init auth` is not the same logical site
#### Trigger points
- upstream fixed site: `0x00F6D95C` in `sub_FFFFFE0007F6D2B8`
- current dynamic site: `0x00FA2A78` in `sub_FFFFFE0007FA2838`
#### Function logic (high level)
- `sub_FFFFFE0007F6D2B8` is a workqueue/thread-call state machine.
- `sub_FFFFFE0007FA2838` is another lock/CAS-heavy control path.
Neither decompilation corresponds to `_bsd_init` body semantics directly.
#### Pseudocode (site-level)
`0xF6D95C` neighborhood:
```c
...
call unlock_or_wakeup(...); // BL at 0xF6D95C
...
```
`0xFA2A78` neighborhood:
```c
...
stats_counter++;
x2 = x9; // MOV at 0xFA2A78
cas_release(lock, x2, 0);
...
```
#### Risk
- This is a strong false-equivalence signal.
- If this patch is intended as `_bsd_init` auth bypass, current dynamic hit should be treated as suspect.
### 6.2 B14 `_spawn_validate_persona` strategy changed from 2xNOP to forced branch
#### Trigger points
- upstream fixed sites: `0x00FA7024`, `0x00FA702C` (same function `sub_FFFFFE0007FA6F7C`)
- current dynamic site: `0x00FA694C` (function `sub_FFFFFE0007FA6858`)
#### Function logic and loop relevance
In `sub_FFFFFE0007FA6858`, there is an explicit spin loop:
- `0xFA6ACC`: `LDADD ...`
- `0xFA6AD4`: `B.EQ 0xFA6ACC` (self-loop)
Pseudocode:
```c
do {
old = atomic_fetch_add(counter, 1);
} while (old == target); // tight spin at 0xFA6ACC/0xFA6AD4
```
And same function calls:
- `sub_FFFFFE0007B034E4` (at `0xFA6A94`)
- `sub_FFFFFE0007B040CC` (at `0xFA6AA8`)
Your panic signature previously mapped into this call chain, so this mismatch is high-priority for 100% CPU / hang triage.
### 6.3 B9 `_vm_fault_enter_prepare` does not hit the same function
#### Trigger points
- upstream fixed site: `0x00BA9E1C` in `sub_FFFFFE0007BA9C48`
- current dynamic site: `0x00BA9BB0` in `sub_FFFFFE0007BA9944`
#### Pseudocode (site-level)
`0xBA9E1C`:
```c
// parameter setup right before BL
ldp x4, x5, [sp, ...];
bl helper(...);
```
`0xBA9BB0`:
```c
if (w25 == 3) w21 = 2; else w21 = w25; // csel
```
These are structurally unrelated.
### 6.4 B10 `_vm_map_protect` site differs in same large function
#### Trigger points
- upstream fixed site: `0x00BC024C`
- current dynamic site: `0x00BC012C`
- both inside `sub_FFFFFE0007BBFA48`
#### Pseudocode (site-level)
`0xBC012C`:
```c
perm = cond ? perm_a : perm_b; // csel
```
`0xBC024C`:
```c
// different control block; not the same selection point
...
```
Even in the same function, these are not equivalent branch gates.
### 6.5 B15 `_task_for_pid` and B17 shared-region are also shifted
#### Trigger points
- B15 upstream: `0x00FC383C` (`sub_FFFFFE0007FC34B4`)
- B15 dynamic: `0x00FFF83C` (`sub_FFFFFE0007FFF824`)
- B17 upstream: `0x010729CC`
- B17 dynamic: `0x01072A88`
- both in `sub_FFFFFE000807272C`, but not same instruction role
#### Risk
- These are unlikely to explain early APFS/init mount failure alone, but they are still non-equivalent and should not be assumed interchangeable.
---
## 7) Practical triage order for 100% virtualization CPU
Given current evidence, prioritize:
1. B14 strategy A/B first (upstream `0xFA7024/0xFA702C` vs dynamic `0xFA694C`).
2. B13 strategy A/B next (`0xF6D95C` vs `0xFA2A78`).
3. Then B19 and MNT matrix.
Reason: B14 path contains a known tight spin construct and directly calls the function chain previously observed in panic mapping.
---
## 8) Normal boot baseline signature (for pass/fail triage)
Use the following runtime markers as "normal startup reached restore-ready stage" baseline:
1. USB bring-up checkpoint completes:
- `CHECKPOINT END: MAIN:[0x040E] enable_usb`
2. Network checkpoint enters and exits without device requirement:
- `CHECKPOINT BEGIN: MAIN:[0x0411] config_network_interface`
- `no device required to enable network interface, skipping`
- `CHECKPOINT END: MAIN:[0x0411] config_network_interface`
3. Restore daemon enters host-wait state:
- `waiting for host to trigger start of restore [timeout of 120 seconds]`
4. USB/NCM path activates and host loopback socket churn appears:
- `IOUSBDeviceController::setupDeviceSetConfiguration: configuration 0 -> 1`
- `AppleUSBDeviceMux::message - kMessageInterfaceWasActivated`
- repeated `sock ... accepted ... 62078 ...` then `sock ... closed`
5. BSD network interface bring-up for `anpi0` succeeds:
- `configureDatagramSizeOnBSDInterface() [anpi0] ... returning 0x00000000`
- `enableBSDInterface() [anpi0], returning 0x00000000`
- `configureIPv6LLOnBSDInterface() [anpi0], IPv6 enable returning 0x00000000`
- `disableTrafficShapingOnBSDInterface() [anpi0], disable traffic shaping returning 0x00000000`
Practical rule:
- If A/B variant run reaches marker #3 and then shows #4/#5 progression, treat it as "boot path not stuck in early kernel loop".
- If run stalls before marker #1/#2 completion or never reaches #3, prioritize kernel-side loop/panic investigation.
---
## 9) Why the failing sets are currently excluded
Short answer: they are not equivalent rewrites on this firmware, and multiple
sites land in different control contexts than expected upstream references.
IDA-backed findings used for exclusion:
1. B9 differs by function, not just offset:
- dynamic `0xBA9BB0` in `sub_FFFFFE0007BA9944`
- upstream `0xBA9E1C` in `sub_FFFFFE0007BA9C48`
2. B10 is same large function but different decision blocks:
- dynamic `0xBC012C` vs upstream `0xBC024C` in `sub_FFFFFE0007BBFA48`
3. B13 differs by function and behavior:
- dynamic `0xFA2A78` in `sub_FFFFFE0007FA2838`
- upstream `0xF6D95C` in `sub_FFFFFE0007F6D2B8`
4. B14 dynamic path sits in the spin-loop-containing function:
- `sub_FFFFFE0007FA6858` has `0xFA6ACC`/`0xFA6AD4` tight loop
- same path calls `sub_FFFFFE0007B034E4` and `sub_FFFFFE0007B040CC`
Given this mismatch profile, enabling B9+ as a default set is high risk for
boot regressions until each method is re-derived and validated individually on
the exact kernel build.
---
## 10) Final operational state
- Default JB boot profile: `A1-A4 + B5-B8` only
- Verified by `BASE_PATCH=jb make testing_exec` reproducibility runs:
- `vm/testing_exec_watch_20260305_050051.log`
- `vm/testing_exec_watch_20260305_050146.log`
- Delivery stance:
- prioritize bootability and deterministic restore-ready progression
- reintroduce B9+ / Group C only behind per-method revalidation
---
## 11) New Field Finding: "restore done but system still not fully up" (`make boot`)
Source: interactive serial output from `make boot` on 2026-03-05 (user report).
### 11.1 What the log proves
This run is **not** failing at the old restore-ready gate and **not** the old
early kernel boot-hang class.
Observed progression:
1. APFS root/data/xART/preboot mounts complete in kernel/userspace handoff.
2. `launchd` starts and executes boot tasks.
3. `mount-phase-1`, `mount-phase-2`, `finish-restore`, `init-with-data-volume`,
`keybag`, `usermanagerd` tasks are reached.
4. Log shows:
- `Early boot complete. Continuing system boot.`
- `hello from launchdhook.dylib` / `bye from launchdhook.dylib`
So the pipeline already crossed into JB userspace initialization.
### 11.2 Suspicious signals in this run
1. Early `launchd` assertion:
- `com.apple.xpc.launchd ... assertion failed ... 0xffffffffffffffff`
2. Ignition warning:
- `libignition: cryptex1 sniff: ignition failed: 8`
- then fallback path continues (`ignition disabled`) and boot tasks proceed.
3. `vphoned` host side repeatedly reports:
- `Connection reset by peer`
- indicates daemon channel is not yet stable/ready during this phase.
### 11.3 Most likely fault domain (ranked)
1. **JB-1 launchd modification path (highest probability)**:
- `patch-launchd-jetsam` dynamic branch rewrite may select an incorrect
conditional in some launchd builds.
- `inject-dylib /cores/launchdhook.dylib` adds early runtime side effects.
- The assertion appears in `launchd` startup window, matching this stage.
2. **JB hook/runtime environment coupling**:
- `JB_ROOT_PATH` and BaseBin hook expectations under preboot hash path.
- If path/state is incomplete, startup can degrade without immediate kernel panic.
3. **Less likely: kernel B9+ regression**
- Current default already excludes B9+ and this log clearly reaches deep
userspace boot tasks, so this symptom class is different from earlier
watchdog/restore-gate failures.
### 11.4 Practical triage to confirm
Use same restored disk and isolate JB userspace phases:
1. Baseline control:
- Boot with dev/regular userspace flow (no JB-1 launchd dylib injection).
2. Re-enable only JB-1:
- apply jetsam patch alone first, then add dylib injection.
3. Add JB-2/JB-3 incrementally:
- procursus bootstrap, then BaseBin hooks.
4. Capture first regression point and lock to the exact phase.
### 11.5 Conclusion for this report
- Current symptom ("restore completes but cannot fully start") is now best
modeled as a **post-restore userspace startup regression**, centered around
JB launchd modification/hook stages, not the previous kernel early-boot hang.
---
## 12) Failed vs Successful Boot Log Comparison (same device class, 2026-03-05)
Compared inputs:
- Failing side: `vphone-cli` (startup-hang-fix branch) user-provided `make boot` log.
- Successful side: `vphone-cli-dev` (main) user-provided `make boot` log.
### 12.1 Signals that appear in both logs (low-priority/noise for this issue)
The following lines appear on the successful boot too, so they are unlikely to
be the direct blocker for "cannot fully start":
1. `apfs_find_named_root_snapshot_xid ... No such file or directory (2)`
2. `TXM [Error]: selector: 45 | 78` and `failed to set boot uuid ... 78`
3. `libignition ... cryptex1 sniff: ignition failed: 8` then `ignition disabled`
4. `MKB_INIT: No system keybag found on filesystem.`
5. `mount: failed to migrate Media Keys, error = c002`
6. `Overprovision setup failed ... Ignoring...`
These are therefore weak root-cause candidates for this specific regression.
### 12.2 Differential signals (high-value)
Only/primarily observed in failing run:
1. Early `launchd` assertion:
- `assertion failed ... launchd + 59944 ... 0xffffffffffffffff`
2. JB launchd hook footprint:
- `hello from launchdhook.dylib`
- `set JB_ROOT_PATH = /private/preboot/<hash>/jb-vphone/procursus`
3. Host control channel never stabilizes to a healthy daemon session
before manual stop (`Connection reset by peer` keeps repeating).
Observed in successful run (and absent in failing excerpt):
1. Host eventually reaches:
- `[control] connected to vphoned v1 ...`
- `[control] pushing update ...`
2. No corresponding early `launchd` assertion line in provided success log.
### 12.3 Most likely causes (ranked by differential evidence)
1. **`patch-launchd-jetsam` dynamic hit risk (top suspect)**
The patcher selects a conditional branch dynamically using string xref +
backward window + return-block heuristic. A wrong branch rewrite can produce
launchd internal assertion failures while still allowing partial boot-task logs.
2. **`launchd` dylib injection (`/cores/launchdhook.dylib`) side effects**
Hook runs very early in launchd lifecycle; if environment/setup assumptions
are not met, boot can degrade without immediate kernel panic.
3. **JB-1 combined effect (jetsam patch + dylib injection), not kernel B9+**
Kernel path already reaches deep userspace tasks in both cases; this no longer
matches the previous watchdog/restore-gate kernel hang signature.
### 12.4 Recommended isolation sequence (to convert suspicion -> proof)
Use same restored disk, only vary JB-1 components:
1. `launchd` unmodified control.
2. Apply jetsam patch only.
3. Apply dylib injection only.
4. Apply both (current JB-1).
Record for each:
- whether `launchd assertion failed ... 0xffffffffffffffff` appears
- whether `[control] connected to vphoned v1` appears
- time to first stable userspace service set.

View File

@@ -1,223 +0,0 @@
# JB Mount Failure Investigation (2026-03-04)
## Symptom
- `make setup_machine JB=1` reached `cfw_install_jb` and failed at:
- `Failed to mount /dev/disk1s1 at /mnt1 (opts=rw).`
## Runtime Evidence (Normal Boot)
From `make boot` serial log:
- APFS mount tasks fail with permission errors:
- `mount_apfs: volume could not be mounted: Operation not permitted`
- `mount: /private/xarts failed with 77`
- `mount: /private/preboot failed with 77`
- launchd panics: `boot task failure: mount-phase-1 - exited due to exit(77)`
- Ignition/boot path shows entitlement-like failure:
- `handle_get_dev_by_role:13101: disk1s1 This operation needs entitlement`
This indicates failure in APFS role-based device lookup during early boot mount tasks.
## Runtime Evidence (DEV Control Run, 2026-03-04)
From a separate `fw_patch_dev + cfw_install_dev` boot log (not JB):
- `mount-phase-1` succeeded for xART:
- `disk1s3 mount-complete volume xART`
- `/dev/disk1s3 on /private/xarts ...`
- launch progressed to:
- `data-protection`
- `finish-obliteration`
- `detect-installed-roots`
- `mount-phase-2`
Interpretation: APFS boot-mount path can work on this build/kernel family after recent APFS gate changes.
This does **not** prove JB flow is fixed; it is a control signal showing the kernel-side path is not universally broken.
## Flow Separation (Critical)
- The successful `xART mount-complete` / `mount-phase-2` log is from DEV pipeline:
- `fw_patch_dev` + `cfw_install_dev`
- JB pipeline remains:
- `fw_patch_jb` + `cfw_install_jb`
- `cfw_install_jb` does **not** call `cfw_install_dev`; it runs base `cfw_install.sh` first, then JB-only phases.
## Kernel Artifact Checks
### 1) Ramdisk kernel identity
- `vm/Ramdisk/krnl.img4` payload hash was byte-identical to:
- `vm/iPhone17,3_26.1_23B85_Restore/kernelcache.research.vphone600`
So ramdisk boot was using the same restore kernel payload (no accidental file mismatch in `ramdisk_build`).
### 2) Patchability state (current VM kernel)
On `vm/iPhone17,3_26.1_23B85_Restore/kernelcache.research.vphone600`:
- Base APFS patches:
- `patch_apfs_vfsop_mount_cmp` -> not patchable (already applied)
- `patch_apfs_mount_upgrade_checks` -> not patchable (already applied)
- Key JB patches:
- `patch_mac_mount` -> patchable
- `patch_dounmount` -> patchable
- `patch_kcall10` -> patchable
Interpretation: kernel is base-patched, but critical JB mount/syscall extensions are still missing.
### 3) Reference hash comparison
- CloudOS source `kernelcache.research.vphone600` payload:
- `b6846048f3a60eab5f360fcc0f3dcb5198aa0476c86fb06eb42f6267cdbfcae0`
- VM restore kernel payload:
- `b0523ff40c8a08626549a33d89520cca616672121e762450c654f963f65536a0`
So restore kernel is modified vs source, but not fully JB-complete.
## IDA Deep-Dive (APFS mount-phase-1 path)
### 1) Failing function identified
- APFS function: `sub_FFFFFE000948EB10` (log name: `handle_get_dev_by_role`)
- Trigger string in function:
- `"%s:%d: %s This operation needs entitlement\\n"` (line 13101)
- Caller xref:
- `sub_FFFFFE000947CFE4` dispatches to `sub_FFFFFE000948EB10`
### 2) Gate logic at failure site
The deny path is reached if either check fails:
- Context gate:
- `BL sub_FFFFFE0007CCB994`
- `CBZ X0, deny`
- "Entitlement" gate (APFS role lookup privilege gate):
- `ADRL X1, "com.apple.apfs.get-dev-by-role"`
- `BL sub_FFFFFE000940CFC8`
- `CBZ W0, deny`
- Secondary role-path gate (role == 2 volume-group path):
- `BL sub_FFFFFE000817C240`
- `CBZ W0, deny` (to line 13115 block)
The deny block logs line `13101` and returns failure.
### 3) Patch sites (current vphone600 kernelcache)
- File offsets:
- `0x0248AB50` — context gate branch (`CBZ X0, deny`)
- `0x0248AB64` — role-lookup privilege gate (`CBZ W0, deny`)
- `0x0248AC24` — secondary role==2 deny branch (`CBZ W0, deny`)
- All three patched to `NOP` in the additive APFS patch.
### 4) Additional APFS EPERM(1) return paths in `apfs_vfsop_mount`
Function:
- `sub_FFFFFE0009478848` (`apfs_vfsop_mount`)
Observed EPERM-relevant deny blocks:
- Root-mount privilege deny:
- log string: `"%s:%d: not allowed to mount as root\n"`
- xref site: `0xFFFFFE000947905C`
- error return: sets `W25 = 1`
- Verification-mount privilege deny:
- log string: `"%s:%d: not allowed to do a verification mount of %s (is_suser %s ; uid %d)\n"`
- xref site: `0xFFFFFE0009479CA0`
- error return: sets `W25 = 1`
Important relation to existing Patch 13:
- At `0xFFFFFE0009479044` (same function), current code is `CMP X0, X0` (patched form),
which forces the following `B.EQ` path and should bypass one root privilege check in this region.
- Therefore, if JB still reports `mount_apfs ... Operation not permitted`, remaining EPERM candidates
include other deny branches (including the verification-mount gate path above), not only `handle_get_dev_by_role`.
## Root Cause (Updated, Two-Stage)
Stage 1 (confirmed and mitigated):
- APFS `handle_get_dev_by_role` entitlement/role deny gates were a concrete mount-phase-1 blocker.
- Additive patch now NOPs all three relevant deny branches.
Stage 2 (still under investigation, JB-only):
- DEV control run can pass `mount-phase-1`/`mount-phase-2`.
- JB failures must be analyzed with JB-only artifacts/logs and likely involve JB-only deltas
(launchd dylib injection, BaseBin hooks, or JB preboot/bootstrap interaction), in addition to any remaining kernel checks.
## Mitigation Implemented
### A) Ramdisk kernel split (updated implementation)
- `scripts/fw_patch_jb.py`
- no longer creates a ramdisk snapshot file
- `scripts/ramdisk_build.py`
- derives ramdisk kernel source internally:
- uses legacy `kernelcache.research.vphone600.ramdisk` if present
- otherwise derives from pristine CloudOS `kernelcache.research.vphone600`
under `ipsws/*CloudOS*/` using base `KernelPatcher`
- builds:
- `Ramdisk/krnl.ramdisk.img4` from derived/base source
- `Ramdisk/krnl.img4` from post-JB restore kernel
- `scripts/ramdisk_send.sh`
- prefers `krnl.ramdisk.img4` when present.
### B) Additive APFS boot-mount gate bypass (new)
- Added new base kernel patch method:
- `KernelPatchApfsMountMixin.patch_apfs_get_dev_by_role_entitlement()`
- Added to base kernel patch sequence in `scripts/patchers/kernel.py`.
- Behavior:
- NOPs three deny branches in `handle_get_dev_by_role`
- does not modify existing filesystem patches (APFS snapshot/seal/graft/mount/sandbox hooks remain unchanged).
### C) JB-only differential identified (for next isolation)
Compared with DEV flow, JB adds unique early-boot risk factors:
- launchd binary gets `LC_LOAD_DYLIB` injection for `/cores/launchdhook.dylib`
- `launchdhook.dylib`/BaseBin environment strings include:
- `JB_ROOT_PATH`
- `JB_TWEAKLOADER_PATH`
- explicit launchdhook startup logs (`hello` / `bye`)
- procursus/bootstrap content is written under preboot hash path (`/mnt5/<hash>/jb-vphone`)
These do not prove causality yet, but they are the primary JB-only candidates after Stage-1 APFS gate mitigation.
## Next Validation
1. Kernel/JB isolation run (requested):
- `make fw_patch_jb`
- `make ramdisk_build`
- `make ramdisk_send`
- run `cfw_install_dev` (not JB) on this JB-patched firmware baseline
2. Compare normal boot result:
- If `mount-phase-1/2` succeeds: strong evidence issue is in JB-only userspace phases.
- If it still fails with `EPERM`: continue kernel/APFS deny-path tracing.
3. If step 2 succeeds, add back JB phases incrementally:
- first JB-1 (launchd inject + jetsam patch)
- then JB-2 (preboot bootstrap)
- then JB-3 (BaseBin hooks)
and capture first regression point.
## 2026-03-05 Follow-up (Data-Protection / SEP UserClient MACF)
A later failure mode moved past mount-phase and failed in `data-protection`:
- `IOUC AppleSEPUserClient failed MACF ... seputil`
- `Boot task failed: data-protection - exited due to exit(60)`
This was traced to unpatched IOKit MAC policy hook range (`ops[201..210]`) in
the sandbox extended hook set. Mitigation and patch details are documented in:
- `research/boot_data_protection_seputil_macf_investigation.md`
Follow-up (2026-03-06):
- Even after `ops[201..210]` extension, runtime still showed:
- `IOUC AppleAPFSUserClient failed MACF ...`
- `IOUC AppleSEPUserClient failed MACF ...`
- A second-stage mitigation was added:
- `patch_iouc_failed_macf` (central IOUC MACF gate low-risk early return).

View File

@@ -1,160 +0,0 @@
# launchd Boot Sequence Analysis (mount-phase-1 / data volume lookup)
Date: 2026-03-06
Target binary in IDA: launchd (Darwin Bootstrapper 7.0.0, libxpc_executables-3089.42.1~6)
## Scope
This analysis focuses on the log segment:
- `mount-phase-1`
- `failed to lookup data volume - Attribute not found`
- `mount: data volume missing, but not required in env: 1`
and answers where launchd is involved vs where `mount`/APFS is actually failing.
## Ground Truth from launchd (IDA)
### 1) Boot task source is embedded plist in launchd
- Embedded config plist string contains:
- `Boot` dictionary
- `mount-phase-1` -> `Program=/sbin/mount`, `ProgramArguments=["mount","-P","1"]`
- `data-protection` -> `Program=/usr/libexec/init_data_protection`, `CSIdentityOverride=com.apple.seputil`
### 2) Boot task dictionary wiring
- `sub_10004D978`:
- reads launchd `__TEXT,__config`
- extracts `Boot` dictionary
- stores into `qword_10007F138`
- `sub_100047B94`:
- lookup task from `qword_10007F138`
- runs gate check (`sub_100047C8C`)
- executes task by calling `sub_100047DD0`
### 3) Boot sequence order (relevant slice)
- `start` -> `sub_1000489A4` -> async `sub_100048590`
- `sub_100048590` task order includes:
- `mount-phase-1`
- `data-protection`
- `finish-obliteration`
- `detect-installed-roots`
- `mount-phase-2`
### 4) Task execution and failure handling
- `sub_100047DD0`:
- logs `Doing boot task`
- `posix_spawnp()` actual executable
- for non-async tasks waits via `sub_100049180`
- `sub_100049180`:
- checks exit status
- if `RequireSuccess=true` and exit is failure -> calls `sub_100048D0C`
- `sub_100048D0C`:
- logs `Boot task failed: %s`
- logs `Panicking in 3 seconds.`
- then panics
## Key Conclusion: where the quoted error originates
`failed to lookup data volume - Attribute not found` is **not** generated by launchd internals.
In this path, launchd is only the orchestrator:
1. launchd runs `/sbin/mount -P 1` for `mount-phase-1`
2. `mount` prints `failed to lookup data volume...`
3. launchd only sees child exit result, then decides whether to panic based on `RequireSuccess`
So this errors primary fault domain is in `mount` + APFS/IOKit interactions, not in launchd task scheduler code.
## `mount -P 1` call chain (IDA-verified)
Target binary: `research/artifacts/launchd_23B85/mount.from_vm_disk.current`
- `start` @ `0x100003DC8`
- phase path calls `sub_100003480(&env)` then `sub_100003674()`
- `sub_100003480` @ `0x100003480`
- reads `IODeviceTree:/filesystems/fstab` property `os_env_type`
- calls `APFSContainerGetBootDevice(&CFString)`
- builds `/dev/<boot-container>` string in global buffer
- `sub_100003674` @ `0x100003674`
- calls `APFSVolumeRoleFind(<bootdev>, 0x40, &CFArray)`
- on non-zero return:
- `fprintf("%sfailed to lookup data volume - %s\n", ..., strerror(ret & 0x3fff))`
- if single match, converts CFString -> data volume path
Important:
- `0x40` is the queried role selector in this build.
- `Attribute not found` corresponds to `ENOATTR` (`93`) after `ret & 0x3fff`.
- phase-1 can continue with warning, but this often cascades into `data-protection` failure in your failing trace set.
## Updated Causality (with new control evidence)
User-provided control result:
- `cfw_install` (without JB extras) reproduces the same failure.
- TXM path is known-good in this setup.
Implication:
- JB-only userspace deltas are no longer primary suspects for this error.
- Current highest-confidence differentiator is kernel state/patch delta.
## Plausible causes (re-ranked)
### A. kernel-side causes (highest probability now)
1. APFS role/device lookup path is denied/altered by kernel policy path for `mount` (`IOUC AppleAPFSUserClient ... MACF` class of failure).
2. Kernel APFS patch interaction causes role metadata read path to return "attribute not found".
3. Kernel patch ordering or overlap in `fw_patch_jb` modifies behavior that minimal/non-JB flow does not.
### B. mount userspace path causes (still relevant, but secondary to A)
1. `mount -P 1` phase logic expects Data role metadata that is unavailable under current kernel behavior.
2. Userspace APFS query path receives transformed errno/status from kernel and prints attribute-missing message.
### C. launchd-level causes (currently de-prioritized)
1. launchd task definition mismatch.
2. spawn-level failures before mount logic.
3. task gating differences.
These are less consistent with the new control result and with observed mount-origin log text.
## Practical meaning
- For this failure, launchd reverse already gives enough certainty that launchd is orchestrator only.
- Next decisive work should move to APFS userspace API return-site tracing and corresponding kernel handlers.
- Detailed `mount -P 1` failure/hang matrix is documented in:
- `research/boot_mount_phase1_failure_matrix.md`
## Most likely kernel-side silent-fail line (current ranking)
1. `APFSVolumeRoleFind` path reaches APFS userclient method and gets transformed deny/error (most consistent with `IOUC AppleAPFSUserClient failed MACF ... mount` history).
2. Base APFS entitlement bypass patch (`patch_apfs_get_dev_by_role_entitlement`, patch #16) matched the wrong deny branch or altered control flow for role lookup.
3. Base sandbox-op stubs (mount/vnode related) hit an unintended target due ops-table drift.
Lower probability for this exact string:
- launchd plist/task ordering itself
- fstab format/ramdisk missing (would produce different dominant signatures)
## Artifact notes
Current extracted launchd sample (for reproducible local reference):
- `research/artifacts/launchd_23B85/launchd.from_vm_disk.current`
- sha256: `411d730c95d99a088e94b673eff3fa73d6d3cc778b24b476cd0b7866cd037443`
- `research/artifacts/launchd_23B85/launchd.plist.from_vm_disk.current`
- sha256: `dc972e30220b3e9e8323d23ce4a4737d849893dd79e305693de902ff65ddacab`
Observed in this sample:
- no `/cores/launchdhook.dylib` load command
- launchd embedded boot task plist present and readable

View File

@@ -1,107 +0,0 @@
# Boot Log Comparison Analysis (fail vs success)
Date: 2026-03-06
Scope: compare `/Users/qaq/Desktop/boot.fail.log` and `/Users/qaq/Desktop/boot.success.log` for the current startup failure investigation.
## Executive Verdict
- The fail path is a `launchd` userspace panic caused by `data-protection` task `SIGTRAP`, not an APFS kernel panic.
- The immediate trigger in fail log is that mount could not find the APFS Data volume metadata:
- `failed to lookup data volume - Attribute not found`
- `mount: data volume missing, but not required in env: 1`
- APFS itself does load in both logs with the same version (`2632.40.15`) and continues mounting volumes.
- Based on these two logs alone, evidence is stronger for "Data volume discovery/mapping issue" than "APFS patch count too high".
## Key Evidence
1. APFS module load is healthy in both runs
- Fail: `apfs_module_start ... com.apple.filesystems.apfs, v2632.40.15` (line 178)
- Success: same APFS load/version (line 250)
- Interpretation: no direct sign that APFS kext fails to initialize in fail run.
2. First hard divergence is in mount-phase-1 Data volume resolution
- Fail:
- `failed to lookup data volume - Attribute not found` (line 420)
- `mount: data volume missing, but not required in env: 1` (line 421)
- Success:
- `mount: found boot container: /dev/disk1, data volume: /dev/disk1s2 env: 1` (line 423)
- Interpretation: fail run cannot resolve data volume metadata; success run can.
3. data-protection task outcome differs immediately after that
- Fail:
- `(data-protection) <Error>: exited due to SIGTRAP` (line 432)
- `Boot task failed: data-protection - exited due to SIGTRAP` (line 433)
- `userspace panic` follows (line 458)
- Success:
- `init_data_protection: Gigalocker initialization completed` (line 434)
- boot continues into `mount-phase-2` and beyond (line 502+)
- Interpretation: the crash is in boot task flow after data volume lookup failure, not in APFS module load.
4. Success path shows APFS warnings that are non-fatal
- `mount: failed to migrate Media Keys, error = c002` (line 522)
- `mount_phase_two ... Overprovision setup failed ... Ignoring...` (line 560)
- Interpretation: APFS/AKS warnings can be tolerated when data volume path is intact; these are not the blocking condition here.
## Additional Differences That Confound Direct "Patch Count" Attribution
- Different host build/hash inputs:
- vphoned `GIT_HASH` differs (`e4456e9` vs `fd08c43`)
- binary path differs (`vphone-cli` vs `vphone-cli-dev`)
- `vphoned` signed hash differs
- Different device identity:
- ECID differs across logs
- Different APFS checkpoint state:
- Fail: `cleanly-unmounted`, largest xid `198`
- Success: `reloading after unclean unmount`, largest xid `491`
These differences mean this is not a strict A/B test of only "APFS patch count".
## Assessment of "APFS patch applied too much?"
Current confidence: low-to-medium for that hypothesis from logs alone.
What logs support:
- The failure does involve APFS mount phase and data-protection.
What logs do not support:
- No APFS module crash/oops/panic.
- No explicit APFS patch integrity failure.
- The strongest fail signal is missing data volume attribute, not APFS code-path abort.
More likely from current evidence:
- APFS container/volume role metadata mismatch, or
- environment/image drift between the two runs, causing different boot task assumptions.
## Suggested Next Validation (minimal and decisive)
1. Re-run with identical binaries and same VM snapshot, toggling only APFS-related patch set.
2. Capture APFS volume-role metadata before boot task (expect `disk1s2` Data role to be discoverable).
3. Compare generated firmware/CFW artifacts checksums between fail/success pipelines.
4. If failure reproduces only with APFS patch delta, then bisect APFS patch subset around data-volume lookup path.
## Bottom Line
From these two logs, the actionable breakpoint is:
- "data volume lookup failed" -> "data-protection SIGTRAP" -> userspace panic.
This is a stronger lead than "APFS patch count over-applied", and should be the first branch to validate.
## Update (Control Run)
New control signal from user:
- Same failure reproduces with `cfw_install` (without JB extras).
- TXM is known working in this control.
Updated implication:
- The prior "JB userspace difference" suspicion should be de-prioritized.
- Current primary suspect becomes kernel delta (especially APFS/IOUC/MACF-related behavior under `mount -P 1`).

View File

@@ -1,195 +0,0 @@
# mount `-P 1` Failure / Hang Matrix (IDA)
Date: 2026-03-06
Target: `research/artifacts/launchd_23B85/mount.from_vm_disk.current`
## What `mount -P 1` actually does
Main entry: `start` @ `0x100003DC8`
For `-P 1` (phase-1):
1. Parse phase from `-P` and set global `dword_1000101F4 = 1`.
2. Call `setfsent()` and iterate fstab entries.
3. Resolve boot container/device via `sub_100003480` (`APFSContainerGetBootDevice`).
4. Resolve data volume via `sub_100003674` (`APFSVolumeRoleFind`).
5. Print either:
- `mount: found boot container: ..., data volume: ..., env: ...`
- or `mount: data volume missing, but not required in env: ...`
6. Continue mounting entries with pass number == phase via `sub_1000045B0` (exec `mount_<fstype>`).
Important: for phase-1, missing data volume is normally **not fatal**.
### Critical implementation detail (IDA)
- `sub_100003674` calls:
- `APFSVolumeRoleFind(<bootdev>, 0x40, &outArray)`
- If return != 0:
- prints `failed to lookup data volume - %s` with `strerror(ret & 0x3fff)`
- `Attribute not found` in your log maps to Darwin `ENOATTR(93)`.
### Important caveat on `93` provenance
- Confirmed fact:
- `mount` sees `APFSVolumeRoleFind` return value whose low 14 bits are `93`.
- Not yet proven:
- that kernel returns `93` directly.
- Also plausible:
- APFS userspace layer maps another kernel/APFS status to `ENOATTR` before returning to `mount`.
## All plausible causes for phase-1 fail/hang
### A) Early argument / mode errors (immediate fail)
1. Invalid `-P` value
- message: `-P flag requires a valid mount phase number`
- location: `start` (`0x1000039C4`..`0x100003A70`)
2. Invalid invocation shape (bad argv combination)
- falls into usage path (`sub_1000043B0`)
### B) Boot container / APFS role lookup path
1. Cannot read filesystem info from IORegistry (`os_env_type`)
- message: `failed to get filesystem info`
- function: `sub_100003480`
2. `APFSContainerGetBootDevice` failure
- message: `failed to get boot device - ...` (with retry loop outside restore env)
- function: `sub_100003480`
3. `APFSVolumeRoleFind` failure
- message: `failed to lookup data volume - ...`
- function: `sub_100003674`
4. Multiple Data volumes found
- message: `found multiple data volumes`
- function: `sub_100003674`
Note:
- phase-1 usually continues after (3)/(4).
- phase-2 has stricter fatal behavior on missing data volume in env=1.
### C) fstab traversal / entry filtering issues
1. `setfsent()` failure
- message: `mount: can't get filesystem checklist`
- fatal for phase run
2. Entry type / spec/path invalidity
- examples:
- `%s: invalid special file or file system.`
- `%s: unknown special file or file system.`
- `You must specify a filesystem type with -t.`
These are input/config failures before actual fs-specific helper mount.
### D) Per-filesystem mount helper failures (major phase-1 failure source)
Dispatcher: `sub_1000045B0`
1. FSKit path failure (`sub_100000BC0`)
- messages:
- `File system named %s not found`
- `File system named %s unable to mount`
- `FSKit unavailable`
2. `fork()` / `waitpid()` / child process control failures
- messages include wait/fork warnings in helper path
3. `exec` failure for `mount_<fstype>` helpers
- tries `/sbin/mount_<fstype>` then fallback paths under `/System/Library/Filesystems/...`
- if all fail -> returns mapped failure code
4. Helper exits non-zero or gets signaled
- parent treats as mount failure and propagates code
This bucket is the most common direct reason phase-1 exits non-zero.
### E) Ramdisk special path failures (if ramdisk entry is hit in phase-1)
Ramdisk path: `sub_100002688` + `sub_100002C34` + command wrapper `sub_100002EA4`
Possible failures:
1. preflight format/option parsing fail (`Ramdisk fstab not in expected format.`)
2. `mount_tmpfs` exec or command-run failures
3. copyfile / final mount / umount failures
Not always relevant, but can fail phase-1 if fstab phase-1 includes ramdisk flow.
### F) Kernel / IOKit policy-deny mediated failures (high-probability in your current repro)
From your runtime evidence and control results:
1. `mount` process can hit IOUC/MACF deny path on APFS UserClient access.
2. userspace may surface this as role/attribute lookup failure string, while root cause is kernel-side deny/altered return.
Given:
- same failure reproduces with non-JB `cfw_install`
- TXM known-good
current priority remains kernel delta analysis.
## Kernel patch candidates for this specific signature (ranked)
### 1) Base patch #16 (`patch_apfs_get_dev_by_role_entitlement`) — highest
Why high:
- It directly targets APFS get-dev-by-role gate, which is exactly adjacent to `APFSVolumeRoleFind` behavior.
- It NOPs conditional branches by pattern heuristics; a false match can silently alter return path while keeping system alive.
- Symptom shape fits: boot container lookup can still succeed, but role lookup returns `ENOATTR`.
- Live-kernel validation status:
- patch #16 is present (all 3 target branches are `nop` at runtime).
- therefore current question is semantic side effect, not "patch missing".
### 2) Base sandbox hook patch (`patch_sandbox_hooks`) — medium
Why medium:
- Touches mount/vnode MACF paths by ops-table index.
- If ops index resolution drifts, wrong function may be stubbed and produce semantic corruption instead of crash.
### 3) Base APFS mount checks (#13/#14) — lower for this exact error
Why lower:
- These primarily alter mount authorization/upgrade checks.
- Less directly tied to role-attribute lookup API return code, but still in APFS mount vicinity.
## What to do next (action order)
1. Confirm userland return-site:
- break at `sub_1000036C0` (`BL _APFSVolumeRoleFind`) and inspect `w0` after return.
- expected failing value path: `w0 & 0x3fff == 93`.
2. Correlate with kernel-side return path in the same boot:
- break/trace APFS kernel role lookup function return (`handle_get_dev_by_role` path) and record final returned `w0`.
- determine whether kernel returns `93`, `22`, or other value when userspace later sees `93`.
3. Correlate with kernel log at same timestamp:
- look for `IOUC AppleAPFSUserClient failed MACF in process mount`.
4. Do base-kernel patch isolation first (not JB methods):
- run with base patch #16 disabled (or reverted) while keeping others unchanged.
- if failure clears, root cause is narrowed to entitlement-bypass matcher.
5. If #16 is not root cause, isolate base sandbox hook patch.
6. Only then continue to JB-only methods (`VPHONE_JB_DISABLE_METHODS`), because your latest control says non-JB install still reproduces.
## Hang/stall-specific points (not just hard fail)
1. Boot-device lookup retry loop (`APFSContainerGetBootDevice`) with sleep retries.
2. Child `mount_<fstype>` helper blocking in kernel/IO path (parent waiting in `waitpid`).
3. External command wrapper (`sub_100002EA4`) blocking while waiting for command output/exit.
These produce "looks stuck" behavior even before explicit non-zero exit.
## Practical triage checklist for phase-1
1. Confirm exact failing subpath:
- preflight/APFS lookup vs helper mount vs waitpid/exec.
2. Correlate with kernel log at same timestamp:
- especially `IOUC AppleAPFSUserClient ... mount`.
3. Separate:
- non-fatal data-volume warning in phase-1
- true fatal return path that makes launchd panic on `RequireSuccess`.

View File

@@ -1,6 +1,6 @@
# Kernel JB Remaining Patches — Research Notes
Last updated: 2026-03-04
Last updated: 2026-03-07
## Overview
@@ -12,6 +12,12 @@ Last updated: 2026-03-04
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.
On 2026-03-06, three patches were retargeted after IDA-MCP re-analysis revealed their matchers were hitting wrong sites:
- `patch_bsd_init_auth` — was hitting `exec_handle_sugid` instead of the real `bsd_init` rootauth gate
- `patch_io_secure_bsd_root` — was patching the `"SecureRoot"` dispatch branch instead of the `"SecureRootName"` deny-return
- `patch_vm_fault_enter_prepare` — was NOPing a `pmap_lock_phys_page()` call instead of the upstream `cs_bypass` gate
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)
@@ -383,6 +389,24 @@ Should have moderate caller count (hundreds).
**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)`.
### patch_bsd_init_auth — RETARGETED (2026-03-06)
**Historical repo behavior**: matched `ldr x0,[xN,#0x2b8]; cbz x0; bl` pattern, which landed on `exec_handle_sugid` at `0xFFFFFE0007FB09DC` — a false positive caused by `/dev/null` string overlap in the heuristic scoring.
**Problem**: the old matcher targeted the wrong function entirely; patching `exec_handle_sugid` instead of the real `bsd_init` rootauth gate could break boot by mutating an exec/credential path.
**Current status**: retargeted to the real `FSIOC_KERNEL_ROOTAUTH` return check in `bsd_init`. The new matcher recovers `bsd_init` via in-kernel string xrefs, locates the rootvp panic block (`"rootvp not authenticated after mounting"`), finds the unique in-function indirect call (`BLRAA`) preceded by the `0x80046833` (`FSIOC_KERNEL_ROOTAUTH`) literal, and NOPs the subsequent `CBNZ W0, panic`. Live patch hit: `0xFFFFFE0007F7B98C` / file offset `0x00F7798C`. See `research/kernel_patch_jb/patch_bsd_init_auth.md`.
### patch_io_secure_bsd_root — RETARGETED (2026-03-06)
**Historical repo behavior**: fallback heuristic selected the first `BL* + CBZ W0` site in `AppleARMPE::callPlatformFunction`, landing on the `"SecureRoot"` name-match gate at `0xFFFFFE000836E1F0` / file offset `0x0136A1F0`. This changed generic platform-function dispatch routing, not just the deny return.
**Problem**: the patched branch was the `isEqualTo("SecureRoot")` check, not the `"SecureRootName"` policy result used by `IOSecureBSDRoot()`. The old `CBZ->B` rewrite could corrupt control flow for unrelated platform-function calls.
**Current status**: retargeted to the final `"SecureRootName"` deny-return selector: `CSEL W22, WZR, W9, NE` at `0xFFFFFE000836E464` / file offset `0x0136A464` is replaced with `MOV W22, #0`. This preserves the string comparison, callback synchronization, and state updates, and only forces the final policy return from `kIOReturnNotPrivileged` to success. See `research/kernel_patch_jb/patch_io_secure_bsd_root.md`.
### patch_vm_fault_enter_prepare — RETARGETED (2026-03-06)
**Historical repo behavior**: matcher looked for `BL(rare) + LDRB [xN,#0x2c] + TBZ` and NOPed the BL at `0xFFFFFE0007BB898C`, which was actually a `pmap_lock_phys_page()` call inside the `VM_PAGE_CONSUME_CLUSTERED` macro — breaking lock/unlock pairing in the VM fault path.
**Problem**: the derived matcher overfit the wrong local shape. The upstream 26.1 patch targeted the `cs_bypass` fast-path gate (`TBZ W22, #3`), not the clustered-page lock helper. NOPing only the lock acquire while the unlock still ran caused unbalanced lock state, explaining boot failures.
**Current status**: retargeted to the upstream semantic site — `TBZ W22, #3, ...` (where W22 bit 3 = `fault_info->cs_bypass`) at file offset `0x00BA9E1C` / VA `0xFFFFFE0007BADE1C` is replaced with `NOP`, forcing the `cs_bypass` fast path unconditionally. This matches XNU's `vm_fault_cs_check_violation()` logic and preserves lock pairing and page accounting. See `research/kernel_patch_jb/patch_vm_fault_enter_prepare.md`.
---
## Environment Notes

View File

@@ -132,7 +132,7 @@ Observed output:
- `scripts/patchers/kernel_jb_patch_hook_cred_label.py` now implements faithful upstream C23 semantics
- `scripts/patchers/kernel_jb.py` includes `patch_hook_cred_label_update_execve` in the active Group C schedule
- `research/00_patch_comparison_all_variants.md` should describe C23 as a faithful wrapper trampoline, not as a mis-targeted early-return patch
- `research/0_binary_patch_comparison.md` should describe C23 as a faithful wrapper trampoline, not as a mis-targeted early-return patch
## Practical Effect

View File

@@ -1,4 +1,4 @@
# Kernel Patch Validation: Sandbox Hooks 21-26 (Regular/Development)
# Kernel Patch Validation: Sandbox Hooks 17-26 (Regular/Development)
Date: 2026-03-05
@@ -6,6 +6,8 @@ Date: 2026-03-05
Validate the following non-JB kernel patches on a freshly prepared (unpatched) firmware kernelcache:
- 17/18 `file_check_mmap`: `mov x0,#0` + `ret`
- 19/20 `mount_check_mount`: `mov x0,#0` + `ret`
- 21/22 `mount_check_remount`: `mov x0,#0` + `ret`
- 23/24 `mount_check_umount`: `mov x0,#0` + `ret`
- 25/26 `vnode_check_rename`: `mov x0,#0` + `ret`
@@ -27,6 +29,8 @@ Patch flow under test:
1. `_find_sandbox_ops_table_via_conf()` to locate `mac_policy_conf`
2. `mpc_ops` pointer to read function entries by index
3. `HOOK_INDICES`:
- `file_check_mmap = 36`
- `mount_check_mount = 87`
- `mount_check_remount = 88`
- `mount_check_umount = 91`
- `vnode_check_rename = 120`
@@ -73,7 +77,7 @@ From direct `KernelPatcher` run on clean payload (in-memory, no file write):
Using IDA DB and disassembly/decompile on the same firmware family:
- Entry sites match the three hook slots above.
- Entry sites match the five hook slots above.
- For `vnode_check_rename`, downstream body includes rename-related path monitoring logic (`pathmonitor_prepare_rename`), confirming semantic alignment with rename hook behavior.
- Note: current IDA database had these entry points already recognized as patched stubs; additional inspection was performed from `entry+8` into original body for semantic validation.
@@ -81,8 +85,8 @@ Using IDA DB and disassembly/decompile on the same firmware family:
Status: **working for now**.
For clean `fw_prepare` kernelcache, the 21-26 sandbox hook patches:
For clean `fw_prepare` kernelcache, the 17-26 sandbox hook patches:
- resolve through the correct `mac_policy_ops` table,
- hit the expected three hook entry addresses,
- hit the expected five hook entry addresses,
- and rewrite exactly the first two instructions to `mov x0,#0; ret`.

View File

@@ -51,8 +51,8 @@ apply the dynamic patcher to a freshly extracted vresearch101 kernelcache.
Result: **byte-identical** output between hardcoded and dynamic patching.
- `KernelPatcher` patches found: 25
- Hardcoded patches applied: 25
- `KernelPatcher` patches found: 26
- Hardcoded patches applied: 26
- `cmp -l /tmp/kc_vphone600_upstream.raw /tmp/kc_vphone600_dynamic.raw`:
no output (files identical)
@@ -60,33 +60,34 @@ Result: **byte-identical** output between hardcoded and dynamic patching.
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 | | | \_handle_get_dev_by_role entitlement bypass |
| 17 | 0x23AC528 | 0xD2800000 | \_hook_file_check_mmap mov x0,#0 |
| 18 | 0x23AC52C | 0xD65F03C0 | \_hook_file_check_mmap ret |
| 19 | 0x23AAB58 | 0xD2800000 | \_hook_mount_check_mount mov x0,#0 |
| 20 | 0x23AAB5C | 0xD65F03C0 | \_hook_mount_check_mount ret |
| 21 | 0x23AA9A0 | 0xD2800000 | \_hook_mount_check_remount mov x0,#0 |
| 22 | 0x23AA9A4 | 0xD65F03C0 | \_hook_mount_check_remount ret |
| 23 | 0x23AA80C | 0xD2800000 | \_hook_mount_check_umount mov x0,#0 |
| 24 | 0x23AA810 | 0xD65F03C0 | \_hook_mount_check_umount ret |
| 25 | 0x23A5514 | 0xD2800000 | \_hook_vnode_check_rename mov x0,#0 |
| 26 | 0x23A5518 | 0xD65F03C0 | \_hook_vnode_check_rename ret |
## TXM Patch Details
@@ -125,7 +126,7 @@ pyimg4 im4p extract \
Dynamic patcher results:
- Patches found/applied: 25
- Patches found/applied: 26
- TXM patch location: `0xFA6B98` (NOP `tbnz w8, #0, #0xfa6c80`)
- Patched output: `/tmp/kc_vresearch1_dynamic.raw`
@@ -134,7 +135,7 @@ Dynamic patcher results:
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
vresearch101 kernelcache with the expected TXM NOP and a full 25-patch set.
vresearch101 kernelcache with the expected TXM NOP and a full 26-patch set.
## Targeted Re-Verification (2026-03-05)

View File

@@ -1,6 +1,6 @@
# TXM Jailbreak Patch Analysis
Analysis of 6 logical TXM jailbreak patches (11 instruction modifications) applied by `txm_jb.py` on the RESEARCH variant
Analysis of 6 logical TXM jailbreak patches (11 instruction modifications) applied by `txm_dev.py` on the RESEARCH variant
of TXM from iPhone17,3 / PCC-CloudOS 26.x.
## TXM Execution Model
@@ -314,7 +314,7 @@ 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()`
### `txm_dev.py` dynamic finder: `patch_selector24_force_pass()`
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
@@ -412,7 +412,7 @@ Universal entitlement lookup function. When `a1 != 0`, it resolves the manifest'
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()`
### `txm_dev.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`.
@@ -495,7 +495,7 @@ Since the validator returns the pointer unchanged, `x20` (raw arg) and the valid
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()`
### `txm_dev.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`
@@ -557,7 +557,7 @@ 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()`
### `txm_dev.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
@@ -645,7 +645,7 @@ if ( (byte_FFFFFFF017070F24 & 1) == 0 )
return 27; // developer mode not enabled
```
### `txm_jb.py` dynamic finder: `patch_developer_mode_bypass()`
### `txm_dev.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