To be honest, my actual first “Japanish gacha music game” is the old sb69 game when I tried it at my freshment grade in university.
I played it for about half a year, and quitted not long after the LR card came out. But I still keep the x旋律 song from Bud Virgin Logic, it is really amazing.
I’ve heard about this new SBR game made by SE, and saw it is now playable. So I installed and tried it out.
For the first 10 gacha, I kept getting male SSRs (??). Almost went crazy before I finally got ダル太夫.
And as for the game experience, it is … just okay? In general the feel has nothing special, the game lack some new attracting points. I guess it’s only the years of fan base of all the bands in SBR project got the game this much players. If it keeps going like this without anything new, I doubt it will last long. At least I won’t be addicted.
- Resource files
- Resource manifests
- Net packets
First of all, the key for CriWare is 54605542411982574, hex: 00C1FF73 963BD6EE. There is an authentication file written in config, which is zYzGZYc.png, not sure what this is for.
Beginning with the downloaded files on device. As always, searching keywords in dump.cs revealed the “decryptAssetBundle” method under “SBR.Core.AssetBundles.ABLoadService” class. And there is a really obvious LoadAssetBundleCrypto method right above it.
Decompiles into the method, it first reads a TextAsset from bundled assets. The file name is class static member “confName”, which is “abc”. And there is indeed such a TextAsset inside data.unity3d file.
Then it initialized a SBR.Core.AssetBundles.ABCryptoKey class. The ctor function reads two strings (“5pvoKUvp2EinvR5C” and “Vh6TCcm4sJsO9VpS”) as the key and salt. ABCryptoKey inherits from AssetBundleCryptoKey class, it has two methods for converting string into byte. All those does is Convert.FromBase64String .
The ABCryptoKey instance is passed to “Decrypto” method from “Ulibow.Common.Crypto.CryptoService” class. I actually stucked here for a while, because it is a call on a dynamic lookup table. But later I checked the Ulibow.Common.Crypto.Provider, there’s only one RijndaelServiceProvider … well okay then.
Reading this Decrypto function, it passes the password and salt parameter into the Rfc2898DeriveBytes (you again) for 1024 iterations, takes out 48 bytes. The first 32 are the key and the last 16 are the IV, pass them to Rijndael ( or AES-256 ).
The “abc” TextAsset from above is decrypted into a MemoryStream. Read it as “int32 length + byte data” format gets two pwBytes and saltBytes byte arrays. Save them in the AssetBundleCryptoConfig and this is the shared password for all the asset files. (Not key to be exactly, since the key is derived from the password)
- Resource manifests:
There are two type of manifest. One is the asset info, and another is the bundle table.
First we need to understand the URL or path of the resource files. The URL looks like this:
And the local path looks like:
It is basically the same.
I’ve already found SBR.Core.Manager.AcbManager::getEncryptedServerPath method when I was searching in the dump.cs. It called SBR.Core.Encryptor::Encrypt , which called Standard.Hash.xxHash64, and this is a open-source library.
Initially I thought the hash function is modified by looking at the codes. But after some careful comparision, it is actually not touched. The differences came from 64-bit unsigned vs signed, plus the ugly code from ida decompile.
Every part of the path is hashed using xxHash64, with custom seed of 0x33DC7CB65408BDF . The original form of the above path is /asset/ios/charactercard/charactercard00323001 .
Every time the game starts, it downloads /asset/ios/filelist . This file is a custom binary data. It is decrypted using key and salt from AssetListFileCryptoKey class, respectly KAjyg6DTjC5ScfH9 and 2Ae2C7jQEJuU6h2j .
The reading is simple. First you got the header 0xBFC2A1C2, throw away an int32, then it is AssetInfo all the way to the eof. Read the content as “int32 len + char name, int64 hash, int32 size, int32 category, bool is_individual_download” .
After the the filelist also fetch the /asset/ios/ios as needed. It is a standard unity bundle, there’s an AssetBundleManifest object inside ( seems like a built-in class from Unity Engine? ), it contains names, variants (empty at this time), infos. There are hash and dependencies under infos. To find the bundle just search the file name from here.
- Net packets
There’s almost nothing worth researching in packets. Straight msgpack format with no encryption. I saw a “request_hash” field previously, but they put the timestamp in there?? Come on SE, that’s not a hash???
Well just copy contents from actual request, and the db update checker is done. There’s an app_version field, but server only checks if it is smaller than the current version. So I just make it a large number and it works.