首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
CTF对抗
发新帖
0
0
[原创]wp:第八题:亥子合辰·塔影迷楼
发表于: 2026-8-21 12:51
21
[原创]wp:第八题:亥子合辰·塔影迷楼
the_hs
1
2026-8-21 12:51
21
## 1. 程序结构 程序是 64 位 PE,主要逻辑不在明文代码里,而是分两段自解密: ### 第一层解密 入口 `start`: - 取模块基址; - 对 `image + 0x1800` 开始的 `0x510` 字节异或 `0x5A`; - `VirtualProtect` 改成可写执行; - `FlushInstructionCache` 刷新指令缓存。 对应代码区域: ```text 0x140001800 .. 0x140001D0F ``` 之后程序: 1. 输出 `Input:`; 2. 读取最多 63 字节; 3. 去掉结尾 `\r\n`; 4. 要求输入长度必须为 16; 5. 调用第一层里的校验包装函数 `0x1400018D0`。 ## 2. 第二层解密 第一层函数 `0x1400018D0` 会先对第一层明文代码做两个 FNV-1a 风格的 64 位哈希: ```c h1 = 0xCBF29CE484222325; h2 = 0x9E3779B97F4A7C15; for (b in layer1) { h1 = (h1 ^ b) * 0x100000001B3; h2 = (h2 ^ b) * 0x100000001B3; } ``` 得到: ```text h1 = 0x73FB4F498AAB364F h2 = 0xB157C7E044B966DF ``` 然后用这两个哈希生成逐字节密钥流,解密: ```text image + 0x1D10 size = 0x4962 ``` 即: ```text 0x140001D10 .. 0x140006671 ``` 单字节解密逻辑可写成: ```python ctr = i * 0x9E3779B97F4A7C15 x = ctr ^ h1 ^ h2 x ^= x >> 33 x *= 0xFF51AFD7ED558CCD key = x >> 56 plain[i] = enc[i] ^ key ``` 第二层入口是: ```text 0x140001D10 ``` 它根据输入首字节查表,但表里的 8 个函数指针全部相同,最终都进入: ```text 0x140004380 ``` ## 3. 输入约束 校验函数首先计算 16 个输入字节的和: ```c sum(input[i]) == 0x500 ``` 也就是: ```text sum = 1280 ``` 之后输入被编码成一个有限域元素: ```python d[i] = input[i] - 33 s = 0 for i in range(16): s = (s * 94 + d[i]) % p ``` 其中: ```text p = 2^127 - 39 ``` 由于 `byte - 33`,可打印字符 `[33,126]` 正好被映射成 base-94 数字。 ## 4. 有限域运算 大量看起来很复杂的 128 位运算,本质上都是模: ```text p = 2^127 - 39 ``` 汇编中反复出现的: ```text 39 78 0x7FFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFD9 ``` 含义是: ```text 2^128 mod p = 78 p = 2^127 - 39 ``` 所以这些代码是在做: - 128 位加法取模; - 128 位乘法取模; - 条件约减。 ## 5. PRNG 与轮密钥 程序利用两个哈希值和固定常量生成 86 个 64 位伪随机数 `P[0..85]`。 其中: - `P[0..9]`:组成 5 个有限域多项式系数; - `P[10..11]`:组成乘法常数; - `P[12..43]`:构造 256 字节 S-box; - `P[44..63]`:第一组 20 轮密钥; - `P[64..83]`:第二组 20 轮密钥; - `P[84..85]`:和 `.data` 常量异或,得到最终期望密文和哈希。 轮函数是一个两轮结构的 Feistel-like 变换,核心形式为: ```python t = ((H << 64 | k) % p) * M % p y = lo64(t) ^ k ^ ror64(hi64(t), 47) H2 = L ^ S(y) ^ ror64(y, 51) k2 = k ^ 0x9E3779B97F4A7C15 u = ((H2 << 64 | k2) % p) * M % p z = lo64(u) ^ k2 ^ ror64(hi64(u), 47) L2 = H ^ S(z) ^ ror64(z, 51) ``` 其中 `S(y)` 是对 `y` 的 8 个字节分别查 S-box。 由于 Feistel 结构可逆,因此可以从最终期望值反向推出多项式输出目标。 ## 6. 反推目标值 反向执行两组 20 轮变换后,两者得到相同的中间状态: ```text H = 0x4BE831B0AD3A2D36 L = 0x1489375BBA3FB8DE ``` 合并成有限域元素: ```text target = 0x4BE831B0AD3A2D361489375BBA3FB8DE ``` ## 7. 解五次多项式 由 PRNG 得到的五个系数为: ```text c0 = 0x21B3BDE1ACF9ADFA471FB28461A53BB5 c1 = 0x191A3870AD3D941105F15A9C38AAE874 c2 = 0x40C190E211322CAA2F01797348F02C97 c3 = 0x1DF732140B8A3EF758FD09BDE0058A37 c4 = 0x3F0CB8512C6F88E47E5F164FB478AAB5 ``` 需要解: ```text ((((s + c0) * s + c1) * s + c2) * s + c3) * s + c4 = target (mod p) ``` 也就是模 `p` 下的五次方程。 对多项式在 `GF(p)` 上因式分解,可得到一个线性根: ```text s = 0x174A5E1D4CF4F146E72F70092AC ``` ## 8. 还原输入 因为 `s` 是 16 位 base-94 编码,并且每个字节满足: ```python input[i] = digit[i] + 33 ``` 转换代码: ```python s = 0x174A5E1D4CF4F146E72F70092AC digits = [] for _ in range(16): digits.append(s % 94) s //= 94 flag = bytes(x + 33 for x in digits[::-1]) print(flag) ``` 得到: ```text kanxue@2o26o8!@# ``` 校验和也满足: ```python sum(b"kanxue@2o26o8!@#") == 1280 ``` 最终答案: ```text kanxue@2o26o8!@# ```
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
收藏
・
0
点赞
・
0
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
the_hs
1
18
发帖
22
回帖
90
RANK
关注
私信
他的文章
[原创]wp:第八题:亥子合辰·塔影迷楼
21
[原创] wp: 第七题:戌时·暗能潜流
51
[原创]wp: 第二题:巳时·绿光幽语
32
[讨论]被kimi k3吓哭
344
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部