Phase D.6.5 — iPhone:50367 decoded: classic lockdown-over-TCP, advertised, 40/62 services share it¶
Status: verified — completed 2026-04-24
Pure local analysis on the iter-10 held-local pcap. Extracted
both directions of the iPhone:50367 parallel service, applied
five decoding lenses, and decoded the full Services dict from
the greeting Handshake. Protocol on 50367 is classic
lockdown-over-TCP (plaintext XML plist, 4-byte BE length
prefix). Port 50367 is advertised in our Handshake
Services dict under com.apple.mobile.lockdown.remote.trusted
(UsesRemoteXPC=False, ServiceVersion=1). 40 of 62 services
share this wire format — one handler in our Go backend
unlocks 40 endpoints. Full writeup: findings.md.
D.6.6 implementation plan: add lockdown-over-TCP listener to
the Go backend bound to port 50367, handle 3 verbs
(RSDCheckin, QueryType, GetValue), serve cached
device-info plist. ~1 day effort.
The answer to the D.6.x mystery¶
D.6.0-B showed CDS times out ~30s after our Handshake → UUID gate (D.6.1). D.6.1 fixed the UUID → CDS silent instead of timing out. D.6.2/D.6.3/D.6.4a ruled out trigger-type, back-connect hunger, and missing greeting-stream frames as explanations. D.6.5 finds what was actually missing: a parallel lockdown-over-TCP listener at the advertised service port.
When a host-side tool (pymobiledevice3 / CDS / devicectl) wants device state info (pair state, tunnel state, identity beyond what's already in the Handshake Properties), it:
- Reads the Services dict from our Handshake (includes port 50367)
- Opens a fresh TCP connection to that port
- Speaks 4-byte-BE-length-prefixed XML plist
- Expects a 3-verb protocol:
RSDCheckin,QueryType,GetValue
SPIKE mode hasn't been hosting this service. That's why CDS reports "Mercury 1000 connection interrupted" — the greeting works, but the follow-up service isn't there.
The Services dict census¶
Decoded from the 14,124-byte Handshake DATA frame:
| UsesRemoteXPC | Count | Wire format |
|---|---|---|
| True | 22 | HTTP/2 + XpcWrapper (RemoteXPC) |
| False | 40 | Classic lockdown-over-TCP (what 50367 speaks) |
A single handler implementation in the Go backend covers 40
out of 62 services, including both .remote.trusted (port
50367 in our reference capture) and .remote.untrusted (port
50333) variants of lockdown itself.
The 3-verb protocol¶
Captured on iPhone:50367 during pymobiledevice3's lockdown info trigger:
client → iPhone (3 plists, 924 B total):
{Label: "pymobiledevice3", Request: "RSDCheckin", ProtocolVersion: "2"}
{Label: "pymobiledevice3", Request: "QueryType"}
{Label: "pymobiledevice3", Request: "GetValue"}
iPhone → client (4 plists, 9,532 B total):
{Request: "RSDCheckin"} ← bare ack
{Request: "StartService"} ← unsolicited server push
{Request: "QueryType", Type: "com.apple.mobile.lockdown"}
{Request: "GetValue", Value: <88-key device dict>}
All 7 plists are plaintext XML, 4-byte BE length prefix. No TLS, no binary plist, no XpcWrapper. Trivial to implement.
Files¶
findings.md— full decode, flow tables, Services census, D.6.6 implementation sketch, open questions- this
index.md
Raw decoded artifacts and scripts at /tmp/iosmux-d11-* —
local-only, contain real device identifiers from the 88-key
GetValue dict, never committed. Reproducible from the
findings.md "How to reproduce" section.
Next step¶
D.6.6 — implement com.apple.mobile.lockdown.remote.trusted
service handler in cmd/iosmux-backend/ using Go stdlib XML
parsing + howett.net/plist. Source the 88-key device dict
either from a tunneld-mediated snapshot at backend startup
(correct path) or from synthesized constants + tunneld-
advertised UDID (faster path, may fail CDS's field
validation). Recommend the snapshot approach for MVP; falls
back to synthesis if snapshot proves fiddly.
After D.6.6: re-run iter-8/9-style SPIKE triggers against the new backend, observe whether CDS now progresses past the handshake into actual service traffic on stream ⅓ of the greeting session, OR into additional RemoteXPC flows on other advertised ports.