Skip to content

docs(evolution): proposal 0007 — inject into iOS Simulator processes - #105

Closed
Mx-Iris wants to merge 1 commit into
mainfrom
docs/evolution-0007-ios-simulator-injection
Closed

docs(evolution): proposal 0007 — inject into iOS Simulator processes#105
Mx-Iris wants to merge 1 commit into
mainfrom
docs/evolution-0007-ios-simulator-injection

Conversation

@Mx-Iris

@Mx-Iris Mx-Iris commented Aug 18, 2026

Copy link
Copy Markdown
Member

Adds evolution proposal 0007. Documentation only — no code changes.

Why

Injecting into a simulator process crashes the target. MIMachInjector.m
resolves pthread_create_from_mach_thread, dlopen and
sandbox_extension_consume through dlsym(RTLD_DEFAULT, ...) — that is, against
the injector's address space — and then hands those absolute addresses to the
target. The assumption holds only while both processes share a dyld shared
cache. A simulator process runs its own, mapped at the same 0x180000000 base
but laid out differently, so the address resolves to unrelated code.

Register-level evidence from three SpringBoard crash reports: lr is exactly
host pthread_create_from_mach_thread + 4 in all three, and x0 = sp+8,
x1 = 0, x2 = page-aligned + 0x50 match what loader_arm64.s sets up for that
call. The task_for_pid(...): (os/kern) failure seen in the UI is second-order —
by then the target was already dead.

What was verified

An iphonesimulator payload dlopen'd into gamecontrollerd (a daemon, not
SpringBoard):

  • dyld accepts it — dlerror() is NULL, four segments mapped
  • the Swift runtime starts — all three os_log markers present
  • it advertises over Bonjour, and the host connects back (port 52406, confirmed
    from both sides)
  • the target survives

Bonjour needs no new transport: the payload picks that branch at compile time on
non-macOS platforms, and that path already treats simulators as a first-class
case (rv-sim TXT key).

Blocker found during verification

The iOS Bonjour identity is device-level — both the service name and the
identifier derive from the device, a leftover from "one app per device". The
host's connectToBonjourEndpoint de-duplicates on the service name, so with
several injected processes only the first ever connects; the rest sit in
pendingReconnectEndpoints (also keyed by name, so they overwrite each other).
Measured: gamecontrollerd ESTABLISHED, nanoappregistryd stuck at LISTEN.

Resolved by giving devices a Section and processes the entries beneath it,
reusing the grouping rebuildSections() already does for Mac. No new display
structure.

Status

Accepted. Step 1 (feasibility) is done; the proposal records what remains,
starting with the identity rework, which is confined to this repo and can be
verified on its own.

Injecting into a simulator process currently crashes the target. The injector
resolves `pthread_create_from_mach_thread`, `dlopen` and
`sandbox_extension_consume` against its own address space via
`dlsym(RTLD_DEFAULT, ...)`, then hands those absolute addresses to the target.
That holds only while injector and target share a dyld shared cache. A
simulator process runs its own cache — mapped at the same 0x180000000 base but
laid out differently — so the address lands on unrelated code.

Three SpringBoard crashes pin this down at register level: `lr` sits exactly 4
bytes past the host's `pthread_create_from_mach_thread`, and `x0`/`x1`/`x2`
match what `loader_arm64.s` sets up for that call. The `task_for_pid` failure
the user saw is a second-order effect — the target was already dead.

Feasibility is verified: an `iphonesimulator` payload dlopen'd into
`gamecontrollerd` loads, starts its Swift runtime, advertises over Bonjour and
gets a connection back from the host. Bonjour needs no changes — the payload
already takes that branch at compile time on non-macOS platforms.

One blocker surfaced during verification: the iOS Bonjour identity is
device-level (service name and identifier both derive from the device), a
leftover from "one app per device". With multiple injected processes the host
skips every endpoint after the first as a duplicate, so only one ever connects.
Resolved by giving devices a Section and processes the entries under it,
reusing the grouping `rebuildSections()` already performs for Mac.

Status: Accepted.
Copilot AI lite review requested due to automatic review settings August 18, 2026 09:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Mx-Iris

Mx-Iris commented Aug 18, 2026

Copy link
Copy Markdown
Member Author

Superseded by #106: the proposal number collided (0007 is already taken on two unmerged branches) and the branch is renamed to feature/.

@Mx-Iris Mx-Iris closed this Aug 18, 2026
@Mx-Iris
Mx-Iris deleted the docs/evolution-0007-ios-simulator-injection branch August 18, 2026 10:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants