docs: fix outdated patch counts, remove process investigation notes

- Update firmware variant table (CLAUDE.md, AGENTS.md): correct patch
  totals to 51/64/126, add VPhoneMenuBattery.swift, setup_venv_linux.sh,
  tail_jb_patch_logs.sh to architecture tree
- kernel_patcher_verification.md: 25→26 patches (patch_apfs_get_dev_by_role)
- kernel_patch_sandbox.py: docstring 16-25→17-26
- kernel_patch_sandbox_hooks: consolidate to single 17-26 validation file
- txm_jb_patches.md: fix txm_jb.py references → txm_dev.py
- 0_binary_patch_comparison.md: split kernel counts (28 base + 59 JB methods)
- kernel_jb_patch_notes.md: add 2026-03-06 retarget notes for bsd_init_auth,
  io_secure_bsd_root, vm_fault_enter_prepare
- Remove 7 boot investigation process notes (boot_*.md)
This commit is contained in:
Lakr
2026-03-07 18:23:34 +08:00
parent b3ed19232b
commit 048f4c7cc1
14 changed files with 70 additions and 1720 deletions

View File

@@ -31,9 +31,9 @@ For any changes applying new patches, also update research/0_binary_patch_compar
| Variant | Boot Chain | CFW | Make Targets |
| ------------------- | :--------: | :-------: | ---------------------------------- |
| **Regular** | 38 patches | 10 phases | `fw_patch` + `cfw_install` |
| **Development** | 49 patches | 12 phases | `fw_patch_dev` + `cfw_install_dev` |
| **Jailbreak (WIP)** | 84 patches | 14 phases | `fw_patch_jb` + `cfw_install_jb` |
| **Regular** | 51 patches | 10 phases | `fw_patch` + `cfw_install` |
| **Development** | 64 patches | 12 phases | `fw_patch_dev` + `cfw_install_dev` |
| **Jailbreak (WIP)** | 126 patches | 14 phases | `fw_patch_jb` + `cfw_install_jb` |
See `research/` for detailed firmware pipeline, component origins, patch breakdowns, and boot flow documentation.
@@ -73,6 +73,7 @@ sources/
├── VPhoneMenuConnect.swift # Connect menu — devmode, ping, version, file browser
├── VPhoneMenuInstall.swift # Install menu — IPA installation to guest
├── VPhoneMenuRecord.swift # Record menu — screen recording controls
├── VPhoneMenuBattery.swift # Battery menu — battery status display
│ # IPA installation
├── VPhoneIPAInstaller.swift # IPA extraction, signing, and installation
@@ -89,8 +90,8 @@ scripts/
├── patchers/ # Python patcher modules
│ ├── iboot.py # iBoot patcher (iBSS/iBEC/LLB)
│ ├── iboot_jb.py # JB: iBoot nonce skip
│ ├── kernel.py # Kernel patcher (25 patches)
│ ├── kernel_jb.py # JB: kernel patches (~34)
│ ├── kernel.py # Kernel patcher (26 patches)
│ ├── kernel_jb.py # JB: kernel patches (~40)
│ ├── txm.py # TXM patcher
│ ├── txm_dev.py # Dev: TXM entitlements/debugger/dev mode
@@ -111,7 +112,9 @@ scripts/
├── setup_machine.sh # Full automation (setup → first boot)
├── setup_tools.sh # Install deps, build toolchain, create venv
├── setup_venv.sh # Create Python venv
── setup_libimobiledevice.sh # Build libimobiledevice from source
── setup_venv_linux.sh # Create Python venv (Linux)
├── setup_libimobiledevice.sh # Build libimobiledevice from source
└── tail_jb_patch_logs.sh # Tail JB patch log output
research/ # Detailed firmware/patch documentation
```

View File

@@ -77,7 +77,7 @@
| # | 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 |
@@ -153,12 +153,13 @@
| 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
@@ -166,7 +167,7 @@
| ------------- | ------------------- | -------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------- |
| `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` |
| `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/0_binary_patch_comparison.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/0_binary_patch_comparison.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,254 +0,0 @@
# Launchdhook Assertion Handoff (2026-03-06)
## Scope
This note captures the current userspace-side findings for the failing `fw_patch_jb + cfw_install_jb` path.
It is intended as a handoff artifact for follow-up work on the `fix-boot` branch.
The current symptom is no longer "launchd does not start".
The updated symptom is:
- `launchd` starts
- injected `launchdhook.dylib` definitely loads
- `launchd` then hits an early internal assertion before the expected `bash` / follow-on job chain stabilizes
## Executive Summary
### Confirmed
- The original JB `LC_LOAD_DYLIB /cores/launchdhook.dylib` approach is structurally unsafe on the current `launchd` sample because there is not enough load-command slack.
- A short-path alias experiment fixed the Mach-O header-space problem:
- `/cores/launchdhook.dylib` requires 56 bytes and overruns into `__TEXT,__text`
- `/cores/b` still requires 40 bytes and also overruns
- `/b` requires 32 bytes and fits exactly after removing `LC_CODE_SIGNATURE`
- Runtime test with `/b` proves the short-path alias loads successfully, but the main failure remains:
- `launchdhook.dylib` prints its startup logs
- `launchd` then asserts early: `launchd + 59944 ... 0xffffffffffffffff`
### Current conclusion
The short-path `/b` alias fixes the **injection-space** problem, but does **not** fix the **launchd assertion**.
So the remaining problem is now more likely in the hook logic (especially early XPC / daemon config hooks) than in the raw load-command insertion path.
## Evidence Collected
### 1. Mach-O injection space audit
Local dry-run against `vm/.cfw_temp/launchd` established the following:
- Existing load-command slack before the first section: 16 bytes
- After stripping `LC_CODE_SIGNATURE`: 32 bytes
- Required command sizes:
- `/cores/launchdhook.dylib` -> 56 bytes
- `/cores/b` -> 40 bytes
- `/b` -> 32 bytes
Observed effect of the original long-path injection:
- `LC_LOAD_DYLIB /cores/launchdhook.dylib` overwrote the beginning of `__TEXT,__text`
- first instructions at the start of the text section were replaced by injected path bytes
Observed effect of the short-path injection:
- `LC_LOAD_DYLIB /b` fits exactly in the available 32 bytes after `LC_CODE_SIGNATURE` removal
- no additional overwrite into `__TEXT,__text` is needed for that path
### 2. Device-side mount and payload verification
Inside ramdisk shell, manual mount and inspection showed:
- `/dev/disk1s1` mounted at `/mnt1`
- `/dev/disk1s5` mounted at `/mnt5`
- `/mnt1/b` exists and is a Mach-O dylib
- `/mnt1/cores/launchdhook.dylib` exists and is a Mach-O dylib
- `/mnt1/cores/systemhook.dylib` and `/mnt1/cores/libellekit.dylib` are also present
Important clarification:
- `/.b` is an existing hidden root directory on this filesystem and is unrelated to the alias experiment
- the experiment path is `/b`, not `/.b`
### 3. Runtime serial log after switching to `/b`
The following lines appeared during boot:
- `set JB_ROOT_PATH = /private/preboot/<hash>/jb-vphone/procursus`
- `=========== hello from launchdhook.dylib ===========`
- `=========== bye from launchdhook.dylib ===========`
- `com.apple.xpc.launchd ... assertion failed: ... launchd + 59944 ... 0xffffffffffffffff`
Interpretation:
- `/b` injection is working
- `launchdhook.dylib` is loaded and runs its initializer path
- the failure is no longer attributable to the long path not loading or to the Mach-O injection missing outright
## Source-Backed Analysis from Dopamine BaseBin
Source tree used:
- `/Users/qaq/Documents/GitHub/Dopamine/BaseBin`
### 1. launchdhook initialization order
From `Dopamine/BaseBin/launchdhook/src/main.m`, the constructor initializes hooks in this order:
1. `initXPCHooks();`
2. `initDaemonHooks();`
3. `initSpawnHooks();`
4. `initIPCHooks();`
5. `initJetsamHook();`
This matters because the current assertion happens very early, after `launchdhook` has definitely run.
That makes the earlier hooks higher-priority suspects than spawn-time behavior.
### 2. What `initDaemonHooks()` actually does
From `Dopamine/BaseBin/launchdhook/src/daemon_hook.m`:
- hooks `xpc_dictionary_get_value`
- rewrites behavior for these keys:
- `LaunchDaemons`
- `Paths`
- `com.apple.private.xpc.launchd.userspace-reboot`
Behavior summary:
- appends jailbreak daemon plist entries from:
- `JBROOT_PATH("/basebin/LaunchDaemons")`
- `JBROOT_PATH("/Library/LaunchDaemons")`
- appends those same directories to `Paths`
- conditionally returns `com.apple.private.iowatchdog.user-access` when `userspace-reboot` is false/missing
This hook touches exactly the kind of launchd configuration objects that are consulted during early daemon/bootstrap setup.
### 3. What `initSpawnHooks()` actually does
From `Dopamine/BaseBin/launchdhook/src/spawn_hook.c`:
- hooks `__posix_spawn`
- during early boot, it intentionally avoids broad injection until `xpcproxy` appears
- once `xpcproxy` is seen, it flips out of early-boot mode and uses `posix_spawn_hook_shared(...)`
Interpretation:
- spawn hook is real, but it is comparatively later than the daemon config hook
- given the current assertion timing, `initSpawnHooks()` is no longer the top suspect
### 4. What `initXPCHooks()` does
From `Dopamine/BaseBin/launchdhook/src/xpc_hook.c`:
- hooks `xpc_receive_mach_msg`
- participates in jbserver message handling and filtering inside launchd/XPC path
This is also an early-launchd hook and remains a second-tier suspect if daemon-hook isolation does not clear the assertion.
### 5. Runtime jetsam hook vs our static jetsam patch
From `Dopamine/BaseBin/launchdhook/src/jetsam_hook.c`:
- Dopamine also installs a runtime hook on `memorystatus_control`
- this is separate from the repo's static `scripts/patchers/cfw_patch_jetsam.py` binary patch
Therefore two different "jetsam" mechanisms now exist in the failing path:
- static launchd branch patch
- runtime `memorystatus_control` hook
This does not prove either is the current cause, but it means the term "jetsam patch" must be disambiguated in future debugging.
## Current Suspect Ranking
### Highest probability
1. **`initDaemonHooks()` / `daemon_hook.m`**
- hooks `xpc_dictionary_get_value`
- mutates `LaunchDaemons` and `Paths`
- timing matches the observed early `launchd` assertion better than spawn-time logic
### Medium probability
2. **`initXPCHooks()` / `xpc_hook.c`**
- also runs before spawn hook
- directly changes launchd/XPC message handling
3. **static `patch-launchd-jetsam` matcher**
- still considered risky because its matching strategy is heuristic and not CFG-constrained
- but the `/b` experiment shows the assertion survives after fixing the obvious load-command overflow issue
### Lower probability for the current symptom timing
4. **`initSpawnHooks()` / `spawn_hook.c`**
- still relevant for later `bash` / job launch failures
- but no longer the best first suspect for the early `launchd + 59944` assertion
## Recommended Isolation Order for `fix-boot`
### Stage 1: no-daemon-hook control
Goal:
- keep `launchdhook.dylib` loading
- keep `/b` short-path alias experiment in place
- disable only `initDaemonHooks()`
Reason:
- this is the cleanest test of the current top suspect
- if the assertion disappears, the root issue is inside `daemon_hook.m`
### Stage 2: no-xpc-hook control
If stage 1 still asserts:
- restore daemon hook or keep it off, but disable `initXPCHooks()` next
- test whether the assertion is tied to XPC receive hook path instead
### Stage 3: no-spawn-hook control
Only after stages 1 and 2:
- disable `initSpawnHooks()`
- use this to isolate later `bash` / child-process failures if the launchd assertion is already gone or moves later
### Stage 4: revisit static launchd jetsam patch
If all runtime-hook controls still fail:
- re-audit `scripts/patchers/cfw_patch_jetsam.py`
- prefer a source-backed or CFG-backed site selection instead of the current backward-scan heuristic
## Concrete Handoff Notes for Claude
### Facts
- `/b` injection is confirmed working on-device
- `launchdhook.dylib` definitely runs
- launchd still asserts at `launchd + 59944`
- Dopamine source confirms `initDaemonHooks()` runs before `initSpawnHooks()`
### Inference
- the early assertion is more likely to be caused by `daemon_hook.m` or `xpc_hook.c` than by `spawn_hook.c`
### Best next change
Implement a **minimal no-daemon-hook build** first:
- edit `Dopamine/BaseBin/launchdhook/src/main.m`
- temporarily disable only `initDaemonHooks();`
- rebuild `launchdhook.dylib`
- keep `/b` alias loading strategy unchanged for the control run
## Related Files
- `scripts/cfw_install_jb.sh`
- `scripts/patchers/cfw_inject_dylib.py`
- `scripts/patchers/cfw_patch_jetsam.py`
- `research/boot_jb_mount_failure_investigation.md`
- `research/boot_hang_b19_mount_dounmount_strategy_compare.md`
- `Dopamine/BaseBin/launchdhook/src/main.m`
- `Dopamine/BaseBin/launchdhook/src/daemon_hook.m`
- `Dopamine/BaseBin/launchdhook/src/spawn_hook.c`
- `Dopamine/BaseBin/launchdhook/src/xpc_hook.c`

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,11 @@ 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 +388,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

@@ -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)
@@ -77,16 +77,17 @@ Offsets and 32-bit patch values, taken from `patch_fw.py`:
| 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 |
| 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

View File

@@ -5,11 +5,11 @@ from .kernel_asm import MOV_X0_0, RET
class KernelPatchSandboxMixin:
def patch_sandbox_hooks(self):
"""Patches 16-25: Stub Sandbox MACF hooks with mov x0,#0; ret.
"""Patches 17-26: Stub Sandbox MACF hooks with mov x0,#0; ret.
Uses mac_policy_ops struct indices from XNU source (xnu-11215+).
"""
self._log("\n[16-25] Sandbox MACF hooks")
self._log("\n[17-26] Sandbox MACF hooks")
ops_table = self._find_sandbox_ops_table_via_conf()
if ops_table is None: