DMM叫反叛的吉尔伽美什,最近关服了。发一下之前分析的过程及结果,不过也忘了挺多就随便写写了。
1、settings.bytes
抓包发现的疑似数据包的可疑文件只有settings.bytes,就暂时锁定他了。
打开查看十六进制,非常杂乱,一般就是加密或压缩了。看来还要先解密。
拖入ida,然后先看看il2cppdump的dll,经验锁定一个 FY.Settings 的类库,直接看加载部分的方法 LoadFromSerializeFileWithoutCallFinished(方法1) 。
这个方法直接就是解密解压加反序列化,当时以为这就完了,没想到后面还有活。
2、解密
简单追上下文知道3DES加密,但是key和iv是通过一个字符串生成的我叫它secret,大概就是对secret取sha256hash,然后对hash进行base64编码,然后前面做key,后面做iv,但是静态编译并没有找到secret,动态hook一下就好了。
这样key和iv就出来了,然后解密。
3、解压
解密完成了,看看有没有压缩。打开查看十六进制,比较杂乱,有部分明文但不完全连续,一般就压缩了。
再解压,通过方法1可以看出是lz4流式解压。应该很简单吧。结果lz4库解压失败。有追上下文看了下解压代码,好像是自己封装了一层的lz4,简单复现一下(其实偷懒丢ai自己调了),大概就是:
压缩格式
数据流由一个或多个独立数据块连续拼接组成,块与块之间无分隔符,循环读取数据块直到读取到文件末尾。
每一个数据块分为两种类型,由头部控制标记区分:
类型 A:LZ4 压缩数据块
- 先读取一个变长整数 VarInt,作为控制标记;标记最低二进制位为 1,代表当前块是压缩块。
- 再读取一个 VarInt,记录该块解压完成后的明文总字节长度。
- 继续读取一个 VarInt,记录后面 LZ4 压缩数据的字节长度。
- 按照上一步读取到的长度,读取对应字节数量的标准 LZ4 裸块压缩数据,解压后得到该块明文。
类型 B:无压缩原始数据块
- 先读取一个变长整数 VarInt,作为控制标记;标记最低二进制位为 0,代表当前块不压缩。
- 再读取一个 VarInt,记录原始数据的字节长度。
- 按照读取到的长度,直接读取对应字节数量的原始明文,无需解压。
4、反序列化
前奏完成了,再打开查看十六进制,文本连贯,依旧可读性低,应该是序列化成二进制文件了(.bytes是二进制文件的后缀,属于是早有预告)。
继续追上下文,然后就大概了解了整个反序列化方法,不过也有很多猜测与瞎蒙。
反序列化
大概步骤就是获取一个反序列化器,先依次反射获取目标数据类的属性,再从二进制中读取一个4字节typecCode,如果和属性的typeCode一样就根据对应类型获取对应的反序列化器反序列化。
因为是反射获取,所以我并不能看出顺序,我本以为是按照硬编码顺序来的,但是结果好像不是,所以只能通过typeCode来还原了。
typeCode生成逻辑也是追上下文,就是用命名空间,类名,字段按照一定格式拼起来取md5hash。不过也有特殊的,枚举的typeCode是按照基础类型来算的,数组类型typeCode是按照元素类型的typeCode+=1.
反序列化器的方法就正常反序列化了。
5、结尾
经过一番折腾,终于是将settings.bytes反序列化为可读的json了,可喜可贺。
下面是保存的DMM的金闪闪的ab包以及反序列化好的数据包链接。ab包就正常没加密。
百度网盘 请输入提取码