东方弹幕神乐

English version

2022-11-17 更新:

Dankagu Net Enc – 网络通信加密算法

资源汇总:https://pan.baidu.com/s/1eoeaeHBbgw8sHEhsrJVHag


许久不见,又一次来拆一个游戏了。

il2cpp的发展有些太快,两个月前我原本尝试了一下拆idoly pride,然而因为反射调用实在是太多,代码里面全都是动态函数指针,跟踪起来实在是过于艰难,也就放弃了。昨天东方弹幕神乐开放后,一个是为了首先能进去游戏,然后也想着再试一试拆算法逻辑,所以再来一次拆解。

(更新于9月4日)

  • 开幕雷击

首先一上来,global-metadata被动了手脚。

UnityFramework直接扔进ida里面先搞一个string list出来,直接搜global-metadata就能找到一个函数调用,跟进去没走几步,看到一个对着内存key异或的地方,真行。

$in = fopen('global-metadata-enc.dat', 'rb');
$out = fopen('global-metadata.dat', 'wb');

$size = unpack('V', fread($in, 4));

$key = array_map('chr', [0xE, 0x75, 0x3B, 0x8E, 0, 0x14, 0xE6, 0xB2]);

$enc = fread($in, $size[1]);
for ($i=8; $i<strlen($enc); $i++) {
  $enc[$i] = $enc[$i] ^ $key[$i % 8];
}
fwrite($out, $enc);
  • 越狱检测

首先一上来就给人卡在touch to start了,大概猜一下就能猜到它是检测了越狱。在dump.cs里畅游了快两个小时的时候,终于是找到了TakshoAuthorizer::GameStart(),底下跟踪之后调用了SecurityChecker::CheckGameStart(),一看就很可疑。

然后,虽然没有直接找到到底在哪里,但是各种调用SecureLibAPI类的函数,过去一看有个isRooted,套娃调用is_p_jb_o_c,然后分别调用 __ccs __cf __cg __csid __cfc __detd __ch __cipl检查各种方面。事实上,因为这开发组不知道从哪里翻来的上古检测逻辑,这一大串我只有cfc检查文件被查到了,剩下的全都不会触发。

这些检查项目所有的字符串全部用了一个奇特的混淆,把每个char都变成了一个int,读取的时候按顺序读int,& 0xff,然后^ 0x19。

至于那个我撞到了的路径检查,他直接用了一个nsstring转了json,最初我尝试了一下直接把那些路径tar一下,确实可以进游戏了。后来我就试了一下直接把string挂了个hook,检测他开头匹配了那个json的话,就换成一个空json,也能进游戏了。这样就不需要再每次删文件了。

  • 资源加密

在dump里面直接搜着Encrypt,就路过了一个EncryptorManager类,看起来里面有着各种各样的不同密钥。

解密逻辑主要分为两个,一个是RijndaelEncryptor,看名字就知道是用了Rijndael,进去大略看了一眼用的RFC2898生成key和iv。

而资源之类的则是用了一个叫做LightWeightEncryptor的,初始化的时候用seed生成了一个异或表,表内数据由XorShift类进行生成。数据进来的时候还会带有一个salt,本身就是查表时候会额外有一个偏移量。

这部分代码见 https://gist.github.com/esterTion/26e8500dbec9956fc89eef7daeb97e81


8/10 update

  • 内嵌db

其实这个原本是想要找一下具体的判定区间的。不过打开后发现自带的里面没有判定数据,只有判定分值系数。感觉似乎判定数据是可以服务器下发随时变的?看不懂。

以 Raw/db/master_gamedata.dat 为例,使用 lwe(0x99737BBF, 11, 256),解密之后是个.tar.gz,里面放着一个json

  • 本地设置存档(Documents/saves)

这里面其实也没有太多有用的东西,不过本地存着的manifest用的salt会在存档里面写着。

不同的存档各自使用不同的seed初始化一个XorShift类,然后对存档数据调用XorShift::SwizzleBytes加解密。加密表如下:

998741	abp
38741151	rgm
45782187	hako
100	dialog_show
200	boost_setting
300	gacha
400	nakayoshicloud
400	announcement_
500	playmode_
600	system_setting
700	friend
800	sort_filter
7784741	club
58714163	tmp
5458143	tos

解开之后,0-15是文件头,16-31是checksum,32往后直接是json数据

  • 资源manifest

这个算是挺莫名奇妙的一个逻辑,首先最外层要先用1004的lwe打开(编号含义见EncryptorManager类),然后再用1001的Rijndael解开,揭开后里面是一个.tar.gz,存放着一个FlatBuffer。这开发组在这走迷宫呢?放这么多层套娃,真是不嫌累啊?

然后参照上面存档说的,这个地方唯一不确定参数的是1004 lwe用的salt。但实际上,依然可以暴力尝试找出salt来。取数据前16字节,过一遍lwe,过一遍rijn,然后检查开头是不是1f8b就行了。

拿到FlatBuffer,首先肯定是要抄一份schema出来的,照着生成的类也不算很麻烦,然后可以选择扔给flatc让它直接开,也可以生成类然后自己用sdk调用读取。(flatc生成的json,为啥field周围没引号??)

namespace D4L.TakashoFes.Master;

table AssetPathRaw {
  entry:[AssetPathRawEntry];
}

table AssetPathRawEntry {
  ID:uint64;
  AssetPath:string;
  HashedPath:string;
  FileHash:string;
  FileSize:uint32;
  FileRev:uint32;
  FileType:uint32;
  DownloadOption:uint32;
}
file_identifier "D4AO";
root_type AssetPathRaw;

DownloadOption & 0xfff就是每个资源自己的salt了


暂时更新这些进展,grpc目前有点懒得搞了,一个是麻烦,而且反正更新的时候抓个manifest就可以拿新卡面了,也没啥必要非搞个更新检查,毕竟都没人真的看


Bonus round:

33 thoughts on “东方弹幕神乐”

  1. e佬您好,我看到放出来json里没有安卓素材的salt,然后在filelist里看见有差不多的,安卓的manifest也是是直接用1004的lwe开吗?还是也要过一遍Rijndael 1001,我按您说的试了下解出来的不太对劲,解的是这个

    {
    "Platform": "android",
    "DataVersion": 1280,
    "Enable": 2,
    "DB": "arg_t_android_r_00001280_20220116-040000_1642273200",
    "OpenAt": 1642273200,
    "Salt": 2449,
    "HashPath": "1/10/1280-2-66ea03bdca2f6f83b6901aae100b94c1344bd51f"
    }

  2. 感謝大佬指教
    用了2001 也是不行後發現要用PHP64版本才會成功
    不過DB和SAVE 還未成功 但資源和manifest都成功了
    而且找到了大佬說1004 lwe 用的salt 在GRPC
    CommonFeatureset.PlayerApi.OndemandMaster.GetEntriesV1的這個AssetUploadV2+v1_2_0_any+g154裡有回
    看了一下裡面應該還有未來數據的manifest 想必大佬應該早就弄了cronjob自動下載
    JSON LIST在這裡 過幾天會刪
    https://drive.google.com/file/d/1zZETUA8Ni6G3nARw_Yd0UMxRgNV6BIu-/view

回复 tungnotpunk 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax