《无限暖暖》的资源文件解包(和其他逆向工程)

有用的链接

下面有关 AES Key 的部分仅在Infinity Nikki(国际版)上测试过。

在中国大陆发布的版本目前也有效。

配置 Fmodel(推荐)

请确保使用特定版本的 Fmodel,忽略弹出的任何更新提示。

Archive Directory: 包含 InfinityNikki.exe 的目录

UE Versions: GAME_InfinityNikki

Endpoint Configuration (AES)

感谢LukeFZ创建了这个 API。

Endpoint: https://gacha.lukefz.xyz/infinitynikki/keys

Expression: $['mainKey','dynamicKeys']

配置完成后,在AES界面点击Refresh

Endpoint Configuration (Mapping)

感谢LukeFZ创建了这个 API。

Endpoint: https://gacha.lukefz.xyz/infinitynikki/mappings

Expression: $['url','filename']

提取游戏音频

《无限暖暖》的所有游戏内音频使用 Wwise 的特定格式保存和播放。

使用 Fmodel 提取完整的X6Game/Content/Audio并使用wwiser生成音频事件.txtp

.txtp描述了当一个游戏事件发生时,对应音频的播放逻辑,内容为纯文本。使用vgmstream根据.txtp生成相应的.wav音频。

手动获取 AES Key(TODO)

可以使用Reqable调试游戏客户端的 HTTP 网络活动。

客户端获取 Key 的过程可能与对https://api.infoldgames.com/v1/gameconfig/parameter的请求有关,关键词包含PACDKC

响应格式类似:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "gameConfigParameter": {
    "key": "Windows_PACDKC......",
    "value": "......"
  },
  "ret": 0,
  "request_id": "......",
  "msg": "OK",
  "time": ......
}

解密过程大概是这样的

感谢LukeFZ提供了解密后二进制的反序列化方案:

 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
def read_u32(io: BytesIO):
    return u32(io.read(4))


def read_u64(io: BytesIO):
    return u64(io.read(8))


def read_fstring(io: BytesIO):
    length = read_u32(io)
    return io.read(length).decode().rstrip("\x00")


def read_guid(io: BytesIO):
    # return uuid.UUID(bytes=io.read(16)).hex
    a = u32(io.read(4), "little")
    b = u32(io.read(4), "little")
    c = u32(io.read(4), "little")
    d = u32(io.read(4), "little")
    return (
        int.to_bytes(a, 4, "big").hex()
        + int.to_bytes(b, 4, "big").hex()
        + int.to_bytes(c, 4, "big").hex()
        + int.to_bytes(d, 4, "big").hex()
    )


count = read_u32(f)
entries = [None] * count
for j in range(count):
    guid = read_guid(f)
    name = read_fstring(f)
    key_length = read_u32(f)
    assert key_length == 32
    aes_key = f.read(key_length).hex()

    entries[j] = {
        "enc_guid": guid,
        "name": name,
        "key": aes_key,
    }  # (guid, name, aes_key)

其他或许有用的项目:uasset-parser-py

手动获取 Mapping

请注意:由于游戏(不必要地)使用内核级反作弊,有必要使用针对反作弊的注入/转储工具。

获取用于 umodel 的 AES Key 列表

感谢LukeFZ创建了这个 API。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import requests

# From https://cs.rin.ru/forum/viewtopic.php?p=3082204#p3082204
keys = requests.get("https://gacha.lukefz.xyz/infinitynikki/keys").json()
keys_dict = []
keys_dict.append(keys["mainKey"])

for key in keys["dynamicKeys"]:
    keys_dict.append(key["key"])

with open("keys.txt", "w", encoding="utf-8") as f:
    for key in keys_dict:
        f.write("0x" + key + "\n")

极低可能有用的通用工具

提交: 21d61d6b 环境: production Hugo: 0.145.0 主题: 3.30.0-modified 时间: 1741933297287271 语言: zh-cn
本博客内容仅供参考,作者不对其准确性、完整性或适用性作出任何明示或暗示的保证。因使用、引用或解读本博客内容所引发的任何直接或间接后果,作者概不承担任何责任。
本博客可能包含第三方转载内容,相关版权归原作者所有。转载内容仅为分享信息之目的,不代表作者观点。如涉及侵权,请联系删除。
使用 Hugo 构建
主题 StackJimmy 设计