东方弹幕神乐

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:

35 thoughts on “东方弹幕神乐”

  1. 因為見你說NSSTRING 轉JSON 我就以為是NSSTRING 轉NSDATA 再NSDATA轉NSDICT 那種
    所以我用了nsstring dataUsingEncoding 回傳 “{}” 再轉成NSDATA回
    但沒有效
    後來我發現在__cfc 裡 他會用NSFILEManager 檢查一些cydia的文件是否存在
    我全都回FALSE 也沒有效
    然後我把你說的這些FUNCTION
    __ccs __cf __cg __csid __cfc __detd __ch __cipl
    全都HOOK了 變成沒動作也加了LOG 確定 遊戲有CALL過
    而且CALL了之後遊戲也再沒有用NSFILEManager檢查那些cydia文件了
    但也是一樣沒有效….
    我試試用你那個做好了的插件看看 謝謝

    1. 謝謝大佬回覆
      我剛安裝了DEB 來測試 結果不行
      之後我反組看DYLIB 看到dataUsingEncoding
      我才發現我之前只LOG了{ 沒有LOG [
      是我的錯誤 所以沒有發現
      我跟著你那個方法之後就成功了
      目前打算想試著弄私服
      LCX CERT 檢查已經去了 能直接看HTTPS連接
      之後遊戲伺服器是GRPC
      我之前也有搞過另一隻GRPC 音遊
      看到有類似方法能HOOK 來抓回傳
      不過看到你看留言好像還有FlatBuffer 這個我就不太清楚是什麼來了
      我能進遊戲就先看看能不能抓到什麼GRPC回傳
      謝謝

  2. 大佬介不介意分享多一點點怎麼破檢測越獄的?
    我找了nsstring dataUsingEncoding 的確有看到在抓很多奇怪的文件 但我回了空白的NSDICT也不行
    如果我HOOK NSJSONSerialization JSONObjectWithData 的話就會立刻CRASH;/
    所以實在不確定大佬說的NSSTING轉JSON 是指哪一個..

      1. 大佬 不好意思 還想再問一下
        接著試了一下解DB 存檔和資源 但都沒一個成功..
        不是很確定是否最新版本的IPA 裡用的SEED已經不同了(但我用最新版抓到的SEED也不成功:/)
        然後我用874853741 17 256 解xab文件 salt用DownloadOption &0xFFF
        不過都沒解到 不知是否我做錯了
        DB存檔用了XorShift::SwizzleBytes 資源用了LightWeightEncryptor的Modify

        1. 874853741 17 256是什么组合,我这都没见啊?
          xab的话,下载的用2001,内嵌的用1002

          public class EncryptorManager
          {
              public const int EMBEDED_AES_KEY = 1001;
              public const int EMBEDED_ASSETBUNDLE_KEY = 1002;
              public const int EMBEDED_DAT_KEY = 1003;
              public const int EMBEDED_FILELIST_KEY = 1004;
              public const int DEFAULT_KEY = 1501;
              public const int DOWNLOAD_ASSETBUNDLE_KEY_1 = 2001;
              public const int DOWNLOAD_DAT_KEY = 2002;
              public const int DOWNLOAD_DB_KEY = 2003;
          }
          
  3. 请问大佬有研究过pcr的资源直链吗,想下日服的usm来当电脑桌面,国服有些还没实装

  4. dmm版cgss目前能看到完整的Assembly-CSharp。。算是福利了吧?
    不过我觉得不排除以后会像pcr一样突然锁上
    建议大佬如果有空就开始做备份dump吧,谢谢了

    1. 看了眼就,很怪,他把最外层的imascgstage.exe,和plugin的CySpring加壳了。
      加壳了,只能加一点点。

      1. 可能还是懒吧,反正dmm版我昨天试过了跟pcr一样启动有验证。你直接改dll肯定是不行的一验证就重新下载dll覆盖掉了只能自己想别的办法。
        顺便说一句之前那个事就是AR的注入那个方法:我私聊问了下现在做修改的版本作者,居然是用直接注入关键方法无视版本更新只要不变结构就行(github有个库,这里不方便公开),唯一缺点当然还是那个问题必须自己打签名,安全性实在太低一旦用的人太多cy知道了就跟前年一样一封就一大批人

  5. 能请教一下大佬的dumper版本么?
    – dumper 代码有bug
    System.OverflowException: 算术运算导致溢出。
    在 Il2CppDumper.BinaryStream.ReadClassArray[T](Int64 count) 位置 C:\projects\il2cppdumper\Il2CppDumper\IO\BinaryStream.cs:行号 210

  6. 老哥有没兴趣搞一下dmm马娘dll拆包的和即将到来的cgss dmm版?
    马娘虽然拆包csv什么的数据确实看得烂大街了但是从来没人见过拆包后的Assembly-CSharp
    至于cgss上一个能看dll的版本.. 已经是2017年5月了..好像这次也算是又有机会来了现在也变了很多东西,这个月初的696版本好像加了谜之检测,让我用了两三年的安全没封号的改法突然废了现在完全不敢下手

    1. 马娘…但是从来没人见过拆包后的Assembly-CSharp

      因为他pc版也用了il2cpp,本来就没有Assembly-CSharp

      即将到来的cgss dmm版

      不是,触屏音游怎么上电脑??

      让我用了两三年的安全没封号的改法突然废了现在完全不敢下手

      你就是音游王是吧

      1. 那看来应该马娘是无解了。。
        至于cgss。。其实要过cy的检测隔壁perfare也说了,不是100%P就行,但是现在有个问题。。这个方法好像在某android republic半年前也有公开包(具体地址就不发了很容易找出来,至于那个发公开包的人我怎么看都觉得像国人。他们是用注入而我是自己写类似邦邦的跳转随机方法),现在这个方法好像因为cy把所有随机数生成方法全部ban掉了一用就崩游戏但是又苦于看不到源码,所以我才想问问到时候有没机会搞一下

      2. 哦差点忘了,至于你说的dmm版PC怎么玩的问题看cy的说法可能是类似隔壁mltd的autopass搞的个什么autoplay,具体也不明确反正官推上有相关说明

      3. 差不多吧,但是好像打歌功能还有反正目前也不明确
        现在的cgss我是真的暂时不敢下手,AR那人的方法我感觉好像是自己写随机数生成,他那个我不敢用毕竟又涉及到签名问题,现在签名问题自从前年大封号以来到底封不封号一直是个不明确的问题。现在随机数生成的方法我之前也稍微看了下又可能是类似米忽悠和unity的py交易那样,甚至连参数都改了比如RandomRangeInt原本两个参数是int min, int max现在居然是什么没见过的int minInclusive, int maxExclusive??我甚至都不知道具体别的还在里面改了些什么所以完全不敢动

        1. 可能楼上的没注意到或者已经注意到了cy去年又搞了套更恶心的事:延迟封号

          就目前知道的来说cy的追封具体时间不确定可能是随机的也可能是固定每隔一段时间来一次,以现有的例子目前知道的时间点一个是手游周年后(大概9月底)一个就是十周年后。我知道的最明显的例子就是有人周年前大概七月底左右本来测试目的结果忘了恢复数据把某首歌打出高出榜一大概百万分以为会被封号,结果居然只是被清数据号保留下来了??但是他没想到周年刚结束大概9月底那个版本更新后突然追封。。cy是真的事干的越来越离谱了

    2. 马娘可以直接内存中dump出GameAssembly.dll,和global-metadata一起丢进Il2CppDumper就有了,貌似把Assembly-CSharp重命名成了umamusume.dll
      不过现在好像还没有人分析出马娘服务器数据包的解密方法,读数据包内容靠本地hook,如果dalao有兴趣可以尝试破一下解密算法

  7. Hello sir, thank you for the great efforts. Knowing the various method for il2cpp security is very interesting; I really got a lot of help from your articles. When I looked into it and saw the ‘AF 1B B1 FA’ on 0x04, not on 0x00, I gave up quickly because it seemed to be a troublesome one for me. But your research really blew my mind up, I really admire your works.

    By the way, it’s out of the blue, but would you mind if I ask for another help on CRI audio format?
    I’m trying to extract the audio files from Starlit Season’s demo version(PS4), I found some files that seem to be the raw ACB format(according to here) but it seems to be slightly different and didn’t get any success on extracting…

    One additional note you might know; CGSS recently had the Unity upgrade to 2020.3.8f. It seems there are no big changes in the game, but the Beebyte obfuscation is disabled – every function /variable names are plain. I’m having a fun time looking into them(I felt as if I found the PDB of some old game).

    Anyway, thanks for reading the comment.

    1. Thanks for reading the article and commenting

      I don’t actually understand a lot about cri internal. Usually I just use others’ tools.
      Cri in this new TouHou game also might have changed, as the version is now 1.35.00. The initializer says useDecryptor = false, but I can’t get actual audio using key 0.

      The cgss thing is pretty new to me, thanks for this information!
      I’ve actually been away from mobile games a while now, this new TouHou game is kind of a return for me, as I mainly play arcade rhythm games nowadays. ( I also wrote that in my annual reviews )

      1. Thank you for your quick kind reply!

        Gonna look into it harder Starlit Season is the first UE game I look into, there are so many things to learn. If I if something interesting, I will share it with you. Wanted to share anything helpful for you because I really got a lot of help from your articles

        And I’m sorry if I bothered you; computer translation between your language and my language is kinda awful to fully understand so that I missed the change of your main area on the annual post.

        Take care of your health, and thank you again!

    1. 目前是解密不完善 每个文件都有不同的xor key,之前和chrrox聊过这个 不过密码可以暴力算出来

  8. SSL Pinning直接hook LCX.Internal.Util.AcceptAllCertificatesSignedWithASpecificKeyPublicKey的ValidateCertificate,返回true就可以了。
    顺便请教一下这种用GRPC通信的,有什么好的抓包方案吗?除了一个个client intercept

    1. 我试了那个了,但那个只是lcx sdk的通信,不是游戏的。游戏本身的FlatBuffer gRPC我也没啥头绪,不想看了。
      另外直接hook之后,还触发了LV1 Tampering,读完条后直接崩溃了。不过我按照崩溃记录里面的地址直接把那个terminate函数给干掉了倒是能进游戏,挺迷惑的

发表回复

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

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