Files
SandTools/docs/TASK.md
DownloadPizza 2e886f31f0 docs: .wbt save-format write-up
TASK.md — the solved .wbt format (BSON+XOR+gzip), recovered key, and
the one remaining open item (name-index -> word mapping).
2026-06-11 14:43:57 +02:00

48 lines
2.2 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SAND .wbt — DONE (format fully cracked & BSON-verified)
## Result
The `.wbt` walker save format is solved end-to-end and confirmed by parsing the decoded
output as valid BSON. All 5 on-disk files decode and parse cleanly; decode→re-encrypt→gzip
reproduces the original file byte-for-byte.
### Pipeline
- Save: object → Newtonsoft **BSON****XOR****gzip**
- Load: gunzip → un-XOR → BSON parse
### Cipher (the part the prior session got wrong)
- Real key (6 bytes): `70 DD 1F 2A 0B 4A`
- Key index resets every `0xA000` (40960-byte) chunk:
`decoded[i] = raw[i] XOR KEY[(i % 0xA000) % 6]`
- Verified by disassembling `XorCryptography.Encrypt` (GameAssembly.dll RVA 0xBE9B40) and
by the fact the output parses as BSON. Key recovered from known BSON plaintext
(doc-length header + literal `"textureSize"` field name), not from texture-zero guessing.
### Decoded payload = one BSON document
Keys: `textureSize`(512), `textureRawData`(512×512×4 RGBA), `walker`(WalkerBlueprintDto:
Id/UniqueId/Version/Chassis/Compartments[]/Connections[]/3 hashes), `format`, `iconVersion`,
`firstNameIndex`(int32, 0-based), `secondNameIndex`(int32, 0-based), `creationTime`(datetime),
`name`(null), `isBackup`(bool).
## What was wrong before (corrected in memory)
- 12-byte key `4A72D8122A094F7DDD1D2F06` + align 0x29 — WRONG (keystream XOR texture pixel).
- `name_index.json` `0x60..0x7F`→word table — INVALID (read from mis-decrypted noise).
- "envelope header / nonce at EOF-26" — non-existent; it's all just BSON.
## Only remaining unknown
Map `firstNameIndex` / `secondNameIndex` integers → actual words by dumping the Unity
Localization StringTables `WalkerFirstName` / `WalkerSecondName` (AssetRipper/UABE on the
StreamingAssets `aa/` bundle). The byte format itself needs nothing further.
## Quick reference
```python
import gzip
from bson import decode
KEY = bytes.fromhex('70dd1f2a0b4a'); CHUNK = 0xA000
raw = gzip.decompress(open('<file>.wbt','rb').read())
dec = bytes(raw[i] ^ KEY[(i % CHUNK) % 6] for i in range(len(raw)))
doc = decode(dec) # valid BSON
```
Tools: `~/sand_tools/sand.py` (updated key), venv at `~/sand_tools/venv` (pymongo).
GameAssembly.dll: `/mnt/d/SteamLibrary/steamapps/common/Sand Playtest/GameAssembly.dll`.