-
-
[原创]从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[] = { 19 , 91 , 12 , 13 , 102 , 22 , 34 , 43 , 17 , 25 , 88 , 64 , 36 , 16 , 14 , 66 , 49 , 87 , 56 , 44 , 53 , 28 , 11 , 5 , 116 , 37 , 58 , 105 , 20 , 15 , 77 , 7 , 29 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 19 , 91 , 12 , 13 , 102 , 22 , 34 , 43 , 17 , 25 , 88 , 64 , 36 , 16 , 14 , 66 , 49 , 87 , 56 , 44 , 53 , 28 , 11 , 5 , 116 , 37 , 58 , 105 , 20 , 15 , 77 , 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可以自行反编译。