Road to eisIF Prilo (1)

1 月 31 日我说:

私服
有人起头然后找我干活的话是可以的
但是反正我绝对不会领头干这玩意(

(from QQ 聊天记录)

但是 2 月 1 日,虽然仍旧没有动手的计划,不过倒是把“万一真自己搞了,那么叫什么名字呢”这个问题给快速决定下来了。

Prilo the Private Long-life Love Love!

——像极了宝宝还没出生就先把名字起好的家长。

然而思来想去还是不忍放弃:

Anyway 虽然很懒但我还是不想放弃:
——1N 2N 复读卡
——全系 P/C 复读
——除了复读以外其它的 Spark
——その他

(from QQ 聊天记录)

当我们想延续 SIF 的时候,我们到底是想干啥
想看卡面的话,查卡器或者 2 的相册就是了
想打歌的话,2 也是原生体验
只有我们不想放弃它这堆玩具技能的时候,我们才有延续它的意义(

(from QQ 聊天记录)

甚至已经想好了退路:

(不过如果只是这样的话,把查卡器的打歌模拟白嫖过来换卡好像也行欸)
(虽然这样就玩不了饰品了)
(嫖一个 js 网站总比搞一个 server 端来得轻松)

(from QQ 聊天记录)

当时甚至已经把最基本的运营方针给敲定了:

我们把所有的原版技能卡都配给强制签名,然后把所有非签卡面拿来自己出代码里有但是从来没实装的新技能,当个新玩具玩(

(from QQ 聊天记录)

以上都只是空想。直到 2 月 17 日情况有了变化。

这天我突然回想起早年在 GitHub 上看到的项目。虽然名字忘了,但是按照代码里的关键字去搜,很快就搜到了 DarkEnergyProcessor/NPPS。

这个项目停留在非常久远的年代(甚至是不到 4.0 的年代)。我在 GitHub 上看了看 Forks,也没找到有价值的 Fork。

我决定试一下这个 3.x 版本的服务端能不能给 9.10 版本的客户端用。——肯定是不能的,只是看看能不能修复出来。

打开一个安装了 9.10 root 版客户端的模拟器,打开 Fiddler,开启断点,手动将客户端发出的请求的 Header 中的 Host 修改到 localhost。

很快问题就出现了,弹框。这个弹框里的内容 NPPS 静态 html 文件里没有,思考了一下感觉应该是强制大更提示框。很奇怪,捣鼓了好久,最后决定先把服务端的有关版本号检查段落注释掉。

进入第二步,这个时候预计应该出现用户不存在的问题。因为我是打开了一个真实客户端的模拟器,已经登录过真实账号了,但是这个账号在 NPPS 里还没有填进数据库里。不出意外地弹框提示连接错误(后话:其实不能叫不出意外,这里的情况其实是另一种问题)。

把这个真实账号抄进 NPPS 的数据库里,重试。欸怎么还是用户不存在。

然后发现了重大问题:客户端登录服务器时,向服务器发送的账号和密码不是明文,而是加密过的。这个解密密钥也是一起发送的,但是密钥也不是明文,而是 RSA 加密过的。

我们不可能拿到真实服务器的 RSA 私钥,因此,当我们把客户端的请求转发到自己的私服时,我们不可能知道客户端向我们发送的账号和密码到底是什么。

2 月 17 日完。

睡前想到了一个好办法。comsumerKey 这个字段是会在每个请求里明文发送的,而且是固定的 lovelive_test 字样。这个字段似乎是从 server_info.json 里读的(?)。那么,我们可以尝试在数据下载阶段动点手脚,确保每个玩家拿到的数据都是不完全一样的,各自的 consumerKey 都不相同。我们就以这个字段来识别不同的玩家了。

当然,这存在较为严重的安全隐患,不过可能是个可行的方法。

2 月 18 日,先测试了一下,在 NPPS 里根据账号密码查找用户的时候,不论收到的账号密码是多少,都强制返回昨天录入的那个账号。起码先找到了,后话之后再说。

结果还是通信错误。

不久后找到了根源,其实昨天就隐隐有点预感了。

客户端向服务器发送的每个请求都附带一个校验码,这个校验码需要基于一个密钥计算,这个密钥是随机的,也是 RSA 加密发送给服务器的。我们没办法知道这个密钥是什么,我们可以不校验嘛。

但是反过来,服务器向客户端发送的每个响应也要附带一个签名,我们不知道这个签名是怎样生成的。NPPS 是以 str_repeat(“\x00”, 128) 作为签名的,它通过魔改客户端,把客户端里的签名校验给去除了,从而实现运行。

我不知道怎么把客户端里的签名校验给去除掉啊!

几乎判定为寄。

后来突然想到是不是可以考虑把客户端里的那个 RSA 公钥给换掉。

把 apk 按 zip 解压,找了一圈,感觉 libGame.so 这个文件最可能是目标。找了个 arm64-v8a 的 libGame.so,丢进 IDA64 里。这个文件只有大概 5M,比 AS 的那个 il2cpp 小多了。IDA 好像没几分钟就跑完了。打开字符串窗口,搜了一下,果然搜到了这个 RSA 公钥。

那么就来自己生成一对公私钥吧。怎么搞都百度了好久,其实以前我应该是搞过的,不过早忘了。

现在还不会打包 apk。干脆从模拟器上把已安装的那个 so 拷贝出来,直接改它。欸我模拟器居然是 x86 的嘛,幸亏机智,刚才没直接对 armeabi-v7a 的 so 动手,动了也白动。

IDA 改公钥好麻烦,百度了一下没发现可以直接贴文本的改法,只能 HEX 挨个手敲。几百位欸,好麻烦。

改完了甚至怎么保存都需要百度。默认保存只保存 IDA 的分析库,不会动原本的 so 文件的。那个菜单藏得好深。

改完了拷贝回模拟器里,打开游戏,居然黑屏。

难道非得从 apk 打包那里开始走起嘛……

fecent 的作者 2 月初公开了他的 root 版安装包制作工具。或许可以用那个工具帮着打包。

那居然是个 VB6 的项目……

过了好久我突然想到再去模拟器里放 so 的那个目录下看一眼。虽然不知道有什么问题。

去一看还真看出问题来了,ES 文件浏览器把 so 文件复制回去之后自动给了 rwxrwx–x 权限,而除了这个 so 文件以外,其它我没动过的 so 文件的权限是 rwxr-xr-x。

g+w 大概没什么问题,但是 a-r 问题可就太大了,这能不黑屏嘛。

改了改文件权限,果然就能正常看到 bushimo 了。

既然 RSA 的问题解决了,consumerKey 那个方案应该也可以不用了。我们就直接按原本正常的账号密码去处理登录流程就是了。

然而还有问题。虽然我们现在取得了很大进展,我们还不知道服务器响应里的那个签名到底是怎么算的。我拿真实抓包记录试着算了一下,算不出对的签名来。

2 月 18 日完。

2 月 19 日我灵机一动,上 GitHub 搜索关键字「x-message-code」。搜到一些结果,虽然对解决现在的问题帮助不太大,但是至少表明结果相关度很高,基本上都是 SIF 的东西,没什么无关结果混在里面。

关键字再用「x-message-sign」,好家伙搜到了 Salaron/alay。基于 ts 的另一个私服项目,进度比 npps 快得多,目测至少到 6.x 甚至 7.x 版本了。

为了以后能跟 eis 主站结合,我决定还是继续基于 php 的 npps 魔改。ts 项目只作为阅读参考。

Salaron 这个名字好像有点眼熟。看了下哦原来 gris 就是他写的。gris 去年对我帮助很大。

阅读后知道签名是怎么算的了。整个请求体再加上 code 放一起丢去 RSA 签名。结果看上去非常简洁,不过让我自己想我是绝对想不出来。IDA 试着翻了下签名校验那附近的代码,结果根本看不懂。多亏了 alay。

现在客户端不会再在 login/login 直接拒绝服务器响应了,可以进入到下一步了。user/userInfo 没出事,gdpr/get 直接 403 了,NPPS 里没有这个 module。

gdpr 这个字眼最近挺敏感的。我倒搜了一下过往的抓包记录,这个应该是在 21 年 3 月的版本(那就是 9.0 了吧)里新增的,其中包含一个 is_eea 字段。is_eea 最近这个月炸雷了,影响了不少人。

那就该补的补呗。

最后这段故事很痛苦,有意思的话题不多,不想回忆了。

总之到 2 月 20 日午饭前,终于能够看到游戏内主页了!

Road to eisIF Prilo (1) ここまで。

最核心的技术验证已经完成,证明了我们有能力把这个项目做出来。那么就继续做吧。

思考了一下后续数据库设计。

一方面,发放给客户端的那堆 db_,服务器这边肯定也得有一份。

然后我打算跟 npps 不一样,彻底大改它的数据库。

一类是纯游戏配置方面的数据,比如卡池配置,这些我打算用 SQLite,不用 MySQL 了。这样方便随时更新替换,一个文件复制粘贴总比跑一堆 sql 脚本方便了,sql 脚本光准备都得累死。

另一类是与玩家游玩状况相关的数据,这些我打算再分成两类。

一类是结构比较简单的,能用几个字段就描述清楚的东西,比如玩家名,等级。这些放在 MySQL 里,毕竟速度快多了。

另一类是结构复杂的,甚至适合一个玩家一张表的。虽然他们两个参考项目都丢 MySQL 里,我觉得还是拿 SQLite,一个玩家一个库。看着清净。我不喜欢一个 MySQL 数据库里上千张表。想想就觉得难受,浑身不自在。

这个就先计划到这里。

Leave a comment

Your email address will not be published. Required fields are marked *