首页
社区
课程
招聘
[原创] 看雪·2026 KCTF 第八题:亥子合辰·塔影迷楼
发表于: 3天前 16

[原创] 看雪·2026 KCTF 第八题:亥子合辰·塔影迷楼

3天前
16

赛马抽奖,开了两个 ChagptGPT 5.5 High 网页、一个本地 Codex GPT 5.5 High、一个本地 Codex GPT 5.6 Terra High 同时跑,最终一个网页20min率先解出获胜。5.6 Sol Cyber拒答,5.6 Terra虽然也触发了外审,但至少还能回来继续。

共消耗了25%的Plus周额度,绝对缩水了,Tibo的x.com下面一堆人在骂

2c7K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0K9r3q4@1k6%4m8@1i4K6u0W2j5$3!0E0i4K6u0r3M7$3S2S2M7X3g2Q4x3V1j5$3j5e0R3%4k6U0l9&6z5g2)9J5k6o6p5$3j5X3y4Q4x3X3b7^5x3$3g2S2i4K6u0V1j5e0R3^5x3W2)9J5k6o6y4W2y4r3x3&6x3r3b7J5j5X3c8S2x3R3`.`.


(人类)

分隔线

(AI)


kctf2026 CrackMe08 静态分析报告

1. 分析目标

本次分析目标是对样本 kctf2026_CrackMe08.exe 进行静态逆向分析,恢复程序的输入校验逻辑,并找出能够通过校验的正确输入。

最终得到的正确输入为:

kanxue@2o26o8!@#

注意:其中 2o26o8 中的两个 o 是小写英文字母 o,不是数字 0


2. 样本概况

项目 内容
文件名 kctf2026_CrackMe08.exe
文件类型 Windows PE 可执行文件
分析方式 静态分析为主
是否使用模拟器
是否直接动态执行主程序
目标 还原输入校验逻辑并求出正确输入

3. 总体分析结论

程序的校验逻辑并不是直接暴露在主函数中,而是经过了多层隐藏与加密处理。

整体流程可以概括为:

用户输入
  ↓
长度检查
  ↓
字节和检查
  ↓
base-94 编码映射
  ↓
模 p = 2^127 - 39 的域运算
  ↓
多轮二次型变换
  ↓
两段 20 轮 Feistel / 查表变换
  ↓
与目标状态比较
  ↓
输出成功或失败

最终逆向还原后,可以确定唯一可打印输入为:

kanxue@2o26o8!@#

4. 静态分析过程

4.1 初步定位

对 PE 文件进行静态检查后,发现主程序并未直接包含完整的明文校验逻辑。程序中存在一段被简单加密的数据,运行时会先进行解密,再跳转或调用解密后的代码。

第一层解密逻辑的特征较明显,主要使用固定异或常量:

xor key = 0x5A

因此可以通过静态方式直接对对应字节区域进行还原,得到第一层解密后的代码。


4.2 第一层解密

第一层解密后的代码并不是最终校验逻辑,而是继续负责释放或解密第二层核心校验函数。

该阶段说明程序采用了分层隐藏方式:

原始程序
  ↓
第一层 XOR 解密
  ↓
第二层校验函数解密
  ↓
真实校验逻辑

这种结构的作用是干扰直接字符串搜索和简单反汇编,使真正的校验逻辑不会直接出现在初始代码段中。


4.3 第二层校验函数恢复

第二层校验代码同样以字节流形式存在,需要通过还原其解密逻辑得到真实指令。

恢复后可以看到,核心校验函数并不是简单的逐字符比较,而是将输入整体映射为一个大整数,再进行复杂数学变换。


5. 输入基础约束

恢复校验逻辑后,首先可以得到两个非常明确的输入约束。

5.1 长度约束

输入长度必须为:

16 字节

即:

strlen(input) == 16

5.2 字节和约束

程序还检查输入所有字节的 ASCII 值之和,要求其等于:

0x500

换算为十进制:

1280

对最终答案进行验证:

kanxue@2o26o8!@#

逐字节 ASCII 求和:

字符 ASCII
k 107
a 97
n 110
x 120
u 117
e 101
@ 64
2 50
o 111
2 50
6 54
o 111
8 56
! 33
@ 64
# 35

求和结果:

107 + 97 + 110 + 120 + 117 + 101 + 64 + 50
+ 111 + 50 + 54 + 111 + 56 + 33 + 64 + 35
= 1280
= 0x500

满足字节和条件。


6. 核心算法结构

6.1 base-94 输入映射

通过静态还原可以发现,输入字符并不是被逐个独立校验,而是先被转换为一个大整数。

该转换使用类似 base-94 的编码方式,通常对应可打印 ASCII 字符范围。

逻辑可抽象为:

x = 0

for each char c in input:
    x = x * 94 + mapped_value(c)

其中 mapped_value(c) 通常与可打印字符区间相关。

这一步的作用是将 16 字节输入压缩成一个整体数值,使后续校验不再表现为简单的字符级判断。


6.2 模域运算

输入映射得到的大整数随后进入有限域运算,使用的模数为:

p = 2^127 - 39

即所有核心运算都在如下模域中进行:

mod p

该模数接近 128 位规模,能够有效避免简单枚举或直接逆推。


6.3 多轮二次型变换

在有限域中,程序对输入映射值进行多轮二次型运算。

从恢复出的逻辑看,该部分可以抽象为:

x = F1(x)
x = F2(x)
x = F3(x)
x = F4(x)
x = F5(x)

每一轮 Fi 都包含平方、乘法、加法以及模约减操作。

这类结构的目的通常是将输入空间打散,使输入与最终比较值之间不存在明显线性关系。


6.4 Feistel / 查表变换

在有限域运算之后,程序又进入两段 20 轮结构化变换。

其特征类似 Feistel 网络,并伴随查表操作:

for round in 0..19:
    L, R = R, L ^ f(R, round_key)

for round in 0..19:
    L, R = R, L ^ g(R, round_key)

Feistel 结构的特点是:

  1. 即使轮函数不可逆,整体结构仍然可逆;
  2. 每轮都会交换左右状态;
  3. 可通过反向轮序还原前一状态。

因此,在静态恢复出轮函数和常量表后,可以对目标状态进行逆变换,逐步还原输入对应的中间值。


7. 求解思路

由于程序的校验结构整体可逆,因此不需要暴力枚举完整 16 字节输入。

求解过程如下:

目标比较状态
  ↓
逆 Feistel 第二段
  ↓
逆 Feistel 第一段
  ↓
逆有限域二次型变换
  ↓
恢复 base-94 数值
  ↓
转回 16 字节可打印字符串

在恢复出的候选结果中,满足以下条件的唯一输入为:

kanxue@2o26o8!@#

该输入同时满足:

长度 == 16
字节和 == 0x500
字符均为可打印字符
通过还原出的完整校验逻辑

8. 结果验证

8.1 长度验证

kanxue@2o26o8!@#

字符数:

16

满足长度要求。

8.2 字节和验证

ASCII 求和:

0x500

满足字节和要求。

8.3 算法验证

将该输入代入静态还原出的校验算法:

输入 → base-94 映射 → 模域运算 → 多轮 Feistel / 查表变换

最终得到的状态与程序内置目标状态一致,因此判断该输入可以通过校验。


9. 正确输入

最终正确输入为:

kanxue@2o26o8!@#

特别注意:

kanxue@2o26o8!@#
        ^   ^
        o   o

这两个字符是小写字母 o


10. 总结

该 CrackMe 的主要保护点包括:

  1. 多层代码加密
    主程序中没有直接暴露真实校验逻辑,需要先还原两层解密代码。

  2. 整体输入映射
    输入不是逐字符比较,而是被映射为一个大整数。

  3. 有限域复杂运算
    程序使用模 2^127 - 39 的大数域运算,增加了静态分析难度。

  4. Feistel 结构混淆
    多轮 Feistel / 查表变换进一步隐藏输入与目标状态之间的关系。

  5. 可逆结构可被静态求解
    虽然算法复杂,但其核心结构可逆,因此通过静态还原和逆向推导可以直接得到正确输入。

最终得到的 flag / 正确输入为:

kanxue@2o26o8!@#

冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。

最后于 3天前 被mb_mgodlfyn编辑 ,原因:
收藏
免费 0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回