首页
社区
课程
招聘
[原创]从0到1的某Cocos2dlua手游解包笔记
2023-3-31 17:26 5865

[原创]从0到1的某Cocos2dlua手游解包笔记

2023-3-31 17:26
5865

从0到1的某Cocos2dlua手游解包笔记

本文研究的apk基于2023/03/31的国际服版本。lua文件和图像文件都进行了加密。

 

简单看一下,java层基本没上加密。文件加密估计放在native层。打开ida分析libcocos2dlua.so文件,根据https://bbs.kanxue.com/thread-268574.htm#msg_header_h1_6的指导,定位到cocos2d::Image::initWithImageData(),直接就看到cocos2d::FileUtils::decryptUF(),分析后应该是所有加密文件都由这个函数解密。解密函数就是简单的异或固定的密钥。从ida拉出函数并稍加改写就得到了可用的解密器

 

(由于是直接复制ida的函数,不大好看)

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include<iostream>
#include <cinttypes>
#include <fstream>
#define _BYTE unsigned char
#define _DWORD unsigned int
 
unsigned char key[] =
{
   19911213, 1022234431725,
   88643616146649875644,
   532811,   5, 1163758, 1052015,
   77,   729,   0,   0,   0,   0,   0,   0,   0,
    0,   0,   0,   0,   0,   0,   0,   01991,
   1213, 10222344317258864,
   36161466498756445328,
   11,   5, 1163758, 105201577,   7,
   29,   0,   0,   0,   0,   0,   0,   0,   0,   0,
};
 
unsigned int decryptFile(_BYTE* a1, int a2, _DWORD* a3, _DWORD* a4, _BYTE* a5)
{
    __int64 v5; // x8
    __int64 v6; // x5
    __int64 v7; // x3
    int v8; // w9
    int v9; // w11
    int v10; // w7
    __int64 v11; // x2
    int v12; // w5
    int v13; // w1
    int v15; // w5
    _BYTE* v16; // x8
    int v17; // w12
    int v18; // w11
    int v19; // w3
    char v20; // w6
    unsigned int v21; // w8
    unsigned int v22; // w2
    int v23; // w7
    unsigned int v24; // w7
    int v25; // w3
    _BYTE* v26; // x0
    int v27; // w8
    int v28; // w2
 
    if (a2 <= 3)
        return 0xFFFFFFFFLL;
    if (*a1 != 0x55 || a1[1] != 0x46)
        return 0xFFFFFFFELL;
    if (a1[2] == 0x4F)
    {
        v5 = 4LL;
        *a4 = (unsigned __int8)a1[3];
        v8 = 1;
        v6 = 5LL;
        v7 = 6LL;
        v9 = 4;
    }
    else
    {
        v5 = 2LL;
        v6 = 3LL;
        v7 = 4LL;
        v8 = 0;
        v9 = 2;
    }
    *a3 = (unsigned __int8)a1[v5];
    v10 = (unsigned __int8)a1[v6];
    if (v10 == 1)
    {
        v13 = a2 - 3 - v9;
        if (v13 <= 0)
        {
        LABEL_13:
            *a5 = v13;
            return 0LL;
        }
        v15 = v9 + 3;
        v16 = a1;
        v17 = v13 + v9 + 3;
        v18 = (unsigned __int8)a1[(unsigned int)v7] - v9 - 3;
        do
        {
            v19 = v18 + v15;
            v20 = a1[v15++];
            *v16++ = key[v19 % 33] ^ v20;
        } while (v15 != v17);
        *a5 = v13;
        return 0LL;
    }
    else
    {
        if (v10 != 2)
        {
            v11 = 0LL;
            if (v8)
                v12 = 7;
            else
                v12 = 5;
            v13 = a2 - v12;
            if (v13 > 0)
            {
                do
                {
                    a1[v11] = a1[(unsigned int)(v12 + v11)];
                    ++v11;
                } while (v13 > (int)v11);
            }
            goto LABEL_13;
        }
        v21 = (unsigned __int8)a1[v7];
        v22 = v9 + 3;
        v13 = a2 - (v9 + 3);
        *a1 = a1[v13] ^ key[v21 % 0x21 + 48];
        a1[1] = a1[v13 + 1] ^ key[(v21 + 1) % 0x21 + 48 ];
        a1[2] = a1[v13 + 2] ^ key[(v21 + 2) % 0x21 + 48 ];
        a1[3] = a1[v13 + 3] ^ key[(v21 + 3) % 0x21 + 48 ];
        a1[4] = a1[v13 + 4] ^ key[(v21 + 4) % 0x21 + 48 ];
        if (v9 != 2)
        {
            a1[6] = a1[v13 + 6] ^ key[(v21 + 6) % 0x21 + 48];
            a1[5] = a1[v13 + 5] ^ key[(v21 + 5) % 0x21 + 48];
        }
        if ((int)(v13 - v22) > 95)
            v23 = 95;
        else
            v23 = v13 - v22;
        v24 = v23 + v22;
        if (v22 >= v24)
            goto LABEL_13;
        v25 = v21 + v22;
        v26 = &a1[v22];
        v27 = v21 + v24;
        do
        {
            v28 = v25 % 33;
            ++v25;
            *v26++ ^= key[v28 + 48];
        } while (v25 != v27);
        *a5 = v13;
        return 0LL;
    }
}
unsigned char mydata[1000000] = { 0 };
unsigned char outdata[1000000] = { 0 };
int main(int argc, char* argv[]) {
 
 
    if (argc != 3) {
        std::cerr << "Usage: " << argv[0] << " input_file output_file\n";
        return 1;
    }
 
    std::ifstream infile(argv[1], std::ios::binary);
 
    if (!infile) {
        std::cerr << "Error opening input file.\n";
        return 1;
    }
 
    std::ofstream outfile(argv[2], std::ios::binary);
 
    if (!outfile) {
        std::cerr << "Error opening output file.\n";
        return 1;
    }
 
    if (infile && outfile) {
        infile.seekg(0, std::ios::end);
        std::streamsize size = infile.tellg();
        infile.seekg(0, std::ios::beg);
 
        char* buffer = new char[size];
        if (infile.read(buffer, size)) {
            decryptFile((unsigned char*)buffer, size, (_DWORD*)&outdata[100000], (_DWORD*)&outdata[200000], outdata);
            outfile.write(buffer, size);
        }
        delete[] buffer;
    }
 
    infile.close();
    outfile.close();
    return 0;
}

参数1是要解密的文件,参数2是输出的文件名,处理多个文件可以再写个批处理。解密后至少有3类,png图片,用于生成spine的.pvr.ccz,lua脚本(LuaJit)。

 

想要获取动态立绘,可以用spite软件加载。以应用数据下upgrade\res\common\knight_spine\40008001对应的角色动态立绘为例。首先,同目录下的.png文件替换成用解密器解出来的.pvr.ccz再用texturePackerGUI解出来的同名png。打开spine点左上角的"spine"再选导入数据选40008001.atlas,再点开左上角再选纹理解包器,选同目录对应的.skel解到某个目录。之后右侧的图片选中刚刚解包纹理的目录,不出意外的话就可以看到合成出来cg图片了。

 

右侧的动画可以切换动作,左上角“设置”可以切换为“动画”来预览效果。

 

附:spine是旧版本的v3.8.99,正版的免费试用版不支持旧版,请自行寻找旧的破解版或考虑自行破解

 

解包出来的luajit可以自行反编译。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工 作,每周日13:00-18:00直播授课

收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回