Markthegamer108/RAVage
GitHub: Markthegamer108/RAVage
RAVage 是首个通过逆向工程 Ravensburger tiptoi 固件实现 RAV 音频格式双向转换的开源工具。
Stars: 0 | Forks: 0
# RAVage — tiptoi `.rav` 音频转换器
**English** · [Deutsch](README.de.md)
将**任何音频文件** (mp3, wav, m4a, flac, ogg, …) 转换为
**Ravensburger tiptoi pen (3203L)** 可播放的 `.rav` 文件 — 并支持反向转换。
3203L 使用的 RAV 加密是通过 Thumb-2 反汇编从固件更新文件
(`Update3203L.upd`) 中完全逆向工程得出的。这里的所有内容都是从零开始
实现的,并针对原始笔文件进行了字节级精确验证。据我们所知,这是第一个
可用的实现 — 十年前社区曾将其作为“高强度加密”存档
([issue #115](https://github.com/entropia/tip-toi-reveng/issues/115))。

## 预编译二进制文件
无需 Python — 从 [releases 页面](
https://github.com/Markthegamer108/RAVage/releases) 获取适合你操作系统的版本(或最新 workflow 运行的
artifacts):
| OS | 下载内容 | 备注 |
|----|--------------|-------|
| Windows | `ravage-windows.zip` — `ravage.exe` (GUI) + `ravage-cli.exe` | 双击 `ravage.exe` |
| macOS | `ravage-macos.tar.gz` — `ravage.app` + `ravage-cli` | 未签名,因此首次启动:右键点击 → 打开 |
| Linux | `ravage-linux.tar.gz` — `ravage` (GUI) + `ravage-cli` | 如有需要,请执行 `chmod +x` |
所有依赖都已打包 — ffmpeg、密钥表等一应俱全。二进制文件由
GitHub Actions 构建(参见 `.github/workflows/build.yml`)。
### 已测试的硬件
已在真实硬件上验证:**tiptoi pen gen 2 (3203L)**。其他版本的笔
使用相同的 RAV 文件家族,但它们的固件有所不同 — 密钥表是从笔自身的固件更新中提取的(`data/keytable.bin`
来自 `Update3203L.upd`),因此在信任 gen 3 笔之前,需要根据其自带的原厂文件/固件进行验证。如果你有该设备并想进行
测试,解密器(`rav_cli.py decrypt`)会是你的好帮手。
## 快速开始
### GUI(最简单)
```
run_gui.bat
```
或者
```
python rav_gui.py
```
选择一个音频文件,选择一个输出文件夹,点击 **Convert to .rav**,然后将
结果复制到笔的音乐文件夹中(例如 `E:\songs\My Song.rav`)。
### CLI
```
python rav_cli.py convert "My Song.mp3" -o "My Song.rav"
python rav_cli.py decrypt "Old MacDonald Had a Farm.rav" -o song.ogg # research
```
## 工作原理
`.rav` 文件包含一个 0x20 字节的头部,随后是使用简单的带密钥字节转换加密的 **Ogg Vorbis** 音频
流(单声道,22050 Hz)。笔会忽略 Ogg 页面 CRC,因此密文不需要保留有效的 CRC。
### 头部(20 字节)
| 偏移量 | 大小 | 含义 |
|-------:|-----:|---------|
| 0x00 | 16 | magic `Ravensburgerv03\0` |
| 0x10 | 2 | `value16` (little-endian) |
| 0x12 | 1 | flag,始终为 `0xBE` |
| 0x13 | 8 | key blob: `TABLE[value16 + 3 + i] ^ key8[i]` |
| 0x1B | 5 | trailer,固定为 `4E 6C 31 F2 65` |
### 密钥派生
```
key8 = "CommonI2"
checksum = (sum(key8) + value16) & 0xFFFF # e.g. 0x78 → 0x035C
keystream[i] = TABLE[(checksum + i) & 0xFFF] # 512 bytes
```
4096 字节的 `TABLE` 是固件中固定的密钥表(文件偏移量
`0xDBADC`,内存 `0x008EDADC`),在此打包为 `data/keytable.bin`。
### 主体加密算法
应用于从文件偏移量 `0x20` 开始的 payload:
```
kb = keystream[(pos - 0x20) & 0x1FF]
op = pos & 3
op 0 XOR with pass-through: if c ∈ {0x00, 0xFF, kb} or (c ^ kb) == 0xFF
→ keep c unchanged, else out = c ^ kb
op 1 out = (c - kb) & 0xFF
op 2 out = (c + kb) & 0xFF
op 3 out = c ^ kb (no pass-through rules)
```
直通规则(在固件循环 `0x8DF3B4` 中验证过)的存在,使得与 keystream 的字节冲突永远不会产生可能
干扰解码器的值。编码器完全映射了它们(`op 0`:如果
明文是 `0x00`、`0xFF`、`kb` 或 `kb ^ 0xFF`,则原样输出),使加密
**无损** — 官方笔文件在这些位置是有损的,而我们的可以实现字节级精确往返。
## 破解过程
这是为喜欢探索过程的人准备的简短说明。没有侧信道攻击,也没有泄露的
密钥 — 一切都隐藏在显眼处。
1. **兔子洞。** Wiki 和 issue #115(2015年关闭)宣称 RAV 文件使用了“高强度加密”加密,并流传着密钥可能存在于每支笔特定的芯片中的说法。约10年来无人能破解它。
2. **固件。** Ravensburger 在其网站上直接发布了笔的固件更新
(`Update3203L.upd`) — 这是一个普通的 Cortex-M (ARM
Thumb-2) 镜像,没有任何实质意义上的混淆。
3. **寻找音频路径。** 在镜像中搜索 RAV magic
`Ravensburgerv03` → 定位到 `0x8DFE84` 的音频打开函数,
该函数调用 key generator(`0x8DF380`)和解码循环
(`0x8DF3B4`)。
4. **“秘密”其实是公开的。** key generator 使用 `key8 = "CommonI2"`(固件中的硬编码字符串字面量)以及位于镜像 `0xDBADC` 处的 4096 字节表,构建出 512 字节的
keystream。每支笔出厂都内置了解密密钥 — 从来就没有什么单机密钥。这不是密码学,这只是带有查找表的高级 XOR。
5. **验证,失败,修复。** 最初的尝试在 PC 上实现了完美的往返,但笔拒绝播放我们的文件:keystream 索引是相对于 payload 的(`pos - 0x20`),而 op selector 是绝对值(`pos & 3`)。弄错相位会导致整个流静默偏移。
这个小细节花费了好几天的时间。
6. **怪癖。** 笔忽略了 Ogg 页面 CRC(解码器草率,对我们来说很幸运),并拒绝播放带有庞大 Vorbis 注释头的流 — 约 9 KB 的 YouTube 翻录头会失败,而约 3.6 KB 的原厂大小则可行。
7. **真实硬件。** Old MacDonald。Olchi。Drei Chinesen。全部解密为
`OggS`;自定义文件在笔上播放。大功告成。
## 逆向工程笔记
- 固件:`Update3203L.upd` — Cortex-M (ARM Thumb-2) 镜像;文件偏移量 →
内存 `+ 0x00812000`。
- 音频打开函数:`0x8DFE84`;key generator:`0x8DF380`
(`KEY[i] = TABLE[(checksum + i) & 0xFFF]`,注意掩码仅适用于
`checksum + i`);主体加密算法循环:`0x8DF3B4`。
- `0x8DF2FC` / `0x8DF340` 仅用于初始化/清理 — 与密钥派生无关。
- 原厂文件全部使用 `key8 = b"CommonI2"` 和 `value16 = 0x78`
解密了四个出厂文件;payload 全部为 Ogg Vorbis(单声道 22050 Hz)。
### 重新生成 `data/keytable.bin`
```
from rav_tool import ravcrypto
ravcrypto.extract_keytable(r"C:\path\to\Update3203L (1).upd", "data/keytable.bin")
```
## 依赖项
- Python 3.8+
- PATH 中包含 `ffmpeg` **或者** `pip install imageio-ffmpeg`(捆绑的静态构建版本)
- `numpy`(可选 — 包含纯 Python 回退方案,速度慢约 10 倍)
## 文件
```
rav_gui.py / rav_gui.pyw GUI converter (double-click friendly)
rav_cli.py command-line converter + decryptor
rav_tool/ravcrypto.py the RAV cipher, header build/parse, key table
rav_tool/convert.py ffmpeg wrapper (any format → Ogg Vorbis)
data/keytable.bin key table extracted from firmware 3203L
```
## 许可证
MIT — 可自由使用、学习和分享。
## 社区
本项目建立在
[tip-toi-reveng](https://github.com/entropia/tip-toi-reveng) 社区及其
[wiki](https://github.com/entropia/tip-toi-reveng/wiki) 的基础工作之上。RAV 格式是
最后一个尚未解决的重大难题 — 加入
[tiptoi 邮件列表](https://lists.nomeata.de/mailman/listinfo/tiptoi) 并
打个招呼吧。
标签:GUI, Python, 云资产清单, 嵌入式固件, 文件格式, 无后门, 硬件黑客, 逆向工具, 逆向工程, 音频转换