-
-
[原创]第八题:亥子合辰·塔影迷楼 Writeup
-
发表于: 2026-8-21 12:24 20
-
第八题:亥子合辰·塔影迷楼
基本信息
附件中的程序是 64 位 Windows PE,SHA-256 为:
b0dae7a21b059805a35fc5490f84a607d00afb28ad6b5b9f9ebc31574c382ea1
程序只接受 16 字节输入。导入表很小,文件读写、进程退出和时间查询均通过解析 ntdll 中的系统调用号后直接执行 syscall。
两层代码解密
入口首先读取 .tgt 中的偏移和长度,得到第一层区域:
RVA = 0x1800
size = 0x510
程序将该区域改成可写权限,每个字节异或 0x5A,刷新指令缓存后进入 0x1400018D0。
第一层代码对自身计算两组 FNV 风格的 64 位哈希。两个初值分别为 0xCBF29CE484222325 和 0x9E3779B97F4A7C15,乘数均为 0x100000001B3,最终结果为:
h0 = 0x73FB4F498AAB364F
h1 = 0xB157C7E044B966DF
随后解密第二层区域:
RVA = 0x1D10
size = 0x4962
标量路径可以写成:
MASK = (1 << 64) - 1
G = 0x9E3779B97F4A7C15
M = 0xFF51AFD7ED558CCD
for i in range(0x4962):
t = h0 ^ h1 ^ ((G * i) & MASK)
t ^= t >> 33
key = ((M * t) & MASK) >> 56
code[i] ^= key
程序还包含向量化实现,两条路径生成的密钥流相同。解密后,核心校验函数位于 0x140004380。
反调试和上下文生成
核心函数先检查以下状态:
- PEB 的
BeingDebugged和NtGlobalFlag; Dr0至Dr3、Dr7硬件断点寄存器;ProcessDebugPort与ProcessDebugObjectHandle;- 扫描代码段前后的
RDTSC差值。
这些状态合并成一个掩码。正常运行时掩码为 0。核心代码再次对已解密的第二层区域计算哈希,得到:
hash_golden = 0x4AEEE52B5738EB8F
hash_fnv = 0x553D3C5EF6EE11FF
两个哈希与反调试掩码进入 SplitMix64 风格的生成器,循环 43 次,产生 86 个 64 位字。后续数据全部来自这组确定性上下文:
- 第 0 至 9 个字组成五个素域多项式系数;
- 第 10、11 个字组成轮函数乘数;
- 第 12 至 43 个字组成 256 字节替换表;
- 第 44 至 83 个字组成两组各 20 个轮密钥;
- 最后两个字用于解开目标值和最终哈希。
素域与输入编码
所有 128 位运算都在以下素域中进行:
p = 2^127 - 39
= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFD9
输入首先满足字节和检查:
sum(input[0:16]) = 0x500
随后把可打印字符按 94 进制编码:
x = 0
for ch in input_bytes:
x = (x * 94 + ch - 33) % p
字符范围为 0x21 至 0x7E,所以每一位正好落在 0 至 93。94^16 小于 p,最终可以直接用 94 进制无歧义还原。
五个上下文字段记作 c0 至 c4。核心函数用 Horner 形式计算:
y = (((((x + c0)·x + c1)·x + c2)·x + c3)·x + c4) mod p
简化 20 轮变换
反编译结果将 128 位乘法和模约减完全展开,主体超过两千条指令。把重复片段合并后,每轮是两次 Feistel 更新。
设 C 为上下文生成的 128 位乘数,S 为逐字节替换表,定义:
t = (((A << 64) | k) · C) mod p
z = low64(t) XOR k XOR ROR64(high64(t), 47)
F(A, k) = S(z) XOR ROR64(z, 51)
单轮正向运算为:
A' = B XOR F(A, k)
B' = A XOR F(A', k XOR 0x9E3779B97F4A7C15)
因此可以直接逆运算:
A = B' XOR F(A', k XOR 0x9E3779B97F4A7C15)
B = A' XOR F(A, k)
两组 20 轮使用不同轮密钥和不同交替哈希,初始输入都是多项式结果 y。解开上下文掩码后的两个目标分别为:
target1 = 0x0C14DB1C20DD97A8B8FD0694401D3D03
target2 = 0x9A2789B6DE31C2DCACA5240A53FB78A6
分别逆转两组 20 轮,独立得到相同结果:
y = 0x4BE831B0AD3A2D361489375BBA3FB8DE
五次多项式求根
将 Horner 表达式展开后得到有限域上的五次多项式:
f(X) = X^5 + c0·X^4 + c1·X^3 + c2·X^2 + c3·X + c4 - y
先计算:
gcd(f(X), X^p - X)
这样可以保留 F_p 中的一次因子,再用 Cantor-Zassenhaus 方法确定性拆分。多项式只有一个域内根:
x = 0x174A5E1D4CF4F146E72F70092AC
把 x 连续除以 94,取 16 个余数并各加 33,得到:
kanxue@2o26o8!@#
该字符串满足全部条件:字节和为 0x500,多项式结果正确,两组 20 轮输出正确,两组最终 FNV 校验正确。原程序输出 correct。
Flag
kanxue@2o26o8!@#