Files
vphone-cli/research/kernel_patch_jb/patch_thread_set_state.md
T
zqxwceandClaude Fable 5 ceccc126c8 tests: Add Frida encoder/version-gate tests
- ARM64Encoder round-trips (TBZ/TBNZ bit retarget, movz flag clear) and the
  cloudOS 26.4 version-gate compare.
- Reveal procedures and static/runtime validation for both kernel patches;
  patch-comparison table rows JB-23b / JB-25c and the opt-in note.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-12 11:40:24 +03:00

4.1 KiB

JB-23b patchThreadSetStateEntitlementFlag

Scope

Opt-in Frida Stalker patch. Emitted only when firmware patching uses --frida (KernelJBPatcher.applyFrida). Baseline JB/EXP firmware is byte-identical when off (26.4 emits 83 kernel-jb records without --frida, 85 with).

Problem

Frida Stalker follows an existing thread by rewriting its core CPU registers via the thread_set_state MIG routine, which lands in thread_set_state_from_user(). That path passes:

// osfmk/kern/thread_act.c
thread_set_state_from_user(...)
    -> thread_set_state_internal(..., TSSF_TRANSLATE_TO_USER | TSSF_CHECK_ENTITLEMENT);  // 0x1 | 0x200 = 0x201

thread_set_state_internal() (with thread_set_state_allowed() inlined) then, on any flags carrying TSSF_CHECK_ENTITLEMENT, requires the caller task to hold com.apple.private.thread-set-state. Frida's target does not, so the kernel raises GUARD_TYPE_MACH_PORT / THREAD_SET_STATE and terminates it.

Approach — clear the flag, don't NOP the check

Instead of NOPing an entitlement-failure branch inside thread_set_state_allowed(), clear TSSF_CHECK_ENTITLEMENT (bit 9, 0x200) in the flags the user setters pass:

mov  w6, #0x201        // before  (TSSF_TRANSLATE_TO_USER | TSSF_CHECK_ENTITLEMENT)
mov  w6, #0x1          // after   (TSSF_TRANSLATE_TO_USER only)

w6 is the 7th argument to thread_set_state_internal (flags) by the AArch64 calling convention. Clearing bit 9:

  • TSSF_TRANSLATE_TO_USER (0x1) is preserved, so user-pointer translation on the from_user path is unchanged.
  • Both entitlement-gated branches in thread_set_state_allowed() (the core-register and fatal-PAC-debug clauses, each flags & TSSF_CHECK_ENTITLEMENT) fall through to "allowed" — the function's first test is tbnz w6, #9, which is now not taken, so a non-mach-exception thread returns allowed immediately.
  • The TH_IN_MACH_EXCEPTION guard (independent of this flag) stays enforced.

This is narrower and more source-faithful than editing the check: it disables the entitlement requirement only for user-initiated thread_set_state, at the exact call sites that request it.

Reveal Procedure

  1. findString("com.apple.private.thread-set-state").
  2. findStringRefs → all ADRP+ADD xrefs; group by findFunctionStart. Require they resolve to a single function — thread_set_state_internal (the entitlement checks are inlined there). Recover [fnStart, fnEnd) via findFuncEnd.
  3. Scan code for direct b/bl whose target lands in [fnStart - 0x10, fnEnd) (the internal function's entry, allowing a small landing-pad lead-in).
  4. For each such call, scan back up to 8 instructions for mov w6, #0x201 (w6 = flags; abandon if w6 is otherwise written first).
  5. Patch each such setter to mov w6, #0x1 via ARM64Encoder.encodeMovzW, Capstone-verifying the re-encode decodes to mov/movz w6, #1.

No file offsets, VAs, register numbers, or preassembled bytes are hardcoded. Kernels without the shape are skipped without changing bytes (fail-open no-op), and the patch only runs under --frida.

Static Validation — 26.4

Kernel: ipsws/c0ecdb4b…/kernelcache.research.vphone600, UUID BCD06230-CCBE-8E48-50FF-D9C166D83CD5.

patch-component --component kernel-jb --target-os 26.4 --frida emits exactly two kernelcache_frida.thread_set_state_entitlement_flag records:

0x01D95720: mov w6, #0x201 -> mov w6, #0x1
0x01D9594C: mov w6, #0x201 -> mov w6, #0x1

(VA 0xfffffe0008d99720 / 0xfffffe0008d9994c — the thread_set_state_from_user setter and the inlined act_set_state_from_user setter, both feeding the same thread_set_state_internal at 0xfffffe0008d5c170.) Without --frida, zero such records are emitted.

Notes

  • The 26.4 research kernel has no tss_should_crash early-out in the compiled thread_set_state_allowed() (it goes straight to tbnz w6, #9), so the DEVELOPMENT boot-arg bypass is not available — a code patch is required.
  • Companion: Frida Stalker's repeated VM_PROT_COPY overwrite also needs the vm_map_delete immutable-code fix — JB-25c (patch_vm_map_delete_immutable_code.md), applied together under --frida.