Files
2026-10-14 06:59:51 +02:00

72 lines
3.9 KiB
Markdown

# Follow-up request: the RecRoomClientEncryptLayer 9-byte format
Your Tachyon findings resolved the handshake failure. Reconciled against the server side:
- The client's inbound layer strips 9 bytes off **every** datagram before LiteNetLib parses it.
- Our stock server sent a 15-byte ConnectAccept; after the client strips 9 it is 6 bytes, so
`NetConnectAcceptPacket.FromData` fails `Size != 15` and returns null — the exact silent
ignore-and-retry-to-timeout we observed.
- Confirmed on the server: a stock server absorbs the client's over-long ConnectRequest (the 9
extra bytes fold into variable-length connect data), which is why the server logged
"peer connected" while the client never did.
So the server needs the matching layer. We have built it as a configurable
`PacketLayerBase` with `ExtraPacketSizeForLayer = 9`, and proved the plumbing: with the same
9-byte framing on both ends the handshake completes; with it on the server only, a stock
client fails identically to the game today.
**The one thing blocking a live test is the exact 9-byte format and the null-key behaviour.**
This is §3 / step 1 of your own task doc, and it is now the whole critical path.
## Q1 — `RecRoomClientEncryptLayer.ProcessOutBoundPacket` (RVA `0x735D760`)
Full disassembly / decompile. Specifically:
- Are the 9 bytes **prepended** (written at the front, payload shifted) or **appended**
(written after the existing payload)? The offset math in the method answers this.
- What are the 9 bytes when the session key field (`DAKIIEIEKNI`, `NetPacket`+`0x18`) is
**null** — the state during the whole LiteNetLib + NGO handshake, before
`RecRoom_SetEncryptionInfo` runs? Zero fill? A counter? A magic constant? Copied from
somewhere in the packet?
- When the key is **non-null**, what transforms the payload — AES over the body, and the 9
bytes carry a nonce/tag? We need the outbound path, but the null-key branch is what unblocks
the handshake.
- Does it branch on packet property (e.g. treat ConnectRequest/Accept differently from
Channeled data), or is it uniform for every datagram?
## Q2 — `RecRoomClientEncryptLayer.ProcessInboundPacket` (RVA `0x735DF90`)
Full disassembly / decompile. Specifically:
- Does it **validate** the 9 bytes (compare a MAC/magic/counter and drop on mismatch), or just
**strip** them? This is the single most important question: if it only strips when the key is
null, a zero-fill server reply connects immediately; if it validates, we must reproduce the
exact bytes.
- Same prepend-vs-append question from the read side (which 9 bytes are removed, front or back).
- The null-key branch specifically — what happens to a datagram that arrives before a key is set
(which is every handshake packet). Does it early-out, strip-only, or reject?
- Any length guard (e.g. drop if `size < 9`).
## Q3 — the two unidentified layer methods
`0x735DC20` and `0x735E140` are listed as unidentified members of the layer. One-line summary
of each — likely a key setter (the sink for `RecRoom_SetEncryptionInfo`) and/or a
nonce/counter helper. If one installs the key into `DAKIIEIEKNI`, note its signature.
## Q4 — the `.cctor` at `0x735E220`
If the layer has static state (a fixed magic, a base nonce, an RNG seed), it is initialised
here. Dump any constants it writes.
## Why this precise scoping
We are one bounded read from a live handshake. If Q2's null-key path is strip-only, the layer
we have built connects the real client as-is and we move straight to the NGO ConnectionRequest
(where your Tachyon JSON / `"Carrot"` findings take over). If it validates, we need the byte
recipe before any wire test is meaningful — a guess here is the "plausible-but-wrong costs more
than not-found" trap you flagged.
Report bytes and offsets, not prose. Where the null-key path is genuinely absent (i.e. the
layer assumes a key is always set), say so — that itself would be a finding, implying the key
is established out-of-band before the first datagram.