P2.0 — can libirecovery see the VM's virtual DFU endpoint?
2026-09-23, branch
vphone-intg-update. Answer: yes.Plan section P2.0 marks this the largest unknown in P2 and says to stop and re-evaluate if it fails, rather than vendoring idevicerestore on an assumption.
Since answered and acted on. The migration this spike unblocked is done: the restore path is
Sources/VPhoneRestoreover vendored libirecovery and idevicerestore, andscripts/pymobiledevice3_bridge.pyno longer exists. Everything below is written as of the day of the spike, when it still did.Research/Restore/native_restore_architecture.mdhas what P2 went on to do.
Why it was a real question
The restore path at the time went through pymobiledevice3's IRecv, which is
pyusb. libirecovery on macOS goes through IOKit USB. Different transport,
so whether one works tells you nothing about the other, and the whole of
P2.1/P2.2 is wasted work if the IOKit path cannot see a Virtualization.framework
virtual endpoint.
Result
VM booted with --dfu from a bare bundle — vphone-cli vm new output only, no
restore, no firmware, no CFW:
irecv_open_with_ecid_and_attempts succeeds and irecv_get_device_info returns a
fully populated struct. The ECID matches exactly what vphone-vm derived from
machineIdentifier (VPhoneVirtualMachine.swift:296-309), which also confirms that
derivation independently — two different code paths agreeing on
206C763772858301.
AP and SEP nonces are present and correctly sized, so personalisation has what it needs.
What this also established
Getting here required the entitlement split to work end to end under a hostile amfid, so it settled two queue items at once:
vphone-vmcan start a VM holding the private entitlements alone. This was the item everything in step 1 rested on, and it was unproven until now. This one is about the entitlements, not the bypass, and it still stands.- A bypass that covers only the exec is enough: the VM was running and healthy
after the window closed, and
--detachleft the guest running after the patch was removed (detached; pid 33232 keeps running).
Also proven in passing: with amfid refusing, vphone-cli --help still runs and
prints the full explanatory error rather than a bare Killed: 9. That is the
entire point of moving the entitlements off the entry point.
The bypass used for this run is gone. The two window measurements above were taken with
vphone-letmein exec --hold 10 [--detach], which opened its window by writing amfid's__TEXT. That is fatal on a host wherevm.cs_system_enforcementreads 1 — the kernel kills amfid for the dirty page — so the tool was removed the same day. The spike's own findings do not depend on it: what was shown is that a bypass lasting one exec suffices, not that any particular tool provides it.Research/Host/host_binary_split.mdhas the measurement. The replacement isamfidont, below.
Reproducing it
The spike is ~60 lines of Swift against the system libirecovery. Note the link name
is -lirecovery-1.0, not -lirecovery — Homebrew ships
libirecovery-1.0.dylib with no unversioned symlink. That detail carries into P2.1.
Boot the VM first, leaving it running. On a host with AMFI relaxed at boot, that is just:
Where amfid still refuses it, allow that one binary first — in another terminal, and leave it running:
With no VM up, the probe exits 1 with Unable to connect to device (-3) — so a
negative result is distinguishable from a broken harness.
Still open, and deliberately not answered here
Plan section P2.2 asks that FDR equivalence be confirmed as part of this spike.
It was not, and cannot be from device enumeration alone: pymobiledevice3 ran
Restore(..., ignore_fdr=False), and idevicerestore's FDR handling is internal.
Whether the two behave the same only shows up during an actual restore. That stays
an open risk on P2.2 and must not be assumed away — it is called out here so the
next person does not read "P2.0 passed" as covering it. It is still open;
Research/Restore/native_restore_architecture.md carries it forward.