-
-
[原创]第七题:戌时·暗能潜流
-
发表于: 13小时前 39
-
四组样例的密文长度都是明文长度的四倍:一个明文字节最终对应四个十六进制字符。将密文每 12 个字符分组,正好对应三个明文字节。
先比较所有完整三字节块:
TLU -> 94AA48550495
Hel -> 34BB405504B5
202 -> A48844556485
abc -> 547B475584B5
这些块的第 1、4、6、7、9、11 位始终不变,其结构为:
q0 4 q1 q2 4 q3 5 5 q4 4 q5 5
提取位置是:
[0, 2, 3, 5, 8, 10]
再比较两个剩余两字节的尾块:
lo -> 223594B94C53
d! -> 223564BB4553
可以得到两字节尾块结构:
2 2 3 5 q0 4 q1 q2 4 q3 5 3
提取位置是:
[4, 6, 7, 9]
2026 最后的单字节尾块为:
223322356483
对应结构:
2 2 3 3 2 2 3 5 q0 4 q1 3
提取位置是:
[8, 10]
三类固定结构只是外层包装。去掉包装后,每个明文字节均剩下一对十六进制半字节。
恢复内部变换
从样例中提取出的半字节对如下:
TLU -> 9A A8 09
Hello -> 3B B0 0B 9B 9C
2026 -> A8 84 68 68
abcd! -> 57 B7 8B 6B BB
密钥为 121。把每个半字节对看成长度为 2 的循环序列:旋转 1 位会交换两个半字节,旋转 2 位则回到原序。因此逆向时按输出位置循环执行:
1 -> 交换 hi、lo
2 -> 保持 hi、lo
1 -> 交换 hi、lo
每三个字节对还存在一次固定位置重排:
[0, 2, 1]
该排列是自身的逆排列。还原分组顺序后,再将整个字节对数组逆序。
以 TLU 为例,首先从包装中提取:
9A A8 09
逆密钥 121:
A9 A8 90
逆 [0, 2, 1] 重排:
A9 90 A8
整体逆序:
A8 90 A9
此时与明文 54 4C 55 的关系已经很明显:每个高半字节都增加了 5,每个低半字节都增加了 4,运算均在模 16 下进行。
A8 - (5,4) = 54 = 'T'
90 - (5,4) = 4C = 'L'
A9 - (5,4) = 55 = 'U'
用 Hello 继续验证。提取结果是:
3B B0 0B 9B 9C
依次逆密钥、逆分组重排、整体逆序后得到:
9C B9 B0 B0 B3
分别对高、低半字节减去 5 和 4:
48 65 6C 6C 6F -> Hello
其余两组样例也能得到原始明文,说明完整逆变换已经确定。
解密流程
完整解密顺序如下:
- 每 12 个十六进制字符划分一个外层块;
- 按完整块、两字节尾块或单字节尾块的位置提取
q; - 按密钥
121循环交换对应字节对的高、低半字节; - 每三个字节对执行
[0, 2, 1]; - 将整个字节对数组逆序;
- 高半字节减 5,低半字节减 4,全部按模 16 计算;
- 合并半字节并按 ASCII 解码。
Exp
FLAG_CIPHER = (
"14CC4655547594BC475584C5848A43551495448C445584C5D4C9475564C534A84B55A4B574BA4355F495"
"A48844556485648C495534A5548C4F5584A5B4BB405554B522332235A4B3"
)
FULL_POS = (0, 2, 3, 5, 8, 10)
TAIL2_POS = (4, 6, 7, 9)
TAIL1_POS = (8, 10)
def decrypt(ciphertext):
blocks = [ciphertext[i:i + 12] for i in range(0, len(ciphertext), 12)]
if blocks[-1].startswith("22332235") and blocks[-1].endswith("3"):
tail = 1
elif blocks[-1].startswith("2235") and blocks[-1].endswith("53"):
tail = 2
else:
tail = 0
q = []
full_blocks = blocks if tail == 0 else blocks[:-1]
for raw in full_blocks:
q.extend(int(raw[i], 16) for i in FULL_POS)
if tail == 2:
q.extend(int(blocks[-1][i], 16) for i in TAIL2_POS)
elif tail == 1:
q.extend(int(blocks[-1][i], 16) for i in TAIL1_POS)
ordered = []
for i in range(0, len(q), 2):
hi, lo = q[i], q[i + 1]
if (1, 2, 1)[(i // 2) % 3] == 1:
hi, lo = lo, hi
ordered.append((hi, lo))
reversed_pairs = []
for i in range(0, len(ordered), 3):
group = ordered[i:i + 3]
if len(group) == 3:
reversed_pairs.extend((group[0], group[2], group[1]))
else:
reversed_pairs.extend(group)
plain = []
for hi, lo in reversed(reversed_pairs):
value = (((hi - 5) & 0xF) << 4) | ((lo - 4) & 0xF)
plain.append(value)
return bytes(plain)
flag = decrypt(FLAG_CIPHER)
print(flag.decode())
赞赏
他的文章
赞赏
雪币:
留言: