> For AI agents: the complete documentation index is available at https://renmori.mewbaka.cn/llms.txt, the full documentation bundle is available at https://renmori.mewbaka.cn/llms-full.txt.

# 架构

RenMori 由一套**构建期工具**与一个**原生运行时核心**组成。它以已构建完成的 Ren'Py 8.x（Python 3）发行版构建为输入，输出受保护的发行版构建，无需改动任何游戏源码。

原生启动器只是四个层级之一，其余三个层级（资源、脚本、引擎）才是保护的主体所在。

## 全局首要原则：性能优先

任何与运行时性能冲突的保护手段都会被舍弃或削弱，绝不反过来牺牲性能。具体而言：

- **默认不使用重型加壳或虚拟化保护**（VMProtect、Themida 等）：它们会拖慢启动，在 Proton/Wine（Steam Deck 与 Linux）下无法正常工作，容易触发杀毒软件误报，并干扰 macOS 公证
- **热路径上不进行任何反调试或自校验**：完整性校验仅在启动时执行一次
- **只使用对称、硬件加速的加密算法**（AES、AES-NI）：解密以原生代码（Rust 或 C）实现，绝不使用 Python 循环
- **由多态承载保护**：多态是一种零运行时开销的构建期变换，整体策略是“无法批量破解”，而非“在运行时设防”
- **验收线**：受保护的构建在帧时间、场景切换以及 CG 与视频播放上与未受保护的构建无从区分；若某项手段无法满足这一要求，舍弃的是该手段，而非性能

## 四个层级

### 1. 资源（包括字体在内的每一个文件）

- `game/` 下的所有文件（图像、音频、视频、字体、`.rpyc`、`tl/`、JSON 等）均被封入一个**自定义加密容器**，完全取代 RPA 封包格式；每个文件拥有各自的密钥与完整性标签
- 运行时由一个原生模块接管 `renpy.loader` 的文件打开与索引入口，返回**已解密的流式**文件对象；字体（FreeType）、视频（ffmpeg）与音频均经由加载器读取，因此单一入口即可覆盖全部资源
  - **实现任务**：审计每种资源类型的读取路径，确认其均经由 `renpy.loader`，并为任何绕过它的路径补充钩子
- **按偏移量进行流式随机访问解密**，视频与音频从不整体解密到内存中；为此容器格式必须支持按偏移寻址
- **解密结果复用 Ren'Py 既有的缓存**（纹理、音频），同一图像不会被重复解密

### 2. 脚本（源码混淆）

- 输出的**不是**标准 `.rpyc`，而是采用自定义 AST 序列化，其类名映射、字段顺序与结构布局**逐构建随机化**，使依赖伪造 `renpy` 模块、基于 pickle 的反编译器没有任何稳定的绑定目标
- Ren'Py 脚本中内嵌的 Python（`init python`、`$`）在构建期编译并加密，仅在运行时为执行而解密，调试元数据（源文件名、行号）可选择剥除
- 对白与字符串以加密形式存储，仅在使用时解密

### 3. 引擎

- `renpy/` 引擎代码与 Python 标准库一并封包加密，并在启动时进行防篡改的完整性校验
- **白名单加载**：仅加载构建期签名的脚本与模块，放入游戏目录的游离 `.rpy` 或 `.rpyc` 文件一律拒绝，从而阻断 `un.rpyc` 式的注入反编译器
- 发行版构建禁用开发者控制台、开发者模式以及导出数据的 CLI 子命令（compile、lint、dialogue export 等）

### 4. 原生启动器（外壳）

- 以分平台的原生启动器取代 Ren'Py 自带的启动器：它引导内嵌的 Python，从加密容器加载引擎，并**在运行时派生密钥**，磁盘上不留任何明文密钥
- 以 Rust 或 C 编写，同时编译为启动器与 CPython 扩展模块，使核心逻辑位于原生代码而非 Python 之中

## 逐构建多态（核心价值）

每次构建都会随机化容器格式参数、AST 编码映射与密钥派生路径，使通用解包器无从存在。这是一项构建期变换，不增加任何运行时开销。

## 开放核心拆分

划分准则：**任何公开的部分，即使被完全理解，也不得使任何人能够写出一个对所有 RenMori 游戏通用的解包器。**

### 开放部分（本仓库）

- CLI 骨架、配置解析、CI 集成以及 GitHub Actions 示例
- **`renpy.loader` 钩子框架与运行时 SDK**：即接管加载器并将解密后的数据流交还给引擎的管道；将其开源本身就是一项特性，开发者可借此确认其中不存在后门或回传行为，并自行调试兼容性问题
- **基线容器格式 v1 规范**：一种固定、公开、中等强度的格式，见 [容器格式](https://renmori.mewbaka.cn/technical/container-format.md)
- 标准加密原语的使用方式：AES 是公开标准，公开其用法不会带来任何损失
- 文档、示例与测试

开放部分可以单独使用，此时处于基线强度（一种固定、可预测的格式）。

### 闭源部分（预编译二进制插件，已确定）

- **多态 / 变异引擎**：决定每次构建如何随机化容器布局、字段顺序与 AST 类映射；它**仅在构建期运行，绝不分发给玩家**，一旦泄露便会暴露整个变换空间，使攻击者能够编写通用求解器，因此必须保持闭源
- **密钥派生混淆**：密钥最终仍可在客户端派生，但派生与隐藏密钥&#x7684;_&#x65B9;&#x5F0F;_&#x6B63;是抬高逆向成本的关键
- **原生反篡改与密钥恢复运行时**：以不附源码的预编译二进制形式交付；它随游戏一同分发，因此最终仍可被逆向，闭源只是抬高门槛

**交付方式：** 开放的 CLI 在构建期调用闭源的预编译二进制。这一方式适合 CI、可离线运行，并契合免费分发。二者通过一套**稳定且有文档的进程接口**通信（基于 stdin/stdout 的 CLI 调用或本地 IPC：输入为构建产物路径与配置，输出为受保护的产物与构建清单），接口公开而实现不公开，每个平台对应一个二进制，详见 [插件接口](https://renmori.mewbaka.cn/technical/plugin-interface.md)。

此外，运行时可选择只接受由官方闭源二进制签名的构建指纹，使自行编写的弱多态层无法冒充官方实现。

## 抗逆向：如实的模型

前提：一个分发给玩家、且必须在本地解密的二进制，不可能做到无法破解，这就是白盒问题。目标是抬高破解成本、使破解成果无法迁移，并通过轮换方案使旧的破解随时间失效。成功的标准是不存在通用解包器，且每款游戏都需要专门的专家级投入。完整论证见 [威胁模型](https://renmori.mewbaka.cn/technical/threat-model.md)。

两件闭源产物面对不同的威胁模型：

1. **构建期二进制**（多态与密钥派生）：仅在开发机或 CI 机器上运行，**绝不分发给玩家**，因此更易于保护
2. **运行时核心**（随每款发行游戏分发的解密与反篡改模块）：运行在攻击者的机器上，且必须能够解密全部内容，因此完全处于白盒环境

运行时核心的加固措施，按收益排序（并受性能优先原则约束）：

- **全部原生实现，绝不使用 Python**：关键逻辑的任何 `.py` 或 `.pyc` 都会直接暴露实现
- **多态使破解无法迁移**（最重要）：对一款游戏的完整逆向只能得到仅适用于该游戏、该版本的工具
- **密钥绝不是静态常量**：密钥在运行时由多个来源（代码段自哈希、分散的常量、计算所得的值）派生，单次内存转储无法直接得到可用密钥；真正的白盒密码学难度很高，学术构造也屡有被攻破的先例，因此此处不作过度承诺
- **发行版构建剥除符号且不含调试信息**
- **原生混淆**：通过 obfuscator-LLVM（跨平台）实现控制流平坦化、不透明谓词与指令替换，_仅在编译期进行，不引入任何运行时性能开销_
- **自完整性与解密绑定**：若二进制或引擎被修补，密钥派生会得到无效结果而非明确的错误，使补丁更难排查（仅在启动时进行）
- **反调试**：最小化且默认关闭，因为它是一场军备竞赛，误报会触发杀毒软件，且会消耗性能
- **惰性分块解密，用后立即释放**：内存中绝不完整保留一张 CG 或一段视频的明文，这会抬高内存提取的成本，但无法阻止内存提取

兼容性与性能结论（已由性能优先原则确定）：**原生代码、轻量的 obfuscator-LLVM、符号剥除与多态**。重型商业加壳仅作为 Windows 专属、默认关闭的选项，且只有在实测确认对性能无可感知影响时才会启用。

## 平台集成

### Steam

- **允许 Steam 叠加层（Overlay）注入**：叠加层会注入游戏进程，激进的反篡改或反注入措施会将其拦截并破坏商店集成；性能优先原则已使反篡改保持最小化，并明确放行该注入
- **不要与 Steam DRM 双重包装**：Steam DRM 会包装并改写可执行文件，可能与启动时自校验冲突或触发自校验，因此应进行兼容性测试，或建议开发者不使用 Steam DRM，其本身防护也较弱
- 成就与云存档经由 Ren'Py 的 Python API 实现，该 API 未被改动；`libsteam_api` 与 `steam_appid.txt` 仍可正常加载；加密容器为静态文件，因此 Steam 的文件校验与更新均正常工作

发布流程见 [Steam 发行指南](https://renmori.mewbaka.cn/integration/steam-release.md)。

### macOS 签名与公证（假定开发者不进行公证）

- **设计约束：只解密数据，绝不生成或执行解密后的代码**。因此无需 hardened runtime 权限，保护措施也不会迫使开发者购买付费签名；未签名的发行版构建与任何未签名的 Ren'Py macOS 游戏行为一致（通过 Gatekeeper 右键打开）
- **Apple Silicon 至少需要 ad-hoc 签名才能运行**（`codesign -s -`，免费且无需开发者账号）：构建工具默认对其产出的启动器与 dylib 进行 ad-hoc 签名
- 若开发者持有 Developer ID 并希望进行公证，这些二进制仍可重新签名且兼容 hardened runtime，并提供文档化的流程

## 许可

- 全部内容（框架与工具、闭源插件，以及随游戏分发的运行时）均采用 **RenMori License**（[LICENSE](https://github.com/MewBaka/RenMori/blob/main/LICENSE)，中文版 [LICENSE.zh-CN.md](https://github.com/MewBaka/RenMori/blob/main/LICENSE.zh-CN.md)）：可免费用于保护任何 Ren'Py 游戏，包括商业作品；运行时可**以内嵌于发行游戏的形式免费再分发**，开发者因此可以合法销售受保护的游戏
- 不允许的行为：销售 RenMori 本身或其修改版，或以其运营付费加密服务；该许可为源码可得（source-available）许可而非 OSI 开源许可，因为禁止转售的条款无法以宽松许可表达
- 贡献代码须签署 **CLA**，使贡献的代码能够在统一的许可下同时用于框架与闭源插件
- 第三方声明见 [NOTICE](https://github.com/MewBaka/RenMori/blob/main/NOTICE)：**ffmpeg（LGPL/GPL）是主要的合规风险点**，此外还涉及 AES 库与 Ren'Py 自身的许可
- **安全免责声明**：声明任何保护都并非不可破解，并限定游戏被破解时的责任范围，见 [免责声明](https://renmori.mewbaka.cn/license/disclaimer.md)
- **出口与加密声明**：含强加密的软件可能受出口管制约束
- **隐私声明**：不收集任何遥测数据（或如实列出所收集的内容），以消除对加载器钩子回传数据的疑虑

## 里程碑

- **M0**：本设计文档
- **M1**：资源层概念验证（Windows）：加密容器与加载器接管；图像、音频、视频与字体均可加载；`unrpa`、`rpatool`、`RenPack` 与 `RenpyEx` 全部失效；在 CG 与视频密集的样例上，受保护构建与未受保护构建的性能基准对比通过，见 [M1 成果](https://renmori.mewbaka.cn/milestones/m1-results.md)
- **M2**：脚本层与引擎层：自定义 AST 格式、签名白名单加载、发行版锁定以及拒绝注入的脚本，见 [脚本层与引擎锁定](https://renmori.mewbaka.cn/technical/script-layer.md)
- **M3**：单条命令的 CLI 与 CI 集成：输入标准 Ren'Py 构建产物，支持配置文件、退出码、构建清单与 GitHub Actions 示例，见 [CI 集成](https://renmori.mewbaka.cn/integration/ci.md)
- **M4**：逐构建多态；macOS（自动 ad-hoc 签名、可选公证）与 Linux/Steam Deck 启动器；验证 Steam 叠加层、Steamworks 与 Proton/Steam Deck 兼容性，见 [M4](https://renmori.mewbaka.cn/milestones/m4.md)

## 验证标准

- **功能**：受保护的样例游戏能在全部三个平台上运行并完整通关，所有资源与全部剧情内容均正确
- **保护**：`unrpa`、`rpatool`、`RenPack`、`RenpyEx` 与 `unrpyc`（包括 `--try-harder` 与 `un.rpyc` 注入）全部失效，且任何时刻磁盘上都不存在明文文件，字体同样如此
- **性能**：CG 与视频密集的样例相对于未受保护的构建达到验收线
- **可用性与 CI**：在干净的 CI runner 上一条命令即可完成，退出码正确，示例工作流运行通过
