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 sessioniosmux-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.