求助,关于少女艺术绮谭hcg的USM的加密

Unity的Random使用的XORShift128
usm的前8字节是标识(Signature), 后面4字节是加密长度(小端序)
将文件名(包括后缀)转换bytes然后相加得到seed
根据加密长度生成对应的随机数 然后和文件 xor
实现就是这样

from pathlib import Path

class XORShift128:
    __slots__ = ['x', 'y', 'z', 'w']
    MT19937 = 1812433253

    def __init__(self, seed: int = 0):
        self.init_seed(seed)

    def init_seed(self, seed: int):
        self.x = seed & 0xFFFFFFFF
        self.y = (self.MT19937 * self.x + 1) & 0xFFFFFFFF
        self.z = (self.MT19937 * self.y + 1) & 0xFFFFFFFF
        self.w = (self.MT19937 * self.z + 1) & 0xFFFFFFFF

    def xorshift(self) -> int:
        t = self.x ^ ((self.x << 11) & 0xFFFFFFFF)
        self.x = self.y
        self.y = self.z
        self.z = self.w
        self.w = (self.w ^ ((self.w >> 19) & 0xFFFFFFFF) ^ t ^ ((t >> 8) & 0xFFFFFFFF)) & 0xFFFFFFFF
        return self.w

    def next_uint_range(self, umin: int = 0, umax: int = 256) -> int:
        if (umax - umin) == 0: return umin
        return (umin - self.xorshift() % (umax + umin)) if umax < umin else (umin + self.xorshift() % (umax - umin))

def batch(usm_path: str = '', ext: str = '*.usm', subfolder: bool = False):
    path = Path(usm_path) if usm_path else Path.cwd()
    need = [i for i in (path.rglob(ext) if subfolder else path.glob(ext))]
    for i in need:
        with open(i, 'rb') as f:
            if f.read(8) != b'GC\x20Movie':
                continue
            elen = int.from_bytes(f.read(4), 'little')
            data = bytearray(f.read())
        rnd = XORShift128(sum(i.name.encode()) & 0xFFFFFFFF)
        data[:elen] = bytes(j ^ rnd.next_uint_range(0, 255) for j in data[:elen])
        if not data.startswith(b'CRID'):
            print(f'解密错误 ---- {i.as_posix()}')
            continue
        with open(f'{i}.dec', 'wb') as w:
            w.write(data)

if __name__ == '__main__':
    path = r'' # 指定游戏文件所在文件夹
    ext = '*.usm' # 指定要解密的文件类型后缀
    subfolder = False # 是否查找子文件夹内的文件
    batch(path, ext, subfolder)

2 个赞