Cocos Creator 2.X(Cocos2d-js)游戏资源目录分析&逆向还原

js注入版筹划中,不咕的话下周可以更新?

拿到了某变态服游戏,打开lib看到了libcocos2djs.so,判断版本为2.4。游戏把资源文件下载到了/data下,因此需要root。目录结构如下:

g4-start/
├── config.json
├── import
│   ├── 1e
│   │   └── 1ea6e4bcd.json
│   └── e7
│       └── e76cb2d2-fa48-4916-8c8c-d7f64ee0c948.json
└── native
    └── 1e
        └── 1ea6e4bcd.png

可以看到,文件名全部被改为了UUID,不利于处理。打开config.json,uuid项里竟然是Base64(但不是)!在cocos2d/core/utils/decode-uuid看到了对应的源码,成功还原uuid,再通过paths项还原目录结构。

话不多说,上完整代码(在资源目录下运行)

#!/usr/bin/python3
# cocos2d-js资源还原
# Date: 2024/2/14
import os
import json
import shutil

# static sheets
# src: cocos2d/core/utils/decode-uuid
i = [64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,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,64,64,64,64,64,64,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]
a = ["", "", "", "", "", "", "", "", "-", "", "", "", "", "-", "", "", "", "", "-", "", "", "", "", "-", "", "", "", "", "", "", "", "", "", "", "", ""]
n = '0123456789abcdef'
s = [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15, 16, 17, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]

def decode_uuid(t):
    if len(t) != 22:
        return t
    
    a[0] = t[0]
    a[1] = t[1]
    r = 2
    for e in range(2, 22, 2):
        o = i[ord(t[e])]
        l = i[ord(t[e + 1])]
        a[s[r]] = n[o >> 2]
        a[s[r + 1]] = n[(3 & o) << 2 | l >> 4]
        a[s[r + 2]] = n[15 & l]
        r += 3
        
    return ''.join(a)

def replace_uuids(json_str):
    data = json.loads(json_str)
    uuids = data.get('uuids', [])
    decoded_uuids = [decode_uuid(uuid) for uuid in uuids]
    data['uuids'] = decoded_uuids
    return json.dumps(data)

if not os.path.exists('config_patch.json'):
    json_str = open("config.json").read()
    json_str = replace_uuids(json_str)
    file = open("config_patch.json", "w")
    file.write(json_str)
    file.close() #Windows需要close

config = json.load(open('config_patch.json'))
# 遍历paths
for key, value in config["paths"].items():
    path = value[0]
    uuid_index = int(key)
    uuid = config["uuids"][uuid_index]
    output_dir = f"output/{path}"
    os.makedirs(output_dir, exist_ok=True)

    # 在import和native两个目录下判断{对应uuid前两位}/{对应uuid}.*
    for root, dirs, files in os.walk("import"):
        for file in files:
            if uuid[:2] in root and uuid in file:
                src_file = os.path.join(root, file)
                dst_file = os.path.join(output_dir, file)
                # 如果output目录已存在同名文件,重命名
                if os.path.exists(dst_file):
                    index = 1
                    while True:
                        new_dst_file = os.path.join(output_dir, f"{file}.{index}")
                        if not os.path.exists(new_dst_file):
                            dst_file = new_dst_file
                            break
                        index += 1
                shutil.copy(src_file, dst_file)

    for root, dirs, files in os.walk("native"):
        for file in files:
            if uuid[:2] in root and uuid in file:
                src_file = os.path.join(root, file)
                dst_file = os.path.join(output_dir, file)
                if os.path.exists(dst_file):
                    index = 1
                    while True:
                        new_dst_file = os.path.join(output_dir, f"{file}.{index}")
                        if not os.path.exists(new_dst_file):
                            dst_file = new_dst_file
                            break
                        index += 1
                shutil.copy(src_file, dst_file)
    #对单文件目录的处理
    files = os.listdir(output_dir)
    if len(files) == 1 and os.path.isfile(os.path.join(output_dir, files[0])):
        file_to_move = os.path.join(output_dir, files[0])
        new_path = os.path.join(os.path.dirname(output_dir), os.path.basename(output_dir) + os.path.splitext(files[0])[1])
        if os.path.exists(new_path):
            index = 1
            while True:
                new_paths = str(index) + "_" + new_path
                if not os.path.exists(new_paths):
                    new_path = new_paths
                    break
                index += 1
        try:
            os.rename(file_to_move, new_path)
        except:
            print(f"诶, {new_path} 无法访问")
        os.rmdir(output_dir)

print("文件复制完成")

补:对于spine JSON文件的提取:

import os
import json
import shutil
import sys

# static sheets
# src: cocos2d/core/utils/decode-uuid
i = [64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,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,64,64,64,64,64,64,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]
a = ["", "", "", "", "", "", "", "", "-", "", "", "", "", "-", "", "", "", "", "-", "", "", "", "", "-", "", "", "", "", "", "", "", "", "", "", "", ""]
n = '0123456789abcdef'
s = [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15, 16, 17, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]

def decode_uuid(t):
    if len(t) != 22:
        return t
    
    a[0] = t[0]
    a[1] = t[1]
    r = 2
    for e in range(2, 22, 2):
        o = i[ord(t[e])]
        l = i[ord(t[e + 1])]
        a[s[r]] = n[o >> 2]
        a[s[r + 1]] = n[(3 & o) << 2 | l >> 4]
        a[s[r + 2]] = n[15 & l]
        r += 3
        
    return ''.join(a)

def parse_json_files(directory):
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith('.json'):
                json_file = os.path.join(root, file)
                with open(json_file, 'r') as f:
                    json_data = json.load(f)
                try:
                 if json_data[3][0][0] != 'sp.SkeletonData':
                     continue
                except:
                 # Wrong format, skip
                 continue
                
                spine_data = json_data[5][0]
                spine_name = os.path.basename(os.path.dirname(json_file))
                
                if isinstance(spine_data[4], dict):
                    with open(os.path.join(root, f'{spine_name}.skel.json'), 'w') as f:
                        json.dump(spine_data[4], f)
                    with open(os.path.join(root, f'{spine_name}.atlas'), 'w') as f:
                        f.write(spine_data[2])
                    png_files = [f for f in os.listdir(root) if f.endswith('.png')]
                    if len(png_files) > 1:
                        print(f'error: 出现多个.png,目录: {root}')
                    elif len(png_files) == 1:
                        shutil.copyfile(os.path.join(root, png_files[0]), os.path.join(root, f'{spine_name}.png'))
                else:
                    with open(os.path.join(root, f'{spine_name}.atlas'), 'w') as f:
                        f.write(spine_data[3])
                    bin_files = [f for f in os.listdir(root) if f.endswith('.bin')]
                    if len(bin_files) > 1:
                        print(f'error: 出现多个.bin,目录: {root}')
                    elif len(bin_files) == 1:
                        shutil.copyfile(os.path.join(root, bin_files[0]), os.path.join(root, f'{spine_name}.skel.json'))
                    png_files = [f for f in os.listdir(root) if f.endswith('.png')]
                    if len(png_files) > 1:
                        print(f'error: 出现多个.png,目录: {root}')
                    elif len(png_files) == 1:
                        shutil.copyfile(os.path.join(root, png_files[0]), os.path.join(root, f'{spine_name}.png'))

parse_json_files('.')

 

posted @ 2024-02-14 18:15  星如雨yu  阅读(1533)  评论(8编辑  收藏  举报