Docs: bring READMEs in line with current state (net48, JSON wire)
- Root README: launcher is .NET Framework 4.8 (was .NET 6/x86); RPC is framed JSON (was BinaryFormatter); added Contract/ + SecureConfig/; dropped the now-resolved "known duplication" warning; corrected the lib/*.dll note (TeslaConsoleLaunchLib/TeslaSecureConfiguration are source-built, kept only as test baselines); added a short history section. - Console README: Contract is net48-only; Launcher targets net48. - Launcher README: net48 framework-dependent (was net8/x64 self-contained); needs .NET Framework 4.8 (built into Windows), no bundled runtime. - DiffTests README: original 4.11.3.37076 vs recovered 4.11.4.x (no longer share identity); noted version-insensitive comparison + the new protocol/crypto guards. - Removed Launcher/assets/MEMORY.md (stale leftover dev-notes, superseded by the README and now-moot post-BinaryFormatter). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,115 +0,0 @@
|
||||
# TeslaLauncher Project Memory
|
||||
|
||||
## Project Overview
|
||||
Simulation cockpit launcher system (BattleTech/laser-tag venue).
|
||||
Original: Windows 2000 app by Elsewhen Studios LLC.
|
||||
Current: Modern .NET 6 rewrite to work around Session 0 isolation (Vista+).
|
||||
|
||||
## Architecture: 3 components
|
||||
1. **TeslaLauncherService** (Session 0 Windows Service) - TCP 53290, handles Console commands, forwards via Named Pipe
|
||||
2. **TeslaLauncherAgent** (WinForms tray, user desktop session) - Named Pipe server, launches/kills apps
|
||||
3. **SecureConfig** (PodSecureConfigurator) - First-boot network configuration protocol
|
||||
|
||||
## Key Files
|
||||
- `TeslaLauncherService.cs` + `.csproj` - Windows Service (net6.0-windows, x86, self-contained)
|
||||
- `TeslaLauncherAgent.cs` + `.csproj` - Userspace Agent (WinForms, net6.0-windows, x86)
|
||||
- `LaunchModels_Shared.cs` - BinaryFormatter wire types + JSON IPC types
|
||||
- `SecureConfig.cs` - First-boot UDP beacon + AES configuration protocol
|
||||
- `LaunchApps.xml` - Game configs (Red Planet 4.11, BattleTech Firestorm at C:\Games\)
|
||||
- `build.bat` / `install.bat` - Build and install scripts
|
||||
|
||||
## Assets (original binaries)
|
||||
- `assets/Tesla Application Launcher/` — original pod-side binaries (TeslaLauncherService.exe, TeslaSecureConfiguration.dll, etc.)
|
||||
- `assets/Tesla Console/` — original console-side binaries (TeslaConsole.exe, TeslaSecureConfiguration.dll, etc.)
|
||||
- `assets/configoflc.pcapng` — pcap of a SUCCESSFUL pod configuration (used to confirm protocol)
|
||||
- `assets/pcap.pcapng` — pcap of a FAILING configuration (root cause: Console RPLY sent from wrong NIC)
|
||||
|
||||
## SecureConfig Protocol — CONFIRMED from binary (TeslaSecureConfiguration.dll)
|
||||
All values below are verified from the original DLL.
|
||||
|
||||
### UDP Config Phase
|
||||
- Port 53291: pod broadcasts RQST, Console listens
|
||||
- Port 53292: Console broadcasts RPLY (UDP), pod listens
|
||||
- RQST format: `RQST`(4 plaintext) + MAC(6) + RequestId(3) = 13 bytes PLAINTEXT
|
||||
- RPLY format: `RPLY`(4 plaintext) + AES-256-CBC ciphertext(64 bytes) = 68 bytes
|
||||
- CBC ciphertext = IV(16) + PKCS7-padded plaintext(48)
|
||||
- AES key = PBKDF2(passphrase, salt, 1000 iter, 32 bytes)
|
||||
- Salt (32 bytes): 0x17,0xab,0x51,0xd9,0xec,0xd1,0xd4,0x74,0xa9,0x09,0x4a,0x34,0x27,0xfb,0x1f,0xf2,0xde,0xc4,0xf9,0xf1,0xa6,0xd8,0x9e,0xda,0x15,0x11,0x47,0x65,0x32,0xe7,0xe7,0xef
|
||||
- Passphrase alphabet: "23456789ABCDEFGHJKLMNPQRSTUVWXYZ" (32 chars)
|
||||
- RequestId length: 3 chars; Passphrase length: 5 chars
|
||||
- Temp IP: RANDOM in 172.16.0.0/12 range (byte[0]=172, byte[1]=0x1X random, bytes 2-3 random)
|
||||
- Our fixed 172.16.0.100 is fine (Console doesn't connect back to temp IP)
|
||||
- Temp mask: 255.240.0.0
|
||||
|
||||
### TCP Session Phase (port 53292) — CONFIRMED from DLL decompilation (ilspycmd)
|
||||
- Console connects TCP to pod's NEW IP:53292 AFTER UDP config
|
||||
- Both sides call the SAME `NegotiateCryptoStreams` static method:
|
||||
1. Both write own 16-byte IV (raw), read other's 16-byte IV
|
||||
2. OFB setup: outStream keyed from OTHER side's IV, inStream keyed from OWN IV
|
||||
- Pod writes with consoleIv, reads with podIv
|
||||
- Console writes with podIv, reads with consoleIv
|
||||
3. Both write raw "CONF" (4 bytes: 0x43,0x4F,0x4E,0x46) over OFB
|
||||
Both read 4 bytes and verify == "CONF" → key verification handshake
|
||||
If mismatch → returns false → caller retries with new TCP connection
|
||||
- After NegotiateCryptoStreams returns true:
|
||||
4. Pod → Console: BinaryWriter.Write(string) — RSA-2048 public key XML (~417 bytes)
|
||||
5. Console reads RSA XML, generates AES-256 session key, RSA-encrypts it
|
||||
6. Console → Pod: BinaryWriter.Write(int len) + BinaryWriter.Write(byte[] ciphertext)
|
||||
(260 bytes = 4-byte int32 (256) + 256-byte RSA PKCS#1 v1.5 ciphertext)
|
||||
7. Pod: ReadInt32() + ReadBytes(len) + RSA.Decrypt(enc, Pkcs1) → session key
|
||||
- Key derivation: PBKDF2(passphrase, salt, 1000 iter, 32 bytes) — same key both directions
|
||||
- OFBCryptoStream: unidirectional, constructor calls NextBuffer() immediately (IV→KS block 0)
|
||||
|
||||
### Registry / netsh
|
||||
- Hostname: SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName
|
||||
- netsh: `interface ip set address "{adapter}" static {ip} {mask} {gw}` with ` 1` metric
|
||||
- DNS: `interface ip set dns "{adapter}" static {dns}`
|
||||
|
||||
## BinaryFormatter Cross-Runtime Findings (.NET FW 2.0 → .NET 6)
|
||||
- **MemberInfoSerializationHolder field names differ**: .NET FW 2.0 uses "Name", .NET 6 uses "MemberName"
|
||||
- **MethodInfoProxy** intercepts via binder redirect + ISerializable, reads "Name" field
|
||||
- **IObjectReference fixup fails for struct fields**: InvokeCommand is a struct; fixup doesn't update Function field in boxed value type. Fixed with [ThreadStatic] `LastResolvedName` fallback.
|
||||
- **Console checks ICMP reachability** before connecting TCP 53290 — Windows Firewall must allow ICMPv4 type 8
|
||||
- **BindToName** must write "TeslaConsoleLaunchLib" as assembly for Tesla.Net types in serialized responses
|
||||
- **JsonElement not [Serializable]**: Agent IPC returns JsonElement via System.Text.Json; must convert to proper Tesla.Net types before BinaryFormatter serialization
|
||||
|
||||
## Known Bugs Found During Scan
|
||||
1. ~~**CONFIG_FILE path mismatch**~~: Fixed
|
||||
2. **IsMachineConfigured in Agent** doesn't filter virtual adapters (Service version does) - may false-trigger on Hyper-V machines
|
||||
3. **`new Random()` for passphrase/RequestId generation** - should use RandomNumberGenerator (security)
|
||||
4. **Named pipe has no PipeSecurity ACL** - any local process can connect and issue commands
|
||||
5. **Volume control silently fails** if nircmd.exe not present - no fallback
|
||||
6. ~~**`_clientTasks` list**~~: Fixed with _taskLock
|
||||
|
||||
## Wire Protocol
|
||||
- Console → Service: TCP 53290, OFB-encrypted (NegotiateCryptoStreams + session key), BinaryFormatter RPC
|
||||
- Service → Agent: JSON over Named Pipe "TeslaLauncherIPC" with 4-byte length prefix
|
||||
- SecureConfig: UDP 53291 (RQST beacon), UDP 53292 (RPLY), TCP 53292 (OFB + RSA key exchange)
|
||||
|
||||
### Management Port (53290) — confirmed from decompiled TeslaLauncherService.exe
|
||||
- Original: `ManagePort = 53290`, `ConfigPort = 53291`
|
||||
- Every Console connection: NegotiateCryptoStreams(sessionKey) → BinaryFormatter RPC over OFB
|
||||
- Session key: 32-byte AES key from RSA exchange during SecureConfig, saved to TeslaKeyStore.key
|
||||
- Key file: `C:\ProgramData\TeslaLauncher\TeslaKeyStore.key` (format: 1-byte length + key bytes)
|
||||
- Original Console: `PodManagerConnection.Open()` → `NegotiateCryptoStreams()` → `BinaryFormatter.Serialize/Deserialize`
|
||||
|
||||
## Install Product Protocol (confirmed from old pod pcap)
|
||||
- Console uses TWO concurrent TCP 53290 connections:
|
||||
- **Polling connection**: GetInstalledApps, then GetOutOfBandProgress polling (~4/sec), then InstallApp
|
||||
- **Install connection**: InitiateInstallProduct → Guid response, then 8-byte Int64 LE file size + raw file bytes, then FIN
|
||||
- Service handles InitiateInstallProduct and GetOutOfBandProgress directly (not forwarded to Agent)
|
||||
- Progress tracking: receive (0-50%), extract to C:\Games (50-95%), postinstall.bat (96%), complete (100%)
|
||||
- Zip-slip protection on extraction; postinstall.bat executed with 60s timeout then deleted
|
||||
- **FIXED**: Console sent file 3x because `InstallProductWorker` loops `for (i=0; i<3; i++)` and only breaks on `PercentComplete == 99`. We were returning 100 → now returns 99.
|
||||
|
||||
## Fixes Applied (session 2)
|
||||
- **Agent splash-to-normal transition**: form.Close() in timer + Main() falls through to AgentApplication
|
||||
- **Guid format mismatch**: string-first deserialization with TryParse (moot after moving to Service)
|
||||
- **InitiateInstallProduct**: handled directly in Service, returns Guid, triggers ReceiveInstallFile
|
||||
- **LaunchApps.xml ACL**: icacls in install.bat grants Users modify access
|
||||
- **build.bat /q flag**: suppresses pause for non-interactive terminals
|
||||
- **install.bat**: creates C:\Games with Users modify ACL
|
||||
|
||||
## User Preferences
|
||||
- User writes in informal English, technical background
|
||||
- Prefers building via build.bat (not direct dotnet publish)
|
||||
Reference in New Issue
Block a user