← Technical referencesView Markdown ↗

Transport, authentication, and delivery

This is a byte-level reference for the recovered PackClientLauncher.exe build identified in architecture. It documents the reconstructed wire contract. All multi-byte fields are little-endian unless explicitly marked otherwise.

Outer framing

Wire offset Size Field Rule
0x00 4 frame_word 0x5A400000 bitwise OR body_length
0x04 4 type First DWORD of body
0x08 variable Payload body_length - 4 bytes

Receive requires (frame_word >> 22) == 0x169, a nonzero low-22-bit body length no larger than 0x3FFFFF, the complete declared body, and at least four body bytes before reading the type. Send produces type 0x15; its application payload ceiling is 0x3FFFFB. Exact I/O loops fail when Winsock send or recv returns zero or a negative value.

The bytes 24 00 40 5A are only a length word declaring a 36-byte body, hence a 40-byte total frame. They are not a complete message. TCP packet boundaries do not define protocol objects.

The shared syntactic validator accepts decimal 1–3 and 10–23. Its two receive callers apply a stricter semantic gate: direct type 0x15, or outer type 0x16 decrypting to inner type 0x15. Other syntactically accepted values have no constructed or dispatched meaning in B. Type 0x16 is inbound-only in this build.

Relevant B RVAs: exact receive 0x54D1; framed receive 0x550A; exact send 0x56BC; framed send 0x56F5; generic type validator 0x3039.

Handshake

The reconstructed order is client PLH1, server PLC1, then client PLA1. B sends PLH1 and PLA1 inside outer type 0x15. It accepts PLC1 directly inside type 0x15, or inside an authenticated outer 0x16 envelope whose decrypted inner type is 0x15. That first receive occurs at 0x34F6, before Core loading; the encrypted branch requires initialized envelope-key state. B checks no separate post-PLA1 acknowledgement in handshake function 0x344A–0x37AF.

PLH1: client hello, 32 bytes

Offset Size Value/source
0x00 4 ASCII PLH1
0x04 2 Version 1
0x06 2 0x20; semantic name unresolved
0x08 4 Zero; semantic name unresolved
0x0C 4 One; semantic name unresolved
0x10 8 GetTickCount64()
0x18 4 GetCurrentProcessId()
0x1C 4 Zero

PLC1: server challenge, 24 bytes

Offset Size Value/use
0x00 4 ASCII PLC1, checked
0x04 2 Version 1, checked
0x06 2 Unknown; received but not validated or authenticated
0x08 16 Server-supplied challenge

The recovered client does not generate the server challenge.

PLA1: client authentication, 40 bytes

Offset Size Value
0x00 4 ASCII PLA1
0x04 2 Version 1
0x06 2 Zero
0x08 32 HMAC-SHA-256 result

BCryptOpenAlgorithmProvider with SHA256 and HMAC flag 0x8 establishes the MAC construction. The transcript is exactly 42 bytes:

HMAC-SHA-256(
    key = selected_psk_bytes,
    data = PLH1[0x10:0x20]
         || PLC1[0x08:0x18]
         || PLH1[0x0C:0x10]
         || PLH1[0x06:0x08]
         || ASCII "PLK1"
)

Its source lengths are 16 + 16 + 4 + 2 + 4. The handshake magic/version fields and PLC1's unknown word are not included. PLA1 is not plain SHA-256.

The PSK is the nonempty narrow environment value PACK_LAUNCH_PSK; missing, null, empty or failed lookup selects the 19-byte ASCII fallback pack-launch-dev-psk. Bytes are used verbatim up to strlen: no trimming, hex decoding or terminating NUL is included. A whitespace-only value is nonempty.

Authenticated receive envelope

Outer type 0x16 carries this payload:

Envelope offset Size Field
0x00 1 Version, must be 1
0x01 16 Wire-provided IV
0x11 4 Unsigned big-endian ciphertext length C
0x15 C AES-256-CBC ciphertext
0x15 + C 32 HMAC-SHA-256 tag

The exact envelope length is C + 0x35. HMAC covers envelope[0:0x15+C]: version, IV, encoded length and ciphertext. It excludes the outer frame word, outer type DWORD and trailing tag. B compares the tag as eight DWORDs with early exit, then decrypts only after a match. This comparison is not constant time.

The AES helper uses a separate 32-byte key, ChainingModeCBC, a mutable copy of the wire IV, and BCryptDecrypt with BCRYPT_BLOCK_PADDING. The output must contain at least four bytes and begin with inner LE32 type 0x15; bytes following that type return to the handshake or PLK1 caller. Successful CBC/padding acceptance is delegated to BCrypt.

Key-state boundary

B RVA Size Purpose
0x41C44 1 Nonzero readiness gate
0x41C48 32 AES key
0x41C68 32 Envelope HMAC key

These lie beyond the .data file-backed end at RVA 0x41C00 and begin zero-filled. The parser locks a separate state object, checks readiness, copies the keys into two local buffers, then unlocks before authentication/decryption. Zero readiness causes generic framed-receive failure. The code does not test the key arrays themselves for all-zero content.

No initializer or writer for these globals was found in A, B, or the recovered outer components. A's mapper leaves the region zero-initialized, and the handshake PSK does not flow into this key state; no KDF was recovered.

Key initialization therefore remains unresolved. The earliest encrypted read occurs after PLH1 and before Core loading, so later Core acquisition cannot account for the initial key state.

Relevant B RVAs: parser 0xF30E; AES helper 0xF028; readiness read 0xF34F; key source reads 0xF373 and 0xF380; sole parser call 0x55CC.

PLK1 delivery and cache

The header is exactly 0x38 (56) application bytes after lower framing/type removal.

Offset Size Field Recovered interpretation
0x00 4 Magic ASCII PLK1
0x04 2 Wire version 1 or 2
0x06 1 LZ4 flag Nonzero enables decompression only for version 2
0x07 1 Reserved No use established
0x08 8 total_size Transferred byte count
0x10 8 orig_size Original plaintext size for version 2
0x18 32 SHA-256 Expected final plaintext digest

Effective transferred and plaintext sizes must be nonzero, fit a DWORD and not exceed 0x08000000 (128 MiB). Version 1 disables LZ4 and uses total_size as plaintext size. Version 2 uses orig_size.

The first TryLoad(slot, expected_digest, out) is evaluated against this header. A hit sends LE32 control 2 and returns verified cached plaintext without receiving chunks. A miss sends LE32 control 1 and allocates the validated wire-size vector.

Each fresh chunk is a separate lower-framed application object:

Offset Size Field
0x00 4 Sequence, initially zero
0x04 4 Nonzero data length
0x08 variable Exactly that many bytes

The received object must be exactly 8 + length; sequence must match; the 64-bit cursor plus length must not exceed total_size. B copies the chunk, sends the accepted sequence as a four-byte acknowledgement, increments the expected sequence, and finishes only at exact total-size equality.

With LZ4 enabled, an explicitly sized raw-block decode produces exactly orig_size bytes. This is not an LZ4-frame stream. After optional decompression, SHA-256 of the final plaintext must match the header digest.

Mandatory cache round trip

Fresh verified plaintext is not returned directly. Save must succeed, then immediate TryLoad must succeed with a nonempty vector, and that reload becomes the caller's result. Save failure, reload failure, malformed framing, sequence errors or hash failure returns no successful vector.

Cache object Relative layout
Protected blob <base>\pluginsdata\x86\blobs\<slot filename>.pblob
Metadata <base>\pluginsdata\x86\meta\<slot filename>.json

The absolute base is unresolved. Slot 1 names PackClientCore.secondary.dll, while all other selector values name PackClientCore.primary.dll. Both reachable pull calls use slot 0; no failover, promotion or cross-slot path was found.

Save uses description PackMonitorClient.PluginStore, optional-entropy identifier PackMonitorClient.PluginStore.v1, and metadata encoding label dpapi_current_user_v1. The resolved CryptProtect/Unprotect capabilities and dataflow strongly support current-user DPAPI protection. Its metadata template is:

{"version":1,"file":"%s","sha256":"%s","enc":"dpapi_current_user_v1","blob":"%s","root":"%s"}

TryLoad recomputes paths from the slot; it does not trust the metadata's file, enc, blob or root fields for selection. It requires a 64-character matching metadata digest, unprotects the blob, and independently hashes the plaintext before returning it. Blob/metadata writers use a .tmp file then MoveFileExW(..., 9), meaning same-target replace plus write-through; failure cleanup deletes the temporary file.

Relevant B RVAs: pull 0x37AF; transfer body 0x3A49; header receive 0x3AB8; initial TryLoad 0x3C46; chunk receive 0x3DE1; LZ4 boundary 0x3FAC; plaintext hash verification 0x418C; Save 0x4200 -> 0xD899; reload 0x4263 -> 0xDCE3; vector-to-PE handoff 0x4938 -> 0x10591 -> 0x10136.

Validation

The tooling reference provides offline parsers and synthetic fixtures for the structures documented above. Runtime coverage and unresolved boundaries are summarized in evidence and limitations.

Evidence figure

Enlarged evidence figure

Open figure at full resolution ↗