本文相关轮子: proto读取导出 | 定时任务本体 | 数据库对比主数据
A python + libil2cpp.so(Android) dumper:
Article: Generating Proto File For Banddream | DNARoma
因为上周的一个想法,写出了redive和cgss的master数据库diff库,还造了一波bundle解压的轮子
redive第三个号被制裁了,平稳科技流也开始查了
土豆的内容星月大佬写离线的时候已经表示过master不可行,都是登录时返回部分master,所以就可以放弃了
然后就是bang了,开搞前我还又去debug了一下,结果看到密钥瞬间才想起power之前给我说过
炸梦通信是protobuf,包括master和manifest也是,所以就必须要提取原始关系才行了。我刚开始还想尝试暴力提取数据,但是内结构和字符串都是wireType 2,没法区分
在之前我已经在Prefare博客上看到过说dump属性什么的,dnspy改了之后也没搞懂怎么看。后来在评论区看到回复才找到
Perfare:邦邦用的是protobuf-net不是Google.Protobuf,不需要.proto文件,你只需要找到带ProtoContractAttrbuit的类并还原出properties上ProtoMemberAttribute的值就可以序列化了
知道入口后就开始观察了,搜索了一波然后看到master数据的类是SuiteMasterGetResponse(基本上各种proto类都叫GetResponse)
观察了下结构之后就开始写proto dumper了,以SuiteMasterGetResponse为入口,循环匹配含有 [ProtoMemberAttribute] 的成员,然后匹配子类递归。匹配中就需要特殊处理,比如xxxx[]就是一个数组,List`<xxxx>也是一个数组,Dictionary`2<xxx, yyy>就是键值对,观察了下数据分布这里的结构是
message entries { type1 key = 1; type2 value = 2; }
即key+value形式
名称和类型之类的导出做完之后,就是tag id了。tag id并不是连续数,所以就需要读取原始数据。每个成员的ProtoMember属性都带一个地址,这个地址是一个函数,每个函数看起来都是这样的:
__text:0000000101320B5C sub_101320B5C ; DATA XREF: __const:000000010256B948↓o __text:0000000101320B5C LDR X8, [X0,#8] __text:0000000101320B60 LDR X0, [X8] __text:0000000101320B64 MOV W1, #1 __text:0000000101320B68 MOV X2, #0 __text:0000000101320B6C B ProtoMemberAttribute$$.ctor __text:0000000101320B6C ; End of function sub_101320B5C
可以明显看出是个只有5条指令的函数,而id明显就是第3条,那么就只需要读取出立即数就行了
结果这个可以说是整个读取里面最大的坑了。因为某种优化,编译器输出的立即数赋值指令有两种:
movk Rd HALF xx1x 0010 1xxi iiii iiii iiii iiid dddd orr Rd_SP Rn LIMM x01x 0010 0Nii iiii iiii iinn nnnd dddd
movk指令的话,很简单,匹配中间的i位就可以了
但是orr整整卡了我两个小时,在群里大佬stat带领下才逐渐搞明白读取方法
简单讲,从第二字节开始,第一位为0,第二位N表示是否64位,3-8位为immr——旋转数,第三字节1-6为imms——大小数。此处nnnnn恒为11111,表示W31寄存器,即WZR,或读0。立即数结尾imms位设置为1,然后循环右移immr位,即为所读立即数
function readInst($inst) { $chk = $inst & 0x6f800000; if ($chk == 0x42800000) { $imme = ($inst & 0x1fffe0) >> 5; $dest = $inst & 0x1f; //echo "\tmov \tW${dest}, #${imme}\n"; return $imme; } else if ($chk == 0x22000000) { $N =($inst & 0x400000) != 0; $immr =($inst & 0x3f0000) >>16; $imms =($inst & 0xfc00) >>10; $dest = $inst & 0x1f; if ($N) { $size = 64; } else { $size = 32; $mask = 0x20; while($size > 1) { if (($imms & $mask) == 0) break; $size /= 2; $mask /= 2; } } $S = $imms+1; $pattern = bindec( str_repeat(str_pad(str_repeat('1', $S), $size, '0', STR_PAD_LEFT), 32 / $size)); $imme = rotate($pattern, $immr); //echo "\torr \tW${dest}, W31, #${imme}\n"; return $imme; } else { echo decbin($inst)."\n"; throw new Exception('unknown'); } }
这里本来imms还表示pattern大小,但其实也没做出来,因为都是32位的pattern。
搞定tag id读取之后就是一路爽了,稍作修改就能输出规范proto。导出代码gist
造完这么个奇怪的轮子之后又得继续造读取轮子。现有的开源实现没找到能快捷转换protobuf到php数组的,估计也是php类型太少会导致格式信息丢失吧。拿出之前读b站弹幕的轮子改了改,换了最近写的FileStream轮子读取内容(Utaha:你要记住自己的本质是一个轮子工(),读完之后再进行字典键值对处理,最后又一个自定义json输出美化的轮子
高潮来了
写完逻辑代码后跑了一遍,但是我在第二遍才加了core.filemode=false,一堆mode change,于是…
一 秒 爽 到
当场恢复了message导出代码,但是主逻辑不见了。今天早上花了一上午grep /dev/sdb1恢复(msys2竟然有这个设备映射),但是p都没找着。于是,又重新造了一遍自己刚造完的轮子(Utaha:你要记住自己的本质是一个轮子工()
这次记录的基本上就这些,还是要感谢各位前辈造好的轮子,我才能这么爽到(x
不明覺厲,最近再研究sdorica逆向,搜遍有關global-metadata解密的信息都很少,搜尋引擎剛好顯示這篇有關bang dream也來觀摩觀摩下
global-metadata 去 https://github.com/Perfare/il2cppdumper 就行了