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

Caches下的usm确实能通过正常的解usm的方式解,但是解出后时长只有6秒,很显然不是hcg的全部内容,再次筛查后找到了存储hcg的位置,可以直接使用AssetStudioModGUI解开,得到以下内容


接着尝试删除后缀,以正常usm文件解。但尝试后发现不行。下面是源文件链接:通过网盘分享的文件:__data.zip
链接: https://pan.baidu.com/s/1P8uSUC-A3-02s0KhbhtC7A?pwd=7wv7 提取码: 7wv7
希望有懂usm解密的大佬能提供方法或思路,谢谢

手上的脚本是别人的不方便给。大致是把文件名hex累加,当作seed喂给unity的random,然后生成一串字节去异或usm的文件头。
也可以去直接调用游戏函数,这个例子可以参考但不建议模仿(仅作为可行性证明,il2cpp的ctypes定义可以用ctypeslib自动转换)

2 个赞

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 个赞

非常感谢