"""Boxsim file scrambler: the XOR layer of the MyIni2CK container. Reverse engineered from Boxsim 2.1 (Init at VA 0xa12be4, next byte at 0xa12c9c). A table-shuffled combination of three linear congruential generators, seeded with the same constants for every file Boxsim has ever written. """ M32 = 1 << 32 SEED1 = 123 SEED2 = 456 # Compared against a global at VA 0xb10588, which the image ships as 0xffffffff # and no code path ever writes. THRESHOLD = 0xFFFFFFFF class KryptPRNG: """Keystream generator for the XOR layer.""" __slots__ = ("s1", "s2", "s3", "table", "thr") def __init__(self, s1: int = SEED1, s2: int = SEED2, threshold: int = THRESHOLD): self.s1, self.s2, self.thr = s1, s2, threshold self.s3 = (s1 % 0xFFFE) * (s2 % 0x7F23) self.table = [0] * 256 for i in range(256): self._step() self.table[i] = (self.s1 + self.s2) % M32 def _step(self) -> None: self.s1 = (self.s1 * 0x10B95 + 1) % M32 self.s2 = (self.s2 * 0x10BA9 + 1) % M32 if self.s2 >= self.thr: self.s2 = 0 self.s3 = (self.s3 * 0x1155D + 1) % M32 def next_byte(self) -> int: self._step() idx = self.s3 & 0xFF out = self.table[idx] self.table[idx] = (self.s1 + self.s2) % M32 return out & 0xFF def keystream(self, n: int) -> bytes: return bytes([self.next_byte() for _ in range(n)]) def unscramble(body: bytes) -> bytes: """XOR-decrypt the container body (everything after the 8-byte magic).""" ks = KryptPRNG().keystream(len(body)) return bytes(c ^ k for c, k in zip(body, ks, strict=True)) # The layer is symmetric, but keep both names so call sites read correctly. scramble = unscramble