Cryptex Support#344
Conversation
due to setup incompatibilities of idevicerestore and pymobiledevice in regard to REM and Cryptex support.
by allowing all Mach-O executables in both REM phases. (cherry picked from commit 34e820a5baab19e1f149de10da71db2ccf462350)
(cherry picked from commit af4e207129b310d294c356f01e11d5a8167292ac)
|
I just noticed that a MobileDevice restored vphone in Patchless mode panics from time to time. The reason is currently unknown. Logs
|
|
any news on debugging the crash? I am not sure it is smart to merge this without knowing more about why it has a statistical failure. |
There was a problem hiding this comment.
Fable review of the PR:
🔴 Blocking
1. Every cryptex operation reports failure to the host — even on success
vp_handle_cryptex_command (scripts/vphoned/vphoned_cryptex.m) builds its response once as vp_make_response(@"err", reqId) and never resets t on the success paths — it only sets r[@"ok"]. But the host read loop decides success/failure purely from t and ignores ok:
// sources/vphone-cli/VPhoneControl.swift ~823
if type == "err" {
let detail = msg["msg"] as? String ?? "unknown error"
DispatchQueue.main.async { pending.handler(.failure(ControlError.guestError(detail))) }
continue
}So a successful cryptex_install / cryptex_list comes back as t:"err" → thrown as guestError(<cryptexctl stdout>) → surfaced to the user as a failure alert, every time. Every other handler does this correctly, e.g. vphoned_apps.m uses vp_make_response(@"app_launch", …) + r[@"ok"] = @YES on success and reserves @"err" for failures.
Fix: set r[@"t"] to a success type (cryptex_install / cryptex_list) on the happy paths (or build the dict with the success type up front and switch to @"err" only on failure).
2. fw_manifest.py narrows Restore.plist SupportedProductTypes for all variants
scripts/fw_manifest.py changes Restore.plist's SupportedProductTypes from the union of the iPhone + cloudOS lists to the hardcoded ["iPhone99,11", "ComputeModule14,2"]. Against the extracted IPSW that drops iPhone17,3, ComputeModule14,1, ComputeModule17,2, Mac14,14. This script runs at prepare time for every variant, including the existing pymobiledevice3 (non-patchless) restore path — a shared change for a patchless-only feature. It also now disagrees with sources/FirmwarePatcher/Manifest/FirmwareManifest.swift (still emits the union).
iPhone99,11 (the identity the VM presents) is retained, so it probably still passes, but this is an unverified regression to the shared restore path. The BuildManifest addition of ComputeModule14,2 looks genuinely needed for the MobileDevice restore; the Restore.plist narrowing looks like collateral.
Fix: keep Restore.plist's SupportedProductTypes as the union (add ComputeModule14,2 to it if required) rather than replacing it, and confirm a regular/jb restore still completes.
🟠 Restore path (patchless-only, but worth fixing)
All in VPhoneRestoreCLI / the callbacks in sources/vphone-cli/VPhoneCLI.swift:
- 5-minute hang on any pre-restore failure. In
newConnectionCallback, the "Failed to enter recovery", "Not in DFU mode", and "Disappeared" paths set.failedbut never callcompletion.signal()— onlyrestoreProgressCallbacksignals. Sorun()blocks the full 300s and then reports the generic"timeout"instead of the real cause. Signal on every terminal path. BootedOS → recoverytransition is dead-wrong. AfterAMDeviceEnterRecovery, the DFU guard re-checks the staledeviceState(stillBootedOS) and always falls through to "Not in DFU mode". Re-read the state after entering recovery, or return and let rediscovery drive the restore.- Real error reason is discarded. The failure message reads
restoreContext.status.errReason— a stale struct copy synced only by adeferthat runs after thethrowis evaluated — so failures always print"failed: No reason"even thoughrestoreProgressCallbackcaptured the realCFError. Read fromcontextWrapper.contextand drop therestoreContext+deferdance. - Force-casts can trap the process.
progress as! Int32anderror as! CFError(CF→NSNumber/NSError bridging), plusAMRestorableDeviceCopyDefaultRestoreOptions() as!andtry! restoreVariantName(...). Any unexpected private-API payloadSIGABRTs mid-restore. Preferas?with graceful defaults. - Unsynchronized cross-thread access. MobileDevice callbacks fire on a background thread and mutate
RestoreContext.status; the main thread reads it. Only the terminal write is ordered by signal/wait; the.notstarted/.startedtransitions race with nothing. Guardstatuswith a lock (also a Swift 6 concurrency soundness gap). --configis a required option but is never used inrun(), yetrestore_mobiledevicepasses--config ./config.plist. Wire it in or drop it. The--ipswhelp text also looks copy-pasted from--variant.
🟠 Guest daemon / shell robustness
extract_cryptexctlcan abortfw_prepare.scripts/fw_prepare.shdoescp … "$tools_prefix/cryptexctl"withoutmkdir -p "$tools_prefix";.tools/is gitignored, so on a tree that hasn't runmake setup_tools,set -euo pipefailaborts prep. Alsodisk=$(ls -AU $disk_dir | head -1)is unquoted/unchecked (empty → cryptichdiutilerror), and a failedcpleaks the attached image (thehdiutil detachnever runs).- Nil-argument crashes in the guest.
ExtractCryptexdoes[@"Extraction issue" stringByAppendingString:archiveError]; ifarchiveErroris nil (severalunarchive.mearly returns don't set it), that throwsNSInvalidArgumentException, and thevphoned.mdispatch loop has no@try/@catch, so the whole client connection is torn down. Likewise a missingvariant/pathputs aNULLmid-argv, silently truncating thecryptexctlcommand line. Processdeadlock on large output.VPhoneCryptex.swift'srunProcesscallswaitUntilExit()before draining the pipe;cryptexctl create -v/hdiutilcan exceed the ~64 KB pipe buffer and hang forever. Drain the pipe before waiting (or use a background reader).
🟡 Minor / polish
PersonalizeCryptexshadowsok/contents/name, ignores thecreateDirectoryresult, and returns on the first entry (fine if "one cryptex only" is intended — a comment would help).addCryptexctlprints and silently returns if.tools/cryptexctlis missing → the firmware ships withoutcryptexctland the guest feature is quietly disabled. Consider making this a hard error.exit(42)inside the reusablerunProcessbypassesdefercleanup (leaks temp dirs/mounts) and can't be surfaced by the GUI install path.CryptexError.ProcessErrordoesn't name the failing command (one helper runs ~20 subprocesses).setUpdatedComponentsInRestorePlistreplacesSystemRestoreImageFileSystemswholesale with no before/after log.- Typo
"No reponse."(VPhoneControl.swift); run-on error strings ("Extraction issue"). - Dropping
--base-trust-cachein the patchless trustcache: worth a comment on why it's safe (merged mount is a superset) and a note inresearch/, per the repo's docs convention.
This PR adds functionality to install and list Cryptexes via
vphoned.Cryptexes are overlay file systems that can contain data, executables, and daemons, which can be added at runtime within the boundaries of Apple's security ecosystem.
Cryptexes are also being used in the context of Apple's physical Security Research Devices. Therefore, this feature allows security researchers to easily use their Cryptex toolchains also for researching with the vphone.
While developing the cryptex support for the vphone, we hit a limitation of pymobiledevice3. We opened doronz88/pymobiledevice3#1701 and hope to remove f05a53a once this is resolved. Meanwhile, we perform the restore with Apple's MobileDevice library (f05a53a). We have limited this to the patchless mode, for now.
In this PR, we use and copy the
cryptexctlexecutable from the cloudOS filesystem as a utility. A future PR, however, might use the (private Swift)CryptexKitframework directly fromvphoned.