背景:
月初yumesute季常退坑14天的途中,看到了MYUKKE.发的新歌,看着游戏稍微有点意思,就试了试。
翻资源的途中看到逆天游戏把mono il dll存到TextAsset里面跑游戏代码,和人聊了一下现在ios这样操作似乎是可行的?只要热更新部分不涉及到objc api就可以。
至于研究通信则是因为买了个通行证支持一下之后碰上了个逆天bug,为了搞清楚到底什么情况于是稍微读了读kcp。
首先游戏用的是ET框架,这框架主打一个不管细节怎么样反正能快速搞出一个能运行效果能看的游戏出来。有一说一挺好的,就是mono il的性能实在是过于拉跨,尤其是音游对掉帧和延迟非常敏感,基本体验会明显比il2cpp糟糕。
而他通信部分,自带3种通道,TService、KService、以及WService,分别对应raw tcp、kcp、websocket。顺着TextAsset里面开源的游戏代码就能看到,这游戏本地测试用了TService,而发布版本用了KService。
至今为止我和kcp的唯一交集就是v2ray,而且用了一阵之后不是很好用了,大概从第二年开始至今一直用的是websocket tls。具体碰上需要动手研究的还是第一次。听说原神也是用的kcp,而且上了很复杂的通道加密,具体就不清楚了,只是搜了下kcp抓包搜到了一点博客日志。
KService是kcp的封装,最底层是udp,在一个udp包中有一个KService通信,开头1字节表示包类型,1234分别是连接Conn、连接确认ConnACK、数据结束RecvFIN、数据Data。然后4字节标记会话,用来保持登录状态(这通信也太不安全了……明文随便被拦截重放啊)
function readKServiceBody($data) { $type = ord($data[0]); switch ($type) { case 1: { echo "\t\t\tConnect\n"; break; } case 2: { echo "\t\t\tConnect Ack\n"; break; } case 3: { echo "\t\t\tKService Recv Fin\n"; break; } case 4: { // echo "\t\t\tKService Data\n"; $kcpStream = new MemoryStream($data); $kcpStream->littleEndian = true; $kcpStream->position = 5; //...
5字节过后就是一个或多个kcp包,kcp头的定义就比较好办了,一搜一大堆。而这里面比较大的坑其实是frg,因为很多时候一个rpc的响应是没法放到单个udp里的。平时用http不需要自己操心数据分片,而做kcp抓包的时候就需要自己把多个frg拼起来组合成完整的响应。
数据部分则是protobuf(好像新版框架换掉了),开头2字节uint16和代码里的 [Message(messageId)] 编号对应,然后就直接是protobuf内容。
至此整个通信读取就结束了,剩下的就是把开源游戏的 Message 全部导出到proto给自己的代码读就行了。
搓一个udp代理起来,其实用 WorkerMan 还算挺方便的,除了windows上每个端口要新开一个进程以外,其他方面写起来都很快。第一天写的时候没发现需要自己拼frg,不过用来找那个逆天bug也不是跨越多个包的大响应,很快就定位到了问题。
感觉这种好笑问题都能大概猜一下代码思路是不是这样的:
新记录先初始化,然后for循环领取,领过的跳过
于是单次领取时一类全部正常更新了,另一类都跳过了,最终两个分类轮流循环领。
本来留着准备周二周三如果还没修就报一下bug,不过周一晚上一上线就发现修了,然后周三更是逆天,直接给我号回档了。我还以为会像那几个赛博还贷的游戏一样搞个负数出来让人慢慢还贷呢,那不是好玩多了,笑死
编写的抓包反代代码: https://gist.github.com/esterTion/c163e01a491b96525523750a59d07f2b 并不包含所有的proto和message id对应表,如有需要可以自己去看看开源游戏
好久不写文,水一篇,留作kcp小研究,这个协议也确实不复杂,没有单独做加密的情况下,安全确实是完全没有