题目概述
这是一道 Misc 文本隐写题。题目给了一个文本文件 ai_reply.txt,正文看起来是一大段重复、空泛的中文 AI 回复。
这类题通常不会真的要求我们逐字理解长篇废话,而是把信息藏在“外观层”,例如:
- 标点符号
- 换行位置
- 空格数量
- 字符长度
- 重复段落之间的差异
本题的核心就是:提取中文标点符号,并把 4 种不同标点映射成二进制数据,最终还原 flag。
最终 flag:
sdpcsec{welcome_2026_4nd_competiton!}
环境准备
本题只需要 Python 3。
推荐把终端切到题目目录:
cd D:\codex1\黄河流域\signin
确认文件存在:
dir
可以看到:
ai_reply.txt
第一步:观察题目文件
先直接打开 ai_reply.txt。
如果用支持 UTF-8 的编辑器打开,可以看到它是一段正常中文,内容大概一直在重复类似意思:
从整体节奏来看这个方向确实具备继续推进的空间。
如果换一个更稳妥的说法当前结论依然成立:
很多表达之所以显得复杂只是因为包装层数太多,
...
其中有几处非常像提示:
先判断文本里有没有异常结构
多留意形式本身
段落之间的节奏标点习惯以及重复出现的细节
信息藏在外观层而不是语义层
这些句子基本是在提醒我们:
不要只看正文语义,要看文本形式。
第二步:统计标点符号
因为正文一直提示“节奏、标点、形式”,所以我们先统计所有标点。
写一个简单脚本 check_punct.py:
from pathlib import Path
from collections import Counter
import unicodedata
p = Path("ai_reply.txt")
s = p.read_text(encoding="utf-8")
puncts = [ch for ch in s if unicodedata.category(ch).startswith("P")]
print("文本字符数:", len(s))
print("标点数量:", len(puncts))
print("标点统计:", Counter(puncts))
print("前 80 个标点:", "".join(puncts[:80]))
运行:
python check_punct.py
得到的关键信息:
文本字符数: 3446
标点数量: 148
标点统计: Counter({'。': 54, ':': 40, ',': 27, ';': 27})
可以看到,全文只出现了 4 种中文标点:
,
。
;
:
这里非常关键。
因为 4 种符号正好可以表示 4 个状态:
00
01
10
11
也就是说,每个标点可以表示 2 bit。
题目里一共有 148 个标点:
148 * 2 = 296 bit
296 / 8 = 37 byte
37 字节很像一个 flag 的长度。
所以推测:
题目把 flag 转成二进制后,每 2 bit 换成一个标点藏进了文本里。
第三步:枚举标点到二进制的映射
现在的问题是:
4 个标点分别对应哪个 2 bit?
例如可能是:
, -> 00
。 -> 01
; -> 10
: -> 11
也可能是其他顺序。
一共只有 4 个符号,所以所有可能映射数量是:
4! = 24
只有 24 种,非常适合直接暴力枚举。
写脚本 solve.py:
from pathlib import Path
from itertools import permutations
import string
import unicodedata
p = Path("ai_reply.txt")
s = p.read_text(encoding="utf-8")
puncts = [ch for ch in s if unicodedata.category(ch).startswith("P")]
symbols = sorted(set(puncts), key=lambda c: ord(c))
print("symbols:", symbols)
print("punctuation count:", len(puncts))
printable = set(bytes(string.printable, "ascii"))
for perm in permutations(range(4)):
mapping = dict(zip(symbols, perm))
bits = "".join(format(mapping[ch], "02b") for ch in puncts)
data = bytes(int(bits[i:i + 8], 2) for i in range(0, len(bits), 8))
score = sum(ch in printable for ch in data)
if score == len(data) or b"{" in data:
try:
text = data.decode("ascii")
except UnicodeDecodeError:
continue
print("mapping:", mapping)
print("result:", text)
运行:
python solve.py
输出:
symbols: ['。', ',', ':', ';']
punctuation count: 148
mapping: {'。': 1, ',': 0, ':': 3, ';': 2}
result: sdpcsec{welcome_2026_4nd_competiton!}
所以正确映射为:
, -> 00
。 -> 01
; -> 10
: -> 11
第四步:写成最终解题脚本
枚举确认映射后,可以写一个更简洁的最终脚本。
from pathlib import Path
import unicodedata
p = Path("ai_reply.txt")
s = p.read_text(encoding="utf-8")
puncts = [ch for ch in s if unicodedata.category(ch).startswith("P")]
mapping = {
"\uff0c": "00",
"\u3002": "01",
"\uff1b": "10",
"\uff1a": "11",
}
bits = "".join(mapping[ch] for ch in puncts)
flag = bytes(
int(bits[i:i + 8], 2)
for i in range(0, len(bits), 8)
).decode("ascii")
print(flag)
运行:
python final_solve.py
输出:
sdpcsec{welcome_2026_4nd_competiton!}
Flag
sdpcsec{welcome_2026_4nd_competiton!}
复盘
这题的提示其实藏在正文里:
多留意形式本身
段落之间的节奏标点习惯
信息藏在外观层而不是语义层
看到这种“很长、很顺、很空”的文本时,不要急着读懂每句话。
更有效的排查顺序是:
- 统计字符、行数、标点、空格。
- 看是否存在固定数量的符号种类。
- 判断数量是否能整除 8、16、32 等常见编码单位。
- 尝试二进制、四进制、ASCII 解码。
- 对小规模不确定映射进行暴力枚举。
本题就是一个很典型的新手 Misc 文本隐写题。
Discussion
评论区
欢迎交流、补充思路或指出文中的问题。