Solivault 的歌里藏了什么:一次 SSTV 解码实录

2061 字
10 分钟
Solivault 的歌里藏了什么:一次 SSTV 解码实录

先给结论,细节在后面:

  • Solivault(第九章 ARG 那个倒计时页)在倒计时临近时换了壳,开始播放一段 111 秒的歌,音频是加密下发的。
  • 歌里按节拍藏着一条 SSTV(慢扫描电视)传输:每 0.43 秒一个 1200Hz 同步脉冲。
  • 解码出来是一张 320×256 的角色立绘(黑发、黄绿瞳、白衣黑颈环),大概就是第九章的新曲绘/新角色。
  • 倒放音频里没有第二张图,正放这张就是全部。

配套的完整解谜复盘在另一篇:Phigros 第九章《穹顶孤舟》网页解谜复盘。这篇只讲「声音」这一段。

一、页面变了:先问状态,再决定放不放歌#

页面本体瘦成了几十行的引导脚本:

sound: truesound: falsestate.phpsound / open_at / close_atsong.html会响的播放器nosong.html静音版key.php?n=1P-256 公钥 + nonce + tkplay.php?id=1/2/3AES-GCM 密文分片WebCrypto 解密→ AudioContext 播放
sound: truesound: falsestate.phpsound / open_at / close_atsong.html会响的播放器nosong.html静音版key.php?n=1P-256 公钥 + nonce + tkplay.php?id=1/2/3AES-GCM 密文分片WebCrypto 解密→ AudioContext 播放

实测 state.php 返回:

{"sound":true,"open":true,"sound_enabled":true,"open_at":0,"close_at":1790931600,"now":1790412010}

close_at 换算是 2026-10-02 17<00>,和原来的倒计时一秒不差。也就是说这首歌是倒计时前的预热,到点后应该还会再变一次。

二、音频是加密下发的#

  • key.php?n=1 返回一组会话材料:sid、spki(P-256 公钥)、nonce、tk;
  • play.php?id=1、?id=2、?id=3 分别返回 {id, o, f, t} —— 看结构是被切成三段的密文(t 很长,f 是 32 字节的认证材料);
  • key.php?n=2&sid=…&tk=… 返回 {iv, ct, p:[0,21,11,58,"…"]} —— 典型的 AES-GCM 参数加一段分片定位表。

裸 curl 这几个接口一律吃 773 访问被拒绝(要浏览器指纹+会话),但音频的解密是在浏览器里做的,所以只要让浏览器跑完,就能在音频进播放器之前把它截下来。

三、怎么把音频拿到手#

用 Playwright 打开页面,并在页面初始化时 hook 掉 AudioContext.prototype.decodeAudioData——这是音频解密完、即将播放的那一瞬间:

// audio_probe.js 的核心
await page.addInitScript(() => {
const AC = window.AudioContext || window.webkitAudioContext;
const orig = AC.prototype.decodeAudioData;
AC.prototype.decodeAudioData = function (buf, ...rest) {
const u8 = new Uint8Array(buf.slice(0)); // 解密后的原始音频字节
window.__cap.decode.push(u8);
return orig.apply(this, [buf, ...rest]);
};
});
await browser.launch({ args: ['--autoplay-policy=no-user-gesture-required'] }); // 免交互自动播放
await page.goto('https://wiki.pigeon-games.com/WERJETZTALLEINISTWIRDESLANGEBLEIBEN/');

顺手把网络请求也全记了一遍(state.php / key.php / play.php 的响应体都落盘了)。拿到的音频头是 RIFF….WAVE:

项目值
容器WAV(PCM)
声道1(单声道)
采样率44100 Hz
位深16 bit
时长111.356 秒
大小9,821,608 字节

正放(160kbps mp3,便于在线听):

四、先把「是什么声音」量一遍#

  • BPM ≈ 139.7:能量包络自相关的最强峰(第二、第三候选是 69.8 与 105.5,都是它的分数倍/接近值),说明这是一段有稳定节拍的电子曲。
  • 频谱质心 2.63.5kHz,频谱平坦度 0.120.21:谐波性明显,不是白噪声,也不是纯语音。
  • 几乎无静音(非静音占比 99.2%),整段是连续的音乐。
  • 每 2 秒的主导频率在 1.4k~3.1kHz 之间跳,且出现 1397.5 / 1565.5 / 2092.5 Hz 这类稳定的离散值(F6、G6、C7 附近),像合成器的高把位。

顺手过了遍语音识别(whisper base):它在 60 秒和 90 秒处听到「Sper, sper, sper…」(罗马尼亚语「我希望」)。但模型给的 no_speech_prob 是 0.83~0.91 —— 这种整齐重复的词,基本可以判定是被节拍切片带出来的幻觉,先不当线索用。

五、为什么怀疑是 SSTV#

SSTV 的硬特征是「每行开头一个 1200Hz 同步脉冲」。于是我把音频按 4096 点做短时 FFT,分别取三个频带看周期性:

频带周期性峰值周期
1150–1250 Hz(同步脉冲)0.800.430 s
1050–1350 Hz(VIS 头)0.730.430 s
1500–2300 Hz(图像数据)0.900.139 s
40–300 Hz(底鼓)0.600.139 s

1200Hz 带的尖峰出现在 1.06 / 1.49 / 2.55 / 2.98 / 3.41 / 4.69 / 5.55 / 6.41 / 6.84 / 7.27 / 8.97 / 9.40 秒 —— 彼此间隔全是 0.43 秒的整数倍。

而 0.43 秒正好是这首歌一个节拍的长度(60 ÷ 139.7 = 0.4295),也正好是 Scottie S1 的行时长(428ms)。也就是说:有人把 SSTV 的行同步卡在节拍上,让整条图像传输听起来像鼓点。

六、解码#

用 sstv(Rust 实现的 Python 包)直接解:

import sstv
for i, img in enumerate(sstv.decode_from_wav("solivault_song.wav")):
img.save(f"sstv_img_{i}.png")
print(i, img.size) # 0 (320, 256)

结果:1 张图,320×256 —— 正是 Robot 36 模式的标准尺寸。

从 Solivault 歌曲里解出的 SSTV 图像:黑发黄绿瞳、白色裙装、黑色颈环的角色立绘
从 Solivault 歌曲里解出的 SSTV 图像:黑发黄绿瞳、白色裙装、黑色颈环的角色立绘

上图:SSTV 解码结果。横向的噪纹是 SSTV 带宽窄、逐行扫描带来的固有失真,不是原图瑕疵。

图上是一位黑发、黄绿色眼睛、白色裙装、黑色颈环、头上有白色蝴蝶结/头纱的角色。

后续确认(10-02 归零后):这段旋律对应哪首曲子

倒计时归零、End 页那句 slogan 在游戏内解锁之后可以确认:这段旋律对应的是 くるぶっこちゃん 的《Message》 —— 也就是第九章这一轮更新里的新曲,上面这张立绘就是它的曲绘。

也就是说页面那段”会响的歌”并不是随手找的 BGM,而是先一步放出了即将解锁的新曲。

七、顺手核对一下:key.php 里的封面图#

key.php?n=1 的响应里还有一个 au 字段(https://c9.gaoice.run/Solivault-<hash>.png)。它的哈希每次会话都不同,直接 curl 会吃 403 —— 但它是页面自己会加载的资源,让浏览器正常打开页面就能拿到(1920×1080)。

把它和第一阶段从 start.png 里拆出来的两张图做像素级比对:

对比灰度相关系数dHash 汉明距离
au 封面 vs part1.png1.0000/992(完全一致)
au 封面 vs part2.png0.980175/992(82.4%)

结论:这张”封面”就是第一阶段那张黑洞图本身,只是换了会话签名的文件名又发了一次。所以这次更新里唯一的新图,就是上面的 SSTV 角色图。

八、倒放呢?#

第九章的线索里反复出现「Backward」,所以倒放也查了。

倒放(160kbps mp3):

结论:倒放音频里没有第二张 SSTV 图(解码器返回 0 张),正放那张就是全部。倒放频谱也放这儿备查:

倒放后的频谱图
倒放后的频谱图

上图:倒放音频的频谱(正放那张见文末产物清单)。

九、这次用到的产物都在这里#

文件说明下载 / 在线听
phi9-solivault-song.mp3正放,111 秒,160kbps在线听
phi9-solivault-song-reversed.mp3倒放版,同上在线听
phi9-sstv-robot36.pngSSTV 解出的 320×256 图见正文第六节
phi9-spec-forward.jpg正放频谱图(2048×1024,log 频率轴)见下节插图
phi9-spec-reversed.jpg倒放频谱图见上节
solivault_song.wav原始解密音频(9.8MB,44.1kHz/16bit/单声道)—

正放频谱(黄色亮带就是歌曲的能量分布;SSTV 那条传输就藏在 1.5~2.3kHz 的细纹里):

正放频谱图
正放频谱图

十、下一步看什么#

state.php 的 close_at 指向 2026-10-02 17<00>。

后续确认(09-26):播歌阶段已经下线

复查时发现页面已经改回原版:主页恢复成 start.png 两图拆分 + 倒计时的老代码(实测又能切出 583504 字节的第二张图),state.php 与 key.php 接口已撤(404 / 错误页),song.html 现在返回的是 wiki 回退首页。也就是说本文抓取的音频与 SSTV 图是限时窗口期的内容,文内的试听与图片都来自那份存档。

归零之后(10-02 17<00>+)

倒计时归零后,主页面没有立刻换页:主页面和 nosong.html 的 HTML 一字未改,反倒是终点页在 17<01>~17<02> 之间换了一版——撤掉源码混淆、版本号从 2.2.0 跳到 2.4.0,并放出一段只在浏览器控制台里打印的制作人员名单。

播歌用的那套加密接口(state.php / key.php / play.php)没有回来,所以本文的音频仍是那段窗口期唯一的存档。

归零前后完整的实测时间窗与那封控制台致谢,写在收官那篇里。

复现要点

① 需要国内 IP + 真浏览器;② 页面要允许自动播放(--autoplay-policy=no-user-gesture-required);③ 音频只在 decodeAudioData 那一刻是明文,hook 它;④ SSTV 用 sstv.decode_from_wav(),一行搞定。


来源:页面本身(state.php / song.html / key.php / play.php 的实测)与我们的脚本产出(audio_probe.js、audio_probe2.js、SSTV 解码脚本)。第九章 ARG 的整体脉络与致谢见另一篇复盘。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
Solivault 的歌里藏了什么:一次 SSTV 解码实录
https://blog.wmc.pub/posts/phi9-solivault-song-sstv/
作者
Milk
发布于
2026-09-26
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
Phigros 第九章 ARG 收官:17:00 归零那一刻,与藏在控制台里的致谢
技术Solivault 的倒计时在 2026-10-02 17:00 归零。终点页在归零后两分钟内换了一版:源码从混淆变回明文、版本号从 2.2.0 跳到 2.4.0,并多出一段只在浏览器控制台里打印的制作人员名单。这篇记录我抓到的完整时间窗和那几个藏起来的机制。
2
Phigros 第九章《穹顶孤舟》网页解谜复盘:从 Chaocipher 到 13 段碎片拼字
技术一条完整可复现的解谜链:Chaocipher 解出隐藏页、PNG 尾部藏图、零宽字符转摩尔斯、Nihilist 解出 JUSTENGAGEWTH、按浏览器指纹分发的 13 段密文,以及 acrostic 竖读出的两个关键词。
3
穹顶孤舟:Phigros 主线全章总结(含从秘密包里解出的终章)
随笔主线已经收束。这篇把一~九章连起来讲一遍:Legacy 里的一~四章、五《方舟蜃景》到八《弭刻日》的门槛与关底、第九章《穹顶孤舟》的 12 首曲与解密档案,以及最后从客户端秘密包里解出来的 C9S6 终章对白。
4
Phigros 4.0.1 第九章客户端全解:从启动到退出,这一章在程序里究竟发生了什么
技术把 Phigros 4.0.1 客户端里第九章的整条链路走完:启动时怎么恢复解锁与密钥、选曲与难度动画、关卡结算如何推进章节进度、Phase 2 换形态、谜题与推演、密码背后的 AES 方案(含 8 个秘密包的解密与加载/卸载)、Hate→DS→Message→Credits 的演出顺序,以及「退出应用」那一处——附 68 个脚本类各自干了什么。
5
穹顶孤舟 · 素材馆:把第九章整个装进一页
随笔第九章的全部可听可见之物都在这:选难度的六路视频、12 首曲的官方试听与 12 张曲绘、隐藏曲 PV、结局循环、秘密曲、31 段音效切片、10 张材质(噪域/故障/网格/密码界面)、8 组界面截图,以及完整清单表与出处。
随机文章随机推荐

评论区

Profile Image of the Author
Milk
AWMC Founder
分类
标签
站点统计
文章
10
分类
3
标签
22
总字数
58,773
运行时长
0 天
最后活动
0 天前
文章目录