求助大佬指路DMM ガールズクリエイション~少女藝術綺譚~如何解包

acb.py或者vgmstream

没有加密的,使用VGMToolbox中的CRI ACB/AWB工具也能正常解出(PC版资源包)

感谢 Jitsu 大佬的指点!

您提到的 vgmstream 非常有用!我把 vgmstream 作为插件添加到了 foobar2000,之前打不开的 acb 文件里的语音,已经成功提取了。

謝大佬,不過如果是沒有gameplayer的exe的遊戲呢?
我目前正在找オトギフロンティア的appkey
如果大佬知道的話能分享一下嗎? 感謝!

站内不是能搜出来吗,而且和这游戏用的又不是同一个方案

站內那篇只有說明如何解包,我希望知道的是遊戲的appkey :sob:
想研究sp版是如何用md5及base64加密的
不過感覺已經偏題了,畢竟我開頭問的是藝術,抱歉

昨天玩了下这游戏,顺带做一下全资产下载/更新+解密数据表,开始教程:
首先直接找s和t两个参数,跳转到 Gc::GcUtil::AttachSecureLink


可以看到AppKey参与了s参数生成,而t参数就是GetCdnUnixAddTime加了一小时也就是3600,那么首先要找到AppKey

跳转到DMM::OLG::Unity::Engine::Api::Initialize
DMM::OLG::Unity::Engine::Config::Get 拿了AppKey


然后找到DMM::OLG::Unity::Engine::Config::Init函数,是BaseSystemConfig.dat里面拿的

这玩意在安装包里面,有4个:

612b88b23a5eb704db663b6c706ff586 BaseSystemConfig(Staging)
152b24a73b6a14031a4cb1d673c71986 BaseSystemConfig(Production)
a230c7ea95967406780cb9f454418498 BaseSystemConfig(Develop)
68c7fc2368477ad41a45618f013036b4 BaseSystemConfig

68c7fc2368477ad41a45618f013036b4就是我们要的,其他的有兴趣的可以自己研究
继续往下翻能找到DMM::OLG::Unity::Engine::Internal::ConfigData::Load函数,这里就是加载的地方,核心是这部分,受限篇幅不细讲


解析脚本如下:

import io
import base64
import struct
import UnityPy
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from Crypto.Hash import HMAC, SHA256

KEY_BASE = "RWd3NusabzRc"
SECRET_KEY = "dB3aqcLtAmBd"
IV_SIZE = 16

class BinaryReader:
    def __init__(self, stream):
        self._stream = stream

    def read_7bit_encoded_int(self) -> int:
        value = 0
        shift = 0
        while True:
            byte = self._stream.read(1)
            byte_val = ord(byte)
            value |= (byte_val & 0x7F) << shift
            if (byte_val & 0x80) == 0:
                break
            shift += 7
        return value

    def read_string(self) -> str:
        length = self.read_7bit_encoded_int()
        if length == 0:
            return ""
        return self._stream.read(length).decode('utf-8')

class Decryptor:
    def __init__(self, key_base: str, secret_key: str):
        self._aes_key = self._generate_aes_key(key_base, secret_key)

    def _generate_aes_key(self, data_string: str, secret_key_string: str) -> bytes:
        data_bytes = data_string.encode('utf-8')
        secret_key_bytes = secret_key_string.encode('utf-8')
        
        h = HMAC.new(secret_key_bytes, digestmod=SHA256)
        h.update(data_bytes)
        return h.digest()

    def decrypt(self, base64_encrypted_str: str) -> bytes:
        encrypted_data = base64.b64decode(base64_encrypted_str)
        iv = encrypted_data[:IV_SIZE]
        ciphertext = encrypted_data[IV_SIZE:]

        cipher = AES.new(self._aes_key, AES.MODE_CBC, iv)
        decrypted_padded = cipher.decrypt(ciphertext)
        
        return unpad(decrypted_padded, AES.block_size, style='pkcs7')

def extract_config(bundle_path: str) -> bytes:
    bundle = UnityPy.load(bundle_path)
    
    for obj in bundle.objects:
        if obj.type.name == "TextAsset":
            data = obj.read()
            if data.m_Name == "BaseSystemConfig":
                binary_data = data.m_Script.encode('latin-1')
                return binary_data
    
    return b''

def decrypt_config(bundle_path: str):
    binary_data = extract_config(bundle_path)
    stream = io.BytesIO(binary_data)

    decryptor = Decryptor(KEY_BASE, SECRET_KEY)
    config_data = {}

    reader = BinaryReader(stream)
    b64_count = reader.read_string()
    decrypted_count_bytes = decryptor.decrypt(b64_count)
    num_entries = struct.unpack('<i', decrypted_count_bytes)[0]

    for i in range(num_entries):
        b64_key = reader.read_string()
        decrypted_key_bytes = decryptor.decrypt(b64_key)
        key = decrypted_key_bytes.decode('utf-8')

        b64_value = reader.read_string()
        decrypted_value_bytes = decryptor.decrypt(b64_value)
        value = decrypted_value_bytes.decode('utf-8')

        if value == "[BaseSystemEmptyValue]":
            value = ""
        
        config_data[key] = value
        print(f"{key} = {value}")
        
    return config_data

if __name__ == "__main__":
    decrypt_config("68c7fc2368477ad41a45618f013036b4")

得到我们要的AppKey,此外还有一些其他参数


至此可以正式回到s参数生成的部分
拼接AppKey+ 完整url + timestamp,进行md5哈希, 然后Base64 URL-safe 编码,最后除尾部的=就行了,经过测试h参数是不必要的,所以s,t参数部分搞定
_LoadAssetBundleManifestAsync_d__0::MoveNext 函数里面可以找到要下哪些

接下来看数据表,cs文件里面可以找到MasterLoader相关的一些函数


筛选后直接看_Load_b__22_0,拿TextAsset然后用TextAssetDecryption解密,最后解压,这里也不细讲了,base64解码后TripleDES解密(ECB),然后再base64解密, 最后zlib解压(无zlib 头的原始deflate)

成果:

2 个赞

大佬太强了。另外资源清单中的v参数好像也不必要。

应该是,所以我脚本里面直接拼了 :face_with_monocle: