首页
论坛
课程
招聘
[逆向分析] [原创]游戏协议大道-筑基篇
2021-2-23 16:14 3351

[逆向分析] [原创]游戏协议大道-筑基篇

2021-2-23 16:14
3351

上文的结尾说过,游戏数据交互是靠另一套协议来传输的。那么本篇章则开始分析该游戏协议。
感谢wmsuper

00.抓包

通过抓包发现,包的可读性变差了。但看着像是序列化后的json。
其中的参数都是上一篇所见识到的。

00.0.印证

前面说到,这些数据像是序列化后的json。印证了一下,确实如此。
前六个字节为包头,随后四字节表示长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def DecodeData(Data,IsSend,ShowLog):
    Len=0
    index=0
    Maxlen=len(Data)
    b_msg=b''
    Res=[]
    while index<Maxlen:
        ZmFlag=Data[index:index+2]
        index+=6
        Len=Data[index:index+4]
        index+=4
        Len=int().from_bytes(Len, byteorder='big', signed=True)
        if index+Len<=Maxlen:
            b_msg=Data[index:index+Len]
            index+=Len
            msg=msgpack.unpackb(b_msg,raw=False)
            Res.append(msg)
            if ShowLog:
                print(msg)
            Len=0
        else:
            break
    return Res


当我以为这就修道结束的时候,发现后面发送的包居然无法正常解析,显然是加密了。
但发现每个加密的包都会有'cr'两个字符是不变的。经过一些简单的测试都无法解密,那么就直接对程序进行拆包分析了。

01.拆包

将apk解压后查看lib目录发现信息如下
libil2cpp.so
libtolua.so
libunity.so

C#+u3d+lua,经典游戏开发套路。
当我满心欢喜使用Il2CppDumper对il2cpp进行提取后,啥有用信息都没得。此时fk游戏团队!
把il2cpp拉进ida加载Il2CppDumper给的签名(足足44mb),加载大概半个钟,啥有用信息都没得。再次fk游戏团队!
难道游戏逻辑靠lua?
在assets目录下有Lua_Android这么个文件夹引起了我的注意

里面unity3d结尾的都是unity打包的文件,不过头部都多了11个字节出来。

01.1 导出lua

首先清掉文件开头多出来的11个字节保存

然后用AssetStudio加载对应的文件夹32bit_out

嚯,好家伙。看了一下,半喜半忧。

01.2 解密lua

经过一番寻找,终于在il2cpp.so里找到了lua解密的函数。但没想到这么简单!!!
密钥为:'m71'

1
2
3
.bss:036D6D18 StringLiteral_14090 % 4                 ; DATA XREF: LuaEncryption$$Encrypt+5C↑o
.bss:036D6D18                                         ; LuaEncryption$$Encrypt+68↑o ...
.bss:036D6D18                                         ; m71

01.2.1 解密luac


但是解出来的是luac,还得用工具把他反编译成lua

01.2.1 反编译luac

使用luajit-decompiler对解密的luac进行批量反编译
下载地址:https://gitlab.com/znixian/luajit-decompiler

1
python ./main.py --recursive ./xy_luac --dir_out ./xy_lua_d --catch_asserts  -e  luac


完成后,大部分都能反编译成功

02.分析

抓包的时候我们说过,进入游戏后发送的包是加密的。那么我们现在要做的就是找到加密的地方看看算法
通过加密的包里cr字符串的定位可以很幸运的找到了加密的地方

首先是key的来源,看起来是一个时间戳+"000000"来生成的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
local key = MISC_MGR.lst()
 
function lst()
    return _last_sync_time
end
 
function sync_server_time(time)
    _last_sync_time = tostring(time) .. "000000"
 
    logger:info("SYNC server time %f", time)
 
    _time_diff = time - UnityEngine.Time.realtimeSinceStartup
end
 
return function (rid, server_id, time, start_server_time)
    logger:info("玩家(%s)登录成功,时间=%d", rid, time)
    REMOTE_MGR.set_local_server_id(server_id)
    MISC_MGR.sync_server_time(time)
    MISC_MGR.set_start_server_time(start_server_time)
    LTSDK_MGR.login_report()
    AUTHSERVER_MGR.disconnect()
    LTSDK_MGR.sync_last_request()
    WINDOW_MGR.hide_wait()

都没有明确的值来源,服务端要想解密要么本地发送密钥或者服务端下发密钥,这时就得看看有没有什么可疑的数据包了
在顺着第一个被加密的包往上找,终于让我找到了一个相关的

反序列化后数据为:

1
['sync_server_time', [1614052775]]

接着是加密的算法,时间戳+6个0刚好16位,难道是aes?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
msg = "cr" .. cry.encrypt(key, msg, 16, 1)
 
function encrypt(n, s, h, r, d)
    assert(n ~= nil, "Empty password.")
    assert(n ~= nil, "Empty data.")
 
    local r = r or CBCMODE
    local h = h or AES128
    local l = {
        string.byte(n, 1, #n)
    }
    local u = util.padByteString(s)
 
    if r == ECBMODE then
        return ciphermode.encryptString(l, u, ciphermode.encryptECB, d)
    elseif r == CBCMODE then
        return ciphermode.encryptString(l, u, ciphermode.encryptCBC, d)
    elseif r == OFBMODE then
        return ciphermode.encryptString(l, u, ciphermode.encryptOFB, d)
    elseif r == CFBMODE then
        return ciphermode.encryptString(l, u, ciphermode.encryptCFB, d)
    elseif r == CTRMODE then
        return ciphermode.encryptString(l, u, ciphermode.encryptCTR, d)
    else
        error("Unknown mode", 2)
    end
end
AES128 = 16
AES192 = 24
AES256 = 32
ECBMODE = 1
CBCMODE = 2
OFBMODE = 3
CFBMODE = 4
CTRMODE = 4
 
aes = e(function (n, ...)
    local s = util.putByte
    local h = util.getByte
    local r = "rounds"
    local d = "type"
    ...

应该不会是魔改的aes吧?

经过测试,标准的aes_ecb加密。

03.筑基

最终展示如下,登录成功把已登录的挤掉线!

多的不敢写,怕封号。

04.结尾

筑基失败,当场飞升了。

End


看雪学院推出的专业资质证书《看雪安卓应用安全能力认证 v1.0》(中级和高级)!

最后于 2021-2-23 16:17 被零加一编辑 ,原因:
收藏
点赞3
打赏
分享
最新回复 (8)
雪    币: 6777
活跃值: 活跃值 (4736)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
wmsuper 活跃值 3 2021-2-23 16:23
2
0
tql
雪    币: 682
活跃值: 活跃值 (2302)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
supperlitt 活跃值 2021-2-23 17:30
3
0
tql
雪    币: 1350
活跃值: 活跃值 (244)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
眯着眼睛 活跃值 2021-2-23 17:49
4
0
tql
雪    币: 1198
活跃值: 活跃值 (306)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Dyingchen 活跃值 2021-2-23 18:31
5
0
tql
雪    币: 4848
活跃值: 活跃值 (1009)
能力值: ( LV15,RANK:625 )
在线值:
发帖
回帖
粉丝
无名侠 活跃值 10 2021-2-23 21:43
6
0
什么时候元婴
雪    币: 6548
活跃值: 活跃值 (4406)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
pureGavin 活跃值 2 2021-2-23 21:57
7
0
感谢分享,筑基强者恐怖如斯
雪    币: 444
活跃值: 活跃值 (586)
能力值: ( LV7,RANK:113 )
在线值:
发帖
回帖
粉丝
零加一 活跃值 2021-2-23 23:26
8
0
无名侠 什么时候元婴
筑基失败灵根废了。修不了了
雪    币: 1892
活跃值: 活跃值 (220)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
JusticeTitan 活跃值 2021-2-26 21:12
9
0
好家伙~
游客
登录 | 注册 方可回帖
返回