salala159 发表于 2019-11-15 18:21

某游戏协议分析

周五最后一天上班了,明后休假,写个简单的游戏分析吧!
问我为啥不先分析内存?
    答:游戏框架很简单就是一个未加密未混淆的libil2cpp游戏,dump下来自己找函数就行,没啥难度也没啥意思。毕竟分析游戏只是为了好玩,并没有其他的原因,所以就直接分析协议了。1. 模拟器运行游戏

2. 直接上frida抓包,看看包的情况,游戏里跑跑

开心啊,这不明文跑的游戏吗,这年头很难遇到明文直接跑的游戏了,都不需要解密,不需要反编译了。拿个包给大家分析下包的结构:
: 0x53f5dd8    length: 0x25

0000000023 00 1b 00 53 79 6e 63 50 61 72 74 5f 4f 6e 49#...SyncPart_OnI
0000001073 49 6e 53 63 65 6e 65 52 65 71 75 65 73 74 04sInSceneRequest.
0000002000 08 00 10 00                                 .....
23 00 是整个封包去除23 00 这两个字节的长度。
1b 00 是后面协议字符串的长度,这个协议字符串就相当于MessageID,封包的唯一识别。
53 79 6e 63 50 61 72 74 5f 4f 6e 49 73 49 6e 53 63 65 6e 65 52 65 71 75 65 73 74 是协议字符串。
04 00 是后面字节的长度,其实后面字节才是真正的协议内容。
08 00 10 00 是协议内容

3. 识别协议内容:
如果你有足够经验的话,看到08 00 10 00基本上百分之99%断定是经过protobuf序列化后的协议序列。

08(key)00(value) 10(key) 00(value)
key = field_numer << 3 | wire_type<==> 08 = 01 << 3 | 0,可知wire_type=0属于varint,所以08 00的意思就是第一个字段的值为00,值得类型为varint
同理,10 = 02 << 3 | 0,可知wire_type=0属于varint,所以10 00的意思就是第而个字段的值为00,值得类型为varint
所以08 00 10 00反序列化之后的结果为:


4. 自己写protobuf的反序列化脚本,然后游戏的整个协议就破解完了,脚本的效果如下:

5. 贴上我字节写的脚本:
function hook_sendMessage(addr){
        console.green_log('[+] hook_sendMessage @ '+addr.toString());
    Interceptor.attach(addr, {
      onEnter: function (args) {
                        if (Socket.type(args.toInt32()) == null) return //tcp

                        var address = Socket.peerAddress(args.toInt32())
                        if (address === null) return

                        var package_size = args.readU16()   //获取包长
                        var package_name_size = args.add(0x2).readU16()//获取Message str 大小
                        var package_name = args.add(0x4).readUtf8String(package_name_size)//获取Message str
                        var send_buff = args.add(0x4).add(package_name_size)//定位包体位置
                        var proto_buff = send_buff.add(0x2) //定位protobuf序列的位置
                        var proto_buff_size = send_buff.readU16() //获取protobuf序列长度

                        if(package_name == 'OnHeartPkgRequest') //过滤心跳包
                                return
                        var messages = {}
                        var protobuff_list = byteArray2list(proto_buff.readByteArray(proto_buff_size))
                        decode_protobuf(protobuff_list, 0, protobuff_list.length, messages) //反序列化包字节
                       
                        print_dump(args, args.toInt32())
                        console.yellow_log('Message_name: ' + package_name)
                        console.green_log(JSON.stringify(messages, null, 4))

                        switch(package_name) //修改封包
                        {
                                case 'Backpack_OnUseDaoJu':
                                        var write_messages =
                                        {
                                                "1:0:Varint": 0,
                                                "2:1:Varint": 0
                                        }
                                break
                                case 'Duplicate_OnSaoDangGuanQia':
                                        var write_messages =
                                        {
                                                "1:0:embedded message": {
                                                        "1:0:Varint": 0,
                                                        "2:1:Varint": 5,
                                                        "3:2:Varint": 15
                                                }
                                        }
                                break
                                case 'ChatPart_Speak':
                                        var write_messages =
                                        {
                                                "1:0:Varint": 0,
                                                "2:1:string": "",
                                                "3:2:string": "22222",
                                                "4:3:Varint": 1
                                        }
                                break
                        }
                        if(typeof(write_messages) != "undefined")
                        {
                                var data = []
                                encode_protobuf(write_messages, data) //序列化包字节
                                Memory.writeByteArray(proto_buff, data)
                               
                                //写入长度
                                send_buff.writeU16(data.length) //写入protobuf包长
                                args.writeU16(package_size - proto_buff_size + data.length) //写入package长度
                                args = ptr(package_size - proto_buff_size + data.length + 2) //改写发包长度

                                print_dump(args, args.toInt32())
                        }

      },
      onLeave: function (retval) {
              }
    });
}

6. 通过测试该款游戏的协议,还算是比较安全的,没有什么大的刷道具,刷钱bug,但有些小bug,这里我也不公布,希望大家公平玩游戏,不要去搞破坏。

salala159 发表于 2019-11-19 21:53

7lsu 发表于 2019-11-19 15:55
最近一直卡在google protobuf协议这块,看到楼主帖子豁然开朗,感谢楼主无私奉献,最后想问一下有没有深入 ...

看你的需求了,因为一般游戏不带pb文件只能进行无源反序列化,自己写反序列化代码,就像我上面的例子,还有就是携带pb文件的,那github上公开的源码

salala159 发表于 2019-11-16 08:37

逆向破解游戏是学一种思路,学习别人怎么去开刀一款手游,怎么分析,并不是我告诉你哪里有重大漏洞bug,这样对游戏开发商和游戏的公平性都有很大的影响,不推荐

笑哈哈123 发表于 2019-11-15 18:58

这,网页版的也能破解?不是连接到服务器的吗

salala159 发表于 2019-11-15 19:01

系统管理員 发表于 2019-11-15 18:58
这,网页版的也能破解?不是连接到服务器的吗

谁说网游不能破解,探测服务器漏洞

xubo 发表于 2019-11-15 19:10

好牛啊慢慢看 看能不能搞搞

泥泥的泥泥 发表于 2019-11-15 19:18

网游都可以破解了。厉害

Nevada丶丶 发表于 2019-11-15 20:01

感谢楼主分享!

唐子啊 发表于 2019-11-15 21:14

虽然看不懂。。。。

Angel泠鸢 发表于 2019-11-15 22:24

这是网页的?喔,值得我去学习一下了

zengsheng139 发表于 2019-11-15 22:47

膜拜大佬

哈利菜菜 发表于 2019-11-15 23:35

天书啊
完全看不懂
膜拜大神了
页: [1] 2 3 4 5 6
查看完整版本: 某游戏协议分析