-
-
[原创] KCTF 2026 第八题:亥子合辰·塔影迷楼 Writeup
-
发表于: 2026-8-22 11:52 60
-
程序先做两层运行时解密,再进入真正的 checker。checker 将 16 字节输入映射到有限域 2^127 - 39,经过五次多项式和两组 Feistel 网络后与目标值比较。反向恢复目标多项式值后,解有限域方程即可得到 flag。
样本不是常见压缩壳,而是自定义两阶段运行时解密。先按 PE section 信息把文件映射成内存镜像,后续直接在镜像中还原代码即可,不需要修复导入表或重建完整 PE。
.tgt 保存了解密配置和最终比较目标。入口首先读取 .tgt+0x20 的长度,对 RVA 0x1800 开始的 stage1 做固定异或:
stage1 解开后,0x1400018d0 附近出现下一层解密逻辑。程序对 stage1 明文分别用 FNV_OFFSET 和 GOLDEN 作为 seed 计算 FNV64,结果作为第二层 keystream 的种子:
第二层目标由 .tgt 给出:
每个字节的 keystream 由 state、stage1_fnv 和 stage1_golden 计算,然后异或回 stage2:
解密完成后,RVA 0x1d10..0x6671 变成有效 x64 代码;真正 checker 位于 0x140004380,外层 wrapper 位于 0x140001d10。再对 stage2 明文计算 hash,后续常量、S-box、Feistel round key 都由这两个值派生:
完整脚本中的前半部分就是上述脱壳逻辑:把 section 映射到 img,依次解 stage1 和 stage2,然后直接在解密后的内存镜像上恢复 checker 常量。
真正校验函数在 0x140004380。前置约束:
随后把输入按 base94 编成有限域元素:
再计算:
Y 分别进入 A/B 两套 20 轮 Feistel。目标 qword 逆回去后,两套网络得到同一个多项式目标:
于是只需在 GF(P) 上解五次方程:
常量生成时要注意顺序:第二个 splitmix64 的输入需要在 r9 ^= a 前取出,否则 A/B 两套 Feistel 逆回去不会收敛到同一个 Y。
下面是完整精简求解脚本,默认当前目录存在 kctf2026_CrackMe08.exe。无需 PE 解析库,section 偏移按样本固定值写入,执行后直接输出 flag。