ISCC 校赛 misc3 WP

Wang1r Lv4

扭曲的真相

Summary

两个数据文件串起了三层隐写:secret.dat 先拆成 4 条 bit 流得到 RAR 密码,truth.dat 经莫比乌斯式 XOR 还原出加密 RAR,再从 flag.txt 的零宽字符恢复 PNG。

Solution

Clue Mapping

题面里的线索基本分成两组:第一组是总叙事,告诉我们两个文件的职责;第二组是谶语,精确描述 secret.dat 的解法,并引出后续的莫比乌斯/XOR 处理。

题面线索 对应含义 实际用法
两个神秘的数据文件 secret.dattruth.dat 是两条不同链路 先从 secret.dat 取 key,再用 key 解 truth.dat 还原出的 RAR
secret.dat 文件名 secret 是“秘密/钥匙” 不直接拿 flag,而是得到提示和 RAR 密码
truth.dat 文件名 truth 是“真相” 最终主数据来自 truth.dat,题名“真相”也指向它
一位数学大师 数学/拓扑提示 后续 4 段文本里出现莫比乌斯环,说明要按“扭曲纸带”处理
扭曲的真相 truth.dat 做扭曲/反向配对 truth.dat 看作一条 0/1 纸带,左右半段反向 XOR
藏在其中的秘密 数据不按普通文件格式直接读 secret.dat 要拆 bit,flag.txt 还藏零宽字符

谶语逐句对应如下:

谶语 对应操作
四位成组 secret.dat 是 hex 字符流;每个 hex 字符天然对应 4 bit
拆骨分藏 不按普通 hex 转字节,而是把每个 4-bit nibble 拆开
纵向拾取 取所有 nibble 的第 1 位组成第 1 条流,第 2 位组成第 2 条流,依此类推
各归其行 得到 4 条独立 bit 流
零壹铺路 每条流都是 0/1 序列,需要继续按 bit 处理
字符浮光 每 8 bit 组字节,按 UTF-8 解码,文字浮现
四言成谶 4 条 bit 流分别解出 4 段提示文字
水落石方 4 段提示揭示后续方向:莫比乌斯、XOR、反转、key

secret.dat 解出的 4 段文本继续承担线索作用:

解出的文本 作用
取一个长方形纸带...莫比乌斯环 指向“纸带一端翻转再连接”,也就是半段反向配对
”起点“亦或”终点“。 亦或 是 XOR 提示
蚂蚁...两圈...方向上的反转 说明要考虑整体方向反转,而不是只做普通左右 XOR
The key is ... 给出 base64,解码得到 RAR 密码 Yth9Ur062azZA09y4rs5L

Step 1: 从 secret.dat 还原 key

secret.dat 是十六进制字符流。每个 hex 字符对应 4 bit,把第 1/2/3/4 位分别纵向抽成 4 条 bit 流,再每 8 bit 组字节并按 UTF-8 解码。

第 4 条流能读到:

1
The key is WXRoOVVyMDYyYXpaQTA5eTRyczVM

base64 解码后得到 RAR 密码:

1
Yth9Ur062azZA09y4rs5L
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pathlib import Path
import base64, re

raw = Path("secret.dat").read_text(encoding="ascii").strip()
hex_chars = re.findall(r"[0-9a-fA-F]", raw)
streams = [[] for _ in range(4)]

for ch in hex_chars:
bits = f"{int(ch, 16):04b}"
for i, bit in enumerate(bits):
streams[i].append(bit)

messages = []
for bits in streams:
data = bytes(int("".join(bits[i:i+8]), 2) for i in range(0, len(bits), 8))
messages.append(data.decode("utf-8"))

token = re.search(r"The key is\s+([A-Za-z0-9+/=]+)", messages[3]).group(1)
print(base64.b64decode(token).decode())

Step 2: 从 truth.dat 还原 RAR

truth.dat 是一整条 0/1 纸带。把它一分为二,后半段反向后与前半段 XOR,再按 bit 打包,就能得到 RAR5 头。

这个步骤对应了题面里的“扭曲”“莫比乌斯”“亦或”。

1
2
3
4
5
6
7
8
9
10
from pathlib import Path
import numpy as np

arr = np.fromfile("truth.dat", dtype=np.uint8) - ord("0")
arr = arr[: len(arr) // 2 * 2]
half = len(arr) // 2
stage_bits = np.bitwise_xor(arr[:half][::-1], arr[half:])
rar = np.packbits(stage_bits, bitorder="big").tobytes()
Path("truth_stage2.rar").write_bytes(rar)
print(rar[:8].hex())

7-Zip 测试结果是 Everything is Ok,说明这一步正确。

Step 3: 解 RAR,取 flag.txt 的零宽字符

RAR 解出一个很大的 flag.txt,开头是假的 flag:

1
flag={M4403wkhabdIfRxDN111}

真正的数据藏在后面的零宽字符里。U+200B 记作 0,U+200C 记作 1。把它们提取出来后,前后对半,再做一次“后半反向 XOR”,就能恢复 PNG。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from pathlib import Path

b = Path("flag.txt").read_bytes()
zw0 = b"\xe2\x80\x8b"
zw1 = b"\xe2\x80\x8c"

bits = []
i = b.find(zw0)
if i < 0:
i = b.find(zw1)

while i + 3 <= len(b):
chunk = b[i:i+3]
if chunk == zw0:
bits.append(0)
i += 3
elif chunk == zw1:
bits.append(1)
i += 3
else:
i += 1

bits = bits[: len(bits) // 2 * 2]
half = len(bits) // 2
png_bits = [a ^ b for a, b in zip(bits[:half], reversed(bits[half:]))]

out = bytearray()
for j in range(0, len(png_bits), 8):
out.append(int("".join(map(str, png_bits[j:j+8])), 2))

Path("flag_hidden.png").write_bytes(bytes(out))

Step 4: 读 PNG 里的最终提示

PNG 里写的是:

1
2
3
你需要找到一个二进制序列,
截取起始位为 m,长度为 n 的子序列,
通过 base62 编码得到最终的谜底

flag.txt 开头的假 flag 里已经给出参数:

1
M4403 ... N111

所以这里对应的索引是 m=4403, n=111。最后一层要从“某个二进制序列”中截取这 111 bit,再做 base62。

这里不能把可见的 flag={M4403wkhabdIfRxDN111} 直接当最终 flag;它同时给出了 mn,中间的 wkhabdIfRxD 更像干扰/校验片段。按最新提示“每一次收获都有意义”,应回到第一层真正收获到的二进制序列:secret.dat 的 hex nibble bit 流。

RAR 密码也不是随机串:

1
2
3
Yth9Ur062azZA09y4rs5L
^ ^ ^ ^ ^
0 62 az ZA 09

中间 0|62|az|ZA|09 可以解析为:

  • 0:0-based 起始位;
  • 62:base62;
  • azZA09:字母表范围 a-z + Z-A + 0-9

因此最终步骤是:

  1. secret.dat 的原始 hex 字符流;
  2. 每个 hex 字符展开为 4-bit,得到第一层的 nibble bit 序列;
  3. 从 0-based bit offset 4403 截取 111 bit;
  4. 用字母表 abcdefghijklmnopqrstuvwxyzZYXWVUTSRQPONMLKJIHGFEDCBA0123456789 做 base62 编码。

得到:

1
olp95YuuF73D5MsK6

What Is Reused

  • secret.dat 的 4 路 bit 拆分,用来提取 RAR 密码。
  • truth.dat 的莫比乌斯式 XOR,用来恢复加密 RAR。
  • flag.txt 的零宽字符,用来恢复 PNG。
  • PNG 里的 m/n,用来定位最终序列。

Flag

最终候选:

1
flag={olp95YuuF73D5MsK6}
  • 标题: ISCC 校赛 misc3 WP
  • 作者: Wang1r
  • 创建于 : 2026-05-06 20:17:55
  • 更新于 : 2026-05-06 20:19:00
  • 链接: https://wang1rrr.github.io/2026/05/06/ISCC-校赛-misc3-WP/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。