1040 字
5 分钟
0x401-Broken Voice 解题思路

原题:玄机靶场 0x401-Broken Voice 题型:MISC(音频取证 + 文件还原) 隐藏链:zip → 按字节拆碎 → 每字节一个文件 → 文件名=偏移 → 拼接还原 → MP3 → 语音念 flag 最终 flag:flag{12398093888892380234800981239082}


0. 整体思路#

题目名字 “Broken Voice”(坏掉的声音)+ 一个 zip,第一反应:zip 里藏着音频,且大概率被拆碎了(“broken” 暗示损坏/拆分)。


1. 识别文件类型(不用解压就能看出来)#

hexdump 开头:

50 4b 03 04 14 00 ...

50 4b 03 04 = ASCII PK\x03\x04 = zip 的 Local File Header 签名

常见文件头魔数速查#

魔数类型
50 4b 03 04zip
ff d8jpg
89 50 4e 47png
RIFF....WAVEwav
ff fb / ff f3 / ff e0~ff ffmp3 帧同步
ID3mp3(带标签)
OggSogg

2. hexdump 文本还原成二进制#

hexdump -C 是文本,不能直接用。用正则抠出每行的十六进制字节:

import re
data = []
for line in open("Broken+Voice.zip.hexdump"):
m = re.match(r"^[0-9a-f]{8}\s+(.*?)\s*\|", line)
if m:
data += [int(b, 16) for b in re.findall(r"[0-9a-f]{2}", m.group(1))]
open("broken.zip", "wb").write(bytes(data))

💡 也可以用 xxd -r 直接还原,但 Python 更可控(能顺便统计行数、校验大小)。


3. 分析 zip 内容——发现”拆碎”模式#

import zipfile
zf = zipfile.ZipFile("broken.zip")
infos = zf.infolist()
# → 35712 个文件,文件名全是纯数字:0, 1, 10, 100, 1000, 10000...
# 每个文件 comp_size=1, uncomp_size=1, method=0 (stored 不压缩)

关键观察#

  • 35712 个文件 × 每文件 1 字节
  • 文件名 0~35711 连续无缺失(用 set(range(0, 35712)) - set(names) 验证为空)
  • 文件名 = 字节在原始文件中的偏移量

这就是典型的”字节切片”混淆:把一个大文件切成单字节,用文件名记录位置。遇到”大量规律命名的小文件”,直接想到这招。


4. 按文件名排序拼接还原#

infos.sort(key=lambda i: int(i.filename)) # 关键:按数值排序,不是字典序!
buf = b"".join(zf.read(i) for i in infos)
open("broken.bin", "wb").write(buf) # → 35712 字节

⚠️ 坑:文件名是数字时必须按 int 排序。按字符串排序会变成 0, 1, 10, 100, 1000… 顺序全乱!


5. 判断还原出来的文件类型#

open("broken.bin","rb").read(4).hex() # → fff348c4

开头 ff f3MP3 帧同步字0xFF + 高 3 位掩码 0xE0 匹配)。再用 ffprobe 确认参数:

Terminal window
ffprobe broken.bin
# → MPEG2 Layer III, 32000 bps, 16000 Hz, mono, duration 8.93s

自洽性验证#

32kbps / 16kHz 的 MPEG2 L3:每帧 144 字节、0.036 秒 → 248 帧 × 144 = 35712 字节 = 文件大小分毫不差

证明还原正确、无多余数据。顺带发现尾部藏 LAME3.100 编码器标记,属于正常 MP3 结构。


6. 音频常规检查(排除隐写)#

Terminal window
# 转 WAV
ffmpeg -i broken.bin broken.wav
# 频谱图——看有没有藏文字/图案/摩斯码
ffmpeg -i broken.wav -lavfi showspectrumpic=s=1200x600 spec.png
# 波形图——看能量分布
ffmpeg -i broken.wav -lavfi showwavespic=s=1200x300 wave.png

频谱图显示正常语音特征,无隐写图案 → flag 在语音内容本身

💡 音频隐写常见检查点:频谱图(图案/文字)、倒放、变速、摩斯、DTMF、SSTV、LSB、DeepSound/MP3Stego。


7. 语音识别(关键一步)#

本机没装 ASR,安装 faster-whisper:

Terminal window
pip install faster-whisper
# 国内下载模型必须挂镜像,否则卡死/报 401 错误:
export HF_ENDPOINT=https://hf-mirror.com
export HF_HUB_DISABLE_XET=1 # 新版 HF 的 xet 存储在国内会 401
from faster_whisper import WhisperModel
m = WhisperModel("small", device="cpu", compute_type="int8")
segs, info = m.transcribe("broken.wav", language="zh")
for s in segs:
print(s.text)
# → "Flag 是 Flag 123980 9388889 2380 234800 981239082"

方向验证#

同时识别倒放版本:

Terminal window
ffmpeg -i broken.wav -af areverse broken_rev.wav

倒放版本被识别成俄语乱码 → 正放才是正确方向

数字验证#

用 base + small 两个模型交叉识别,数字串完全一致 → 可信。

最终 flagflag{12398093888892380234800981239082}


8. 工具清单#

工具用途
Python + regexhexdump → 二进制
Python zipfilezip 结构分析、拼接
file / 魔数 / ffprobe文件类型识别
ffmpeg转 wav、频谱图、波形图、倒放
faster-whisper语音识别

9. 这类题的通用套路#

特征处理
zip 里大量规律命名小文件文件名=偏移,排序拼接
文件名是数字按 int 排序,别用字典序
拼出来的文件不知道类型看魔数 / file / 帧同步字
音频是”语音”先频谱图查隐写,再 ASR 听内容
语音听不清/方向可疑试倒放、变速(areverse / atempo)
数字串易识别错换模型交叉验证