VdustR/game-save-android-halls-of-torment
GitHub: VdustR/game-save-android-halls-of-torment
针对 Godot 引擎开发的 Android 游戏「Halls of Torment」的存档解密、修改和重加密完整指南。
Stars: 0 | Forks: 0
# Halls of Torment — Android 存档数据
一份关于解密、修改和重新加密 [**Halls of Torment: Premium**](https://play.google.com/store/apps/details?id=com.halls.of.torment.paid.gp) (Android) 存档文件的详细指南。
已在版本 **1.0.1152** 及以下 DLC 上测试:
- [Supporter Pack](https://store.steampowered.com/app/3386980/Halls_of_Torment__Supporter_Pack/)
- [The Boglands](https://store.steampowered.com/app/3919420/Halls_of_Torment__The_Boglands/) (添加了 Alchemist, Crone 和 The Boglands 关卡)
预构建的存档文件可在 [Releases](https://github.com/VdustR/game-android-halls-of-torment-save-data/releases) 页面获取。
## 目录
- [概述](#overview)
- [前置条件](#prerequisites)
- [环境设置](#environment-setup)
- [存档文件位置](#save-file-location)
- [存档文件格式](#save-file-format)
- [解密](#decryption)
- [存档文件结构](#save-file-structure)
- [修改规则](#modification-rules)
- [重新加密](#re-encryption)
- [推回设备](#pushing-back-to-device)
- [云同步注意事项](#cloud-sync-considerations)
- [脚本](#script)
## 概述
Halls of Torment: Premium 基于 **Godot Engine 4.6** (`custom_build`) 构建。存档文件使用 Godot 内置的 `FileAccessEncrypted` 以及基于密码的加密 (`save_encrypted_pass`)。
关键信息:
- **存档格式**:Godot GDEC (AES-256-CFB)
- **加密密码**:作为 UUID 字符串常量存储在已编译的 GDScript 字节码 (`Global.gdc`) 中
- **存档内容**:JSON
- **反篡改**:APK 使用 **pairip** 保护,阻止运行时检测(例如 Frida attach)。但这不影响静态分析或存档文件操作。
## 前置条件
- **已 Root 的 Android 设备或模拟器**(访问 `/data/data/` 所需)
- **ADB** 并具有 root shell 访问权限
- **Python 3** 并安装:
- `cryptography` — AES 加密/解密
- `zstandard` — GDScript 字节码解压(仅在提取密钥时需要)
```
pip install cryptography zstandard
```
## 环境设置
### 使用带有 Magisk 的 Android 模拟器
如果您没有已 Root 的设备,请使用 Android Studio AVD:
1. **创建 AVD**,使用 `google_apis_playstore` 系统镜像(推荐 API 34):
sdkmanager "system-images;android-34;google_apis_playstore;arm64-v8a"
avdmanager create avd \
--name "Game_Research" \
--package "system-images;android-34;google_apis_playstore;arm64-v8a" \
--device "pixel_7"
2. **启动** 并启用可写系统:
emulator -avd Game_Research -writable-system -no-snapshot-load
3. 使用 [rootAVD](https://gitlab.com/newbit/rootAVD) **通过 Magisk 获取 Root**:
git clone https://gitlab.com/newbit/rootAVD.git
cd rootAVD
./rootAVD.sh system-images/android-34/google_apis_playstore/arm64-v8a/ramdisk.img
4. 重启后,打开 Magisk 应用 → **超级用户** 标签页 → 为 `[SharedUID] Shell` 启用开关
5. 验证 root:
adb shell "su -c 'id'"
# uid=0(root) gid=0(root) ...
## 存档文件位置
```
/data/data/com.halls.of.torment.paid.gp/files/HoT_progress_profile.dat
```
其他相关文件:
| 文件 | 描述 |
|------|-------------|
| `files/settings.json` | 游戏设置(音量、控制等)— **未加密** |
| `files/HoT_progress_profile.dat` | 主存档文件 — **已加密** |
## 存档文件格式
存档文件使用 Godot 的 `GDEC` 格式 (Godot 4.4+):
```
Offset Size Description
------ ---- -----------
0x00 4 Magic: "GDEC" (0x47 0x44 0x45 0x43)
0x04 16 MD5 hash of decrypted plaintext
0x14 8 Data length (uint64 LE) — original unpadded size
0x1C 16 IV (Initialization Vector) for AES-CFB
0x2C ... Encrypted data (AES-256-CFB, padded to 16-byte boundary)
```
### 密钥派生(密码模式)
Godot 的 `open_and_parse_password()` 按如下方式派生 AES 密钥:
```
password_string
→ MD5 hash → hex string (32 ASCII chars)
→ use ASCII bytes as 32-byte AES-256 key
```
例如,如果密码是 `"hello"`:
```
MD5("hello") = "5d41402abc4b2a76b9719d911017c592"
AES key = b"5d41402abc4b2a76b9719d911017c592" (32 bytes of ASCII)
```
来源:[Godot Engine `file_access_encrypted.cpp`](https://github.com/godotengine/godot/blob/4.4-stable/core/io/file_access_encrypted.cpp#L107-L117)(游戏使用 v4.6 自定义构建;链接的 4.4 源码具有相同的加密逻辑)
### 加密密码
加密密码是嵌入在已编译 GDScript 字节码(APK 资源包内的 `Global.gdc`)中的 UUID 常量。
要自行提取:
1. 拉取包含资源的 APK 分包:
adb shell "pm path com.halls.of.torment.paid.gp"
# 查找 split_assetPackInstallTime.apk
2. 解压并解压 GDScript 字节码:
import zstandard
with open("Global.gdc", "rb") as f:
data = f.read()
zstd_pos = data.find(b'\x28\xb5\x2f\xfd') # zstd magic
assert zstd_pos != -1, "No zstd data found in .gdc file"
compressed = data[zstd_pos:]
decompressed = zstandard.ZstdDecompressor().decompress(
compressed, max_output_size=2*1024*1024
)
3. 提取字符串并查找紧邻 `HoT_progress_profile.dat` 的 UUID 模式 (`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`)。
版本 **1.0.1152** 的密码:**`e4422259-b391-43d3-9284-5f37189420ed`**
## 解密
```
import struct, hashlib, json
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
PASSWORD = "e4422259-b391-43d3-9284-5f37189420ed"
def decrypt_save(filepath):
with open(filepath, "rb") as f:
magic = f.read(4)
assert magic == b"GDEC", f"Not a GDEC file: {magic}"
md5_expected = f.read(16)
data_len = struct.unpack("
标签:ADB, AES-256-CFB, Android游戏, DNS 反向解析, GDEC解密, GDScript字节码, Godot引擎, Halls of Torment, Homebrew安装, HTTP工具, Pairip保护, Python加密, Root权限, 云安全监控, 云资产清单, 存档修改, 存档编辑器, 密钥提取, 数据解密, 游戏修改, 逆向工具, 逆向工程, 静态分析