【公式ゲーム】ミナシゴノシゴトR|キャラ・ストーリー・X(旧Twitter)
许久没摸鱼了,今天突然看到想试试。
简单抓包看到config.json,不过文件数量太小了很明显不完全。
再找了会就可以找到resource.json,看了看文件是base64+aes加密的。
因为是cocos2djs的,找到index.json分析
找decrypt可以直接找到
\\\\\\\\\\\\\\\_decrypt
_decrypt: function(t) {
var e = CryptoJS.SHA256(“密钥"), i = CryptoJS.enc.Base64.stringify(e).substr(0, 32), n = {
iv: “IV",
mode: CryptoJS.mode.CTR
}, o = CryptoJS.AES.decrypt(t, i, n);
return JSON.parse(CryptoJS.enc.Utf8.stringify(o));
}
});
然后base64+aes解密就行(不过文件用了个openssl的Salted需要适配一下,模式与py的ctr有点出入)
解密出来大概是这样的
resource
{
"version": "2.6.4",
"sizes": {
"0": 8399355112,
"1": 1850563607,
"3": 5870036352
},
"assets": {
"coscli": {
"0": {
"md5": "66bb737e68efcb8aef88f882fe48d1d0"
}
}
},
"searchPaths": [],
"commitHash": "d915065d470eef363680d288fcd243a29704d852"
}
然后看读取下载
在downlad里面
\\\\\\\\\\\\\\\_download
_download: function(t, e, i, n, o) {
var s = this._manifest.getManifestData(t);
if (s) {
if (n) {
var r = this._convertToMD5Path(s);
n.push(a.makeLocalStoragePath(r));
}
this._downloadAsset(s, e, o);
} else {
n && n.push(t);
this._downloadUrl(t, a.makeStoragePath(a.makeAssetPath(t)), !1, e, o);
}
},
大概就是各种取md5然后下载什么的,这里给出拼接下载的脚本。
拼接下载
import json
import hashlib
import os
import requests
from urllib.parse import urljoin
import sys
def l(t):
"""首字符0/1/2/3对应的分片规则"""
return [t[0:2], t[4:6]]
def d(t):
"""首字符4/5/6/7对应的分片规则"""
return [t[2:4], t[6:8], t[0:2]]
def p(t):
"""首字符8/9/a/b对应的分片规则"""
return [t[4:6], t[0:2], t[6:8], t[2:4]]
def h(t):
"""首字符c/d/e/f对应的分片规则"""
return [t[6:8], t[2:4], t[4:6], t[0:2]]
split_map = {
'0': l, '1': l, '2': l, '3': l,
'4': d, '5': d, '6': d, '7': d,
'8': p, '9': p, 'a': p, 'b': p,
'c': h, 'd': h, 'e': h, 'f': h
}
def f(md5_str):
if not md5_str or len(md5_str) < 8:
return ""
first_char = md5_str[0].lower()
if first_char not in split_map:
return ""
parts = split_map[first_char](md5_str)
parts = [p for p in parts if p]
return "/".join(parts)
def convert_md5_path(asset_path, asset_md5):
if not asset_path or not asset_md5:
return ""
full_path_md5 = hashlib.md5(asset_path.encode('utf-8')).hexdigest()
if '.' in asset_path:
no_ext_path = asset_path[:asset_path.rindex('.')]
ext = asset_path[asset_path.rindex('.'):]
else:
no_ext_path = asset_path
ext = ""
no_ext_md5 = hashlib.md5(no_ext_path.encode('utf-8')).hexdigest()
split_path = f(no_ext_md5)
final_md5_part = f"{full_path_md5}/{split_path}/{asset_md5}{ext}"
final_md5_part = final_md5_part.replace("//", "/").rstrip("/")
return final_md5_part
def parse_manifest_and_download(manifest_path, base_url):
try:
with open(manifest_path, 'r', encoding='utf-8') as f:
manifest_data = json.load(f)
except FileNotFoundError:
print(f"❌ 错误:未找到manifest文件 {manifest_path}")
return
except json.JSONDecodeError:
print(f"❌ 错误:{manifest_path} 不是有效的JSON文件")
return
version = manifest_data.get("version", "")
if not version:
print("⚠️ 警告:manifest中未找到version字段,可能导致下载失败!")
else:
print(f"✅ 解析到manifest版本:{version}")
assets = manifest_data.get("assets", {})
output_list = []
for asset_path, versions in assets.items():
# 优先选3版本,无则选1,最后选0
if "3" in versions:
selected_version = versions["3"]
elif "1" in versions:
selected_version = versions["1"]
elif "0" in versions:
selected_version = versions["0"]
else:
print(f"⚠️ 警告:{asset_path} 无可用版本,跳过")
continue
asset_md5 = selected_version.get("md5", "")
if not asset_md5:
print(f"⚠️ 警告:{asset_path} 无MD5值,跳过")
continue
md5_path_part = convert_md5_path(asset_path, asset_md5)
if not md5_path_part:
print(f"⚠️ 警告:{asset_path} 生成MD5路径失败,跳过")
continue
if version:
full_url = urljoin(base_url + "/", f"{version}/{md5_path_part}")
else:
full_url = urljoin(base_url + "/", md5_path_part)
output_list.append({
"url": full_url,
"path": asset_path
})
print(f"📌 生成URL:{full_url} → 对应路径:{asset_path}")
# 保存output.json
with open("output.json", 'w', encoding='utf-8') as f:
json.dump(output_list, f, ensure_ascii=False, indent=4)
print(f"\n✅ 已生成output.json,共{len(output_list)}个资源")
#下载资源
download_dir = "downloaded_assets"
os.makedirs(download_dir, exist_ok=True)
print(f"\n📥 开始下载资源(保存到 {download_dir})...")
success_count = 0
fail_count = 0
fail_list = []
for item in output_list:
url = item["url"]
asset_path = item["path"]
save_path = os.path.join(download_dir, asset_path.replace("/", os.sep))
os.makedirs(os.path.dirname(save_path), exist_ok=True)
# 下载资源
try:
print(f"\n正在下载:{url} → {save_path}")
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
}
response = requests.get(url, stream=True, timeout=60, headers=headers)
response.raise_for_status() # 抛出HTTP错误(4xx/5xx)
# 写入文件
with open(save_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
print(f"✅ 下载完成:{save_path}")
success_count += 1
except requests.exceptions.RequestException as e:
error_msg = f"❌ 下载失败:{url} → {str(e)}"
print(error_msg)
fail_count += 1
fail_list.append({
"url": url,
"path": asset_path,
"error": str(e)
})
print(f"\n📊 下载完成统计:")
print(f"总资源数:{len(output_list)}")
print(f"成功:{success_count}")
print(f"失败:{fail_count}")
if fail_list:
with open("download_fail.json", 'w', encoding='utf-8') as f:
json.dump(fail_list, f, ensure_ascii=False, indent=4)
print(f"❌ 失败资源列表已保存到 download_fail.json")
if __name__ == "__main__":
manifest_path = input("请输入manifest文件的路径(如./manifest.json):").strip()
base_url = "https://minasigo-no-shigoto-pd-c-res.orphans-order.com"
parse_manifest_and_download(manifest_path, base_url)
有部分额外文件下面也有回答了。decrypt的参数原来是token。。。