55 lines
1.7 KiB
Python
55 lines
1.7 KiB
Python
"""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
|