72 lines
3.9 KiB
Markdown
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.
|