Skip to content

Phase D.6.3 findings — H1b FALSIFIED; H2-reply is the surviving hypothesis

Status: verified — 2026-04-24

Single decisive experiment: ran the same UUID-patched Go backend and devicectl manage pair trigger as iter-8, but this time with tcpdump -i lo0 "tcp and not port 49151" — wide filter capturing all loopback TCP including any back-connect CDS might attempt. Result: only one TCP connection observed the entire session, from CDS to our backend on [::1]:34719. Zero SYNs to any of the 62 advertised service ports. H1b (CDS back-connects to service port, gets ECONNREFUSED) is decisively falsified. H2-reply (we're missing a server- initiated follow-up frame) is the surviving hypothesis.

TL;DR

CDS receives our #8 big Handshake, keeps the TCP connection open for ~3 seconds of total silence, then cleanly FINs. During that window it makes zero attempts to connect anywhere else — not to any port in our advertised Services dict, not to any other loopback address. The 42-packet pcap is pure back-and-forth between [::1]:49492 (CDS) and [::1]:34719 (our backend), then FINs.

Implication: CDS is waiting for us to send the next frame, not for any back-connect to succeed. The missing variable lives inside the HTTP/2 stream, not in parallel service connections.

The capture

Total: 42 packets, 17,733 bytes, active traffic spanning 2.995 s (despite 120 s tcpdump runtime — all silence after 3 s).

Unique SYN destinations (the entire scientific output of D.6.3):

src dst SYN count
[::1]:49492 [::1]:34719 1 (only outbound SYN)
[::1]:34719 [::1]:49492 1 SYN-ACK (response)

CDS's one and only outbound TCP connection went to our backend. The rest of the 42 packets are data frames (HTTP/2 preface, SETTINGS, WINDOW_UPDATE, HEADERS, DATA ×6 including the 14 124 B big Handshake), acknowledgements, and two FINs.

Backend log — same 40-line baseline

Session 1 captured, 41 lines including startup. Identical structure to iter-7 and iter-8: - HTTP/2 preface + SETTINGS + WINDOW_UPDATE exchange - #3 HEADERS(s1) → client DATA(s1, 44 empty) → #4 DATA(s1, 44 mirror) - #6 HEADERS(s3) → client DATA(s1, 24 sync) → #5 DATA(s1, 24 mirror) - client DATA(s3, 24 INIT_HANDSHAKE) → #7 DATA(s3, mirror) - #8 DATA(s1, 14124 B big Handshake) with patched UUID d1913a25-be84-4ac7-9d3f-597a9070fcfe - EOF from peer (CDS) after ~3 seconds of silence

Why iter-8 saw 4 sessions and iter-9 saw 1

iter-8: devicectl manage pair followed by unpair+pair fallback — four client invocations, each opening a fresh CDS-to-backend connection. iter-9: single devicectl manage pair only (no fallback), one connection. Both iterations have the same per-session handshake sequence. Session count doesn't affect the scientific finding — the SYN-destination list is about what CDS does on ANY session, not aggregate.

Why 3 s EOF here vs 119 s EOF in iter-8 session 3

iter-8 session 3 held 119 s because the subsequent unpair devicectl call was what tore down CDS's ESTABLISHED. In iter-9 with no such cascade, CDS application-layer logic self-aborts at ~3 s after receiving our Handshake. The ~3 s is CDS's internal "waited for response" deadline post-handshake.

What H1b being falsified means

We can confidently stop thinking about: - "Do we need to bind any service port on our backend?" — no, CDS doesn't try to reach service ports pre-pair - "Is the Services dict content wrong?" — probably not semantically (CDS accepts it), or at minimum not wrong in a way that triggers back-connects - "Is our UniqueDeviceID redacted field causing an identity cross-validation failure that CDS responds to with a back- connect?" — no, or it would have produced a SYN

What H2-reply surviving means

CDS is waiting, within the same HTTP/2 stream, for the iPhone to emit something else. The iter-01 replay corpus ends at #8/#9 because the Q2 pcap was captured during a one-shot pymobiledevice3 remote rsd-info round-trip — the iPhone did its server-side reply in one burst and that was enough for the client (pymobiledevice3) to finish. Real CDS pair-flow is different — it expects more frames.

D.6.4 plan

Options ranked by operational risk × scientific yield:

D.6.4a (recommended): passive reference capture of real iPhone post-handshake

On havoc, temporarily switch tunneld from SPIKE mode back to stock (real-iPhone-tunnel) mode, tcpdump on utun4 — same methodology as Q2 — but this time with a longer capture window (60-120 s). Run the non-state-changing pymobiledevice3 remote rsd-info or equivalent that triggers normal handshake exchange, and observe what the iPhone emits AFTER its #8 big Handshake.

Risk: low. rsd-info is read-only; doesn't touch pair state. Only operational change is a tunneld mode swap, which the existing iosmux-restore.sh workflow already supports.

Expected yield: authoritative reference bytes for the missing follow-up frame(s). Compare against our 14 326 B replay corpus to identify what frames come after #9.

D.6.4b (higher-risk): real iPhone pair reference

Same as above but with devicectl manage pair on the stock tunneld endpoint. Risk: pair state could get confused if CDS and iPhone disagree about who's paired with whom. High scientific yield if we haven't already captured the missing frames with D.6.4a.

Recommendation: do D.6.4a first, evaluate yield, only go to D.6.4b if necessary.

D.6.4c (code-only sandbox): server-initiated heartbeat

Modify the Go dispatcher to emit a PING frame on stream 0 every N seconds after #8. No reference needed. Cheapest to try but lowest prior probability — the 3 s EOF pattern from iter-9 suggests CDS isn't timing out on keepalive semantics.

Artifacts

Under iter-09-wide-pcap/:

  • verbose-session.log — 3,150 B, 41 lines, single session
  • iosmux-d9-wide.pcap — 18,429 B, 42 packets, unfiltered loopback (minus tunneld's HTTP API). Pre-commit UDID scan: 0 matches across all patterns. Safe to commit.

Status

  • Step A (decisive capture): delivered.
  • Step B (document H1b falsification, rank next): this file.

iter-9 closes H1b. D.6.4a (passive utun4 reference capture in stock tunneld mode) is the next research step — awaits explicit user approval because it requires a tunneld mode swap.