Plan: CDS Inject with Real RSD Transport¶
Date: 2026-04-13 Status: IMPLEMENTED. Device connected in Xcode. Action interception is the open work.
Summary¶
CoreDeviceService is injected via LC_LOAD_DYLIB (iosmux_inject.dylib). The dylib
constructs a real xpc_remote_connection to iPhone via the pymobiledevice3 tunnel,
parses the RSD Handshake (46 device properties + 74 services), creates the SDR and
RSDDeviceWrapper, sets DeviceInfo fields, and registers the device with CDS. Result:
device shows in devicectl and Xcode as state=connected.
Architecture¶
0. iosmux_install_md_proxy() — MobileDevice property proxy
1. iosmux_rsd_init() — TCP connect + create xpc_remote_connection
+ activate + wait for Handshake (5s timeout)
+ parse Services dict into g_services[74]
2. create_remote_device(conn) — OS_remote_device alloc, set ivars,
attach xpc_remote_connection at offset 80
3. Resolve Swift symbols — DeviceInfo.init, SDR init, install(browser:),
RSDDeviceWrapper.__allocating_init, etc.
4. Build DeviceInfo — set name, productType, hwModel, marketingName,
state=connected(3), pairingState=paired(2),
visibilityClass=default,
areDeveloperDiskImageServicesAvailable=true,
preparednessState=.all (0xF)
5. Build DeviceIdentifier — 33-byte enum, UUID variant tag=0
6. SDR.__allocating_init — passes empty capabilityImplementations dict
7. Find ServiceDeviceManager via heap scan
8. handleDiscoveredSDR(sdr) — publishes device added event
8b. updateIdentifier() — registers SDR in managedServiceDevices
8c. (DYLD_INTERPOSE for remote_device_* — already active at this point)
9. connected_callback fire — async polling
9b. RSDDeviceWrapper.__allocating_init — completes in ~20ms, stores wrapper in
g_hook_wrapper (Step 9b directly, not via
sdr+104 read overflow)
10. Verify SDR in managedServiceDevices
11. Hook serviceDeviceRepresentations(forDeviceIdentifiedBy:) — returns our SDR
12. Hook CDS+0xCCB0 — uses g_hook_wrapper
13-14. (REMOVED — CoreDevice+0x30BEE0 / +0x30C33C shared cache patches)
15. Hook CDS+0x5E2D0 — callq patch returning wrapper for our UUID
17. (REMOVED — CoreDeviceUtilities DDI throw inline patch)
19. Hook CDS+0xB896 — currently `mov al, 1; ret`, broken (see Open Issues)
DYLD_INTERPOSE table (top of iosmux_inject.m)¶
| Function | Replacement | Purpose |
|---|---|---|
| remote_device_copy_service_names | g_interpose_service_names array | Prevent blocking XPC query |
| remote_device_copy_service | return NULL | Prevent blocking XPC query |
| remote_device_copy_property | return NULL | Prevent blocking XPC query |
| remote_device_heartbeat | callback(true) immediately | Prevent blocking XPC heartbeat |
| remote_service_create_connected_socket | TCP to tunnel + service port | Service connection |
| remote_service_connect_socket | TCP to tunnel + service port | Service connection |
| xpc_remote_connection_create_with_remote_service | create from connected fd | RemoteXPC service connection |
Linked with -F/System/Library/PrivateFrameworks -framework RemoteServiceDiscovery -framework RemoteXPC
so the DYLD_INTERPOSE table has valid original pointers (otherwise dyld aborts at load).
Function Signature (proven)¶
xpc_remote_connection_t xpc_remote_connection_create_with_connected_fd(
int fd, // TCP socket to RSD endpoint via tunnel
dispatch_queue_t queue, // target queue (concurrent — serial deadlocks)
uint64_t version_flags, // 0x100000000000006 (from RSD RemoteXPCVersionFlags)
uint64_t mode_flags // 0
);
// After activate(): receives Handshake event with MessageType="Handshake",
// MessagingProtocolVersion=7, Properties={46 device keys}, Services={74 entries}
Build and Deploy¶
ssh havoc "cd ~/iosmux/inject && make clean && make"
ssh havoc-root "cp /Users/nullweft/iosmux/inject/iosmux_inject.dylib /Library/Developer/CoreDevice/iosmux_inject.dylib"
ssh havoc-root "killall CoreDeviceService"
# Wait 3s for inject + RSD init, then check log:
ssh havoc "tail -50 /tmp/iosmux_inject.log"
Verified results¶
| Item | Status |
|---|---|
| RSD Handshake (46 properties + 74 services) | OK |
| Real iPhone properties (iPhone14,6, iOS 26.4, SerialNumber, ...) | OK |
devicectl list devices shows state=connected |
OK |
| Device visible in Xcode Devices & Simulators | OK |
| RSDDeviceWrapper created (init returns ~20ms) | OK |
| pairingState=paired, areDDIServicesAvailable=true, preparednessState=.all | OK |
| DYLD_INTERPOSE active for all 7 hooks | OK |
| No shared cache __TEXT modifications | OK |
Open issues¶
CDS+0xB896 hook is broken¶
mov al, 1; ret returns true unconditionally for invoke(anyOf:usingContentsOf:).
This short-circuits the Swift async continuation chain. Mercury never gets the
result, never sends an XPC reply, Xcode times out with "error communicating with
remote process". Click Pair → CDS dies (action handler crashes anyway because
g_hook_wrapper was a garbage pointer from a separate read overflow bug, see below).
Correct fix per E+F research: convert CDS+0xB896 from mov al, 1; ret to a
passthrough trampoline (movabs r10, <orig_invoke>; jmp r10), and intercept actions
at a higher level instead.
Wrapper read overflow at sdr+104¶
Step 9b created wrapper as a __block void * local variable in if (rsd_wrapper_init && rsd_wrapper_meta).
Step 12 then read g_hook_wrapper = *(void **)((uint8_t *)sdr + 104) — reading 8 bytes
16 bytes past the end of an 88-byte SDR object. The result was a garbage pointer
from the adjacent heap object, returned to CDS by CDS+0xCCB0 and CDS+0x5E2D0 hooks.
Fixed in commit 59ca5cd: g_hook_wrapper = wrapper directly in Step 9b, removed the
sdr+104 read in Step 12. Untested at the time of writing — may eliminate Pair crashes
on its own.
Action interception strategy¶
Previous attempts assumed Xcode↔CDS uses the flat CoreDevice.featureIdentifier /
CoreDevice.output dict format that pymobiledevice3 uses against the iPhone.
This was wrong. Xcode↔CDS uses Mercury.XPCDictionary with Swift Codable + a
mangledTypeName envelope. Filtering by string keys cannot work on that path.
See docs/research/action-interception-full-picture.md for the synthesis of
three parallel research sessions on hook ABI, connection identification, and the
actual Mercury wire format.