你以为这是一串人类忍不住发出的笑声?不对。在布朗诺夺旗赛(CTF)的一道挑战里,选手拿到一个纯文本文件 aha.txt,里面只有空格隔开的“单词”。每个单词恰好8个字母,而且只用了 A 和 H 两种字符。第一眼像是某人在屏幕上不停地“啊哈”、“哈哈”,但细看之后就会发现——这是二进制穿上了一件玩笑的外衣。

文件给出的词条一共有35组,每组固定8个字符,例如 AHHAAAHA、AHHAAHHH、AHAHAHAH。当一段密文只使用两个不同的符号时,密码学选手的脑袋里立刻会亮起红灯:这几乎就是二进制的等价形式。两组符号可以映射为 0 和 1,而恰好8位就是1个字节——这个长度一下子就暴露了它想表达的其实是 ASCII 字符(美国标准信息交换码)。一条8字母的序列,就像是一个字节的条形码,等我们去扫。

接下来的解码步骤简单得令人发指。随便选一种映射规则,比如让 A 代表 0,H 代表 1,然后逐组替换成二进制字符串。把“AHHAAAHA”变成“01100010”,再把这个二进制数转为十进制,最后通过 ASCII 表翻出对应的字符。代码量少到只有几行:读取文件、切分词条,用 replace 把字母换成比特,再用 int(bits, 2) 和 chr() 就拿到了原始信息。跑一遍脚本,35个字符鱼贯而出,组成了这道题的旗标:bronco{UFUNNYLMAOLOLXDIJBOLROFLHAHA}。旗标里塞满了大笑的俚语,仿佛在说“玩笑开完了,但你确实被骗了”。

这里没有技巧,全靠模式识别。首先,字母表大小就是巨大的线索。密文只用到两种字符,几乎一定是二进制;衍生一下,如果出现了16种字符,大概率是十六进制;而字母表增加到64个,再配上“+”、“/”和“=”后缀,那就是把 base64 写在脸上了。其次,固定的分组宽度更直接把底牌掀给了你。8个字符一组,不用写一行代码就知道它是一个字节;当分组长度是2,往往对应十六进制对;4个字符则是 base64 的四元组。这些都是解码时的肌肉记忆。

映射方向也只有两个可能。如果你猜 A=0、H=1,出来的全是乱码,那根本不用慌,翻过牌面重新猜 A=1、H=0 再跑一次就好了。两道岔路口,闭着眼睛先走一边,走不通再退回来,开销几乎为零。这个思路和网络安全里经常遇到的“先试最简单的攻击”如出一辙——在千头万绪面前,最小代价的猜测往往一击即中。

最终旗标里的那串 LOL、ROFL,正好呼应了文件里满屏的 A 和 H。这场比赛叫“不得儿戏”(No Laughing Matter),可整道题从头到尾都在对你挤眉弄眼。下次再见到满纸的“哈哈”声,不妨先别跟着笑,看看它们是不是在用 ASCII 偷偷讲另一套话。