-
-
[原创]# KCTF 2026 – Rosetta Calibration 题解(Writeup)
-
发表于: 2026-8-10 12:32 8
-
KCTF 2026 – Rosetta Calibration 题解(Writeup)
1. 题目信息
| 项目 | 内容 |
|---|---|
| 题目名称 | KCTF 2026 - Rosetta Calibration |
| 目标文件 | kctf_rosetta.exe(64 位 PE 控制台程序) |
| 文件 SHA256 | caaa1c964b15d9b3250be0dc64412e5f64c29e297a971c226e8dd705d6b87e67 |
| 公开有效对 | Name: CAAA1C964B15D9B3 / Serial: $/.;-):,(-#!%*-&?+@ |
| 题目目标 | 求出Name = KCTF 的唯一合法 Serial |
附带说明:本题所在环境原本配置了 ida-pro-mcp(MCP 服务监听于本机
127.0.0.1:13337),虽工具未直接暴露给会话,但通过 MCP SSE 协议(/sse建连 →/sse?session=...上 POSTinitialize/tools/callJSON-RPC 消息)直接调用了 IDA 的decompile、xrefs_to、find_regex、py_eval等能力完成分析。
2. 程序运行形态
========================================
KCTF 2026 ROSETTA TERMINAL
========================================
Sixteen symbols. Sixteen meanings.
One surviving record is the dictionary.
Name : KCTF
Serial : <输入>
- 从标准输入依次读取 Name 与 Serial(逐字节
ReadFile); - 输出三种失败态:
[-] Invalid name format.(exit 2)[-] The inscription cannot be translated.(exit 3)[-] Calibration rejected. Key incorrect.(exit 1)
- 成功时输出
[+] Calibration accepted. Key correct!(exit 0)。
3. 静态分析
3.1 关键字符串定位
用 find_regex 在 IDA 字符串中搜索,得到关键提示字符串及其引用位置:
| 地址 | 字符串 | 引用者 |
|---|---|---|
0x140002090 |
"Serial : " |
start |
0x1400020a4 |
"[-] Calibration rejected. Key incorrect.\r\n" |
start |
0x14000211c |
"[+] Calibration accepted. Key correct!\r\n" |
start |
0x140002173 |
题目标语 | start |
所有 xref 都落在单一函数 start(0x140001000,长度 0x389),即整个校验逻辑集中在一个函数中。另有三个子函数:
sub_1400013E0:由 Name 生成 16 字节期望值;sub_140001520/sub_140001540:Serial 符号 → 数值 0~15 的映射。
3.2 start 主校验流程(伪码要点)
// 读取 Name 到 v16(≤39 字节),长度记 v4(v5 = v4)
// 读取 Serial 到缓冲区(≤47 字节),长度记 v3
if (v4 - 33 >= 0xFFFFFFE0) { // 名字长度 v4 ∈ [0, 32]
v6 = 0;
while ((byte)(v16[v6] - 33) < 0x5E) { // 名字每个字符 ∈ '!'..'~'
if (v4 == ++v6) {
if (v3 == 19) { // ★ Serial 必须恰好 19 字符
v7 = 0; v8 = 16912; v9 = 0; v10 = 0;
do {
while (v7 > 14 || !_bittest(&v8, v7)) {
v11 = sub_140001520(serial[v7]); // 符号 → 0..15
if (v11 > 0xF || _bittest(&v9, v11)) goto FAIL;
v9 |= 1 << v11;
v14[v10++] = v11; // 解码结果(16 字节)
if (++v7 == 19) goto DONE;
}
if (serial[v7] != 45) goto FAIL; // '-' 分隔符
++v7;
} while (v7 != 19);
DONE:
if (v10 == 16 && v9 == 0xFFFF) { // 16 个值互异且为 0..15 全排列
sub_1400013E0(v16, v5, &Buffer); // 由 Name 求 16 字节
if (Buffer == v14) { SUCCESS } else { FAIL }
}
}
}
}
}
要点解读:
v8 = 16912 = 0x4210,置位的是第 4、9、14 位 → Serial 中索引 4、9、14 必须是'-'(0x2D)。- 因此 Serial 格式 =
4符号-4符号-4符号-4符号(共 19 字符); - 16 个符号经
sub_140001520映射为 015,且必须互异(v9 == 0xFFFF,即 015 全排列); - 最后把「符号解码出的 16 字节」与「Name 派生的 16 字节」做 128 位相等比较。
4. 核心算法还原
4.1 符号 → 数值(sub_140001520 + sub_140001540)
__int64 sub_140001540(char a1) {
v1 = a1 - 33;
if (v1 <= 0x1Fu) return dword_140002000[v1]; // 只认 '!'..'@'
return 0xFFFFFFFF;
}
__int64 sub_140001520(char a1) {
return (a1 == 40) ^ (unsigned)sub_140001540(); // '(' 时异或 1
}
dword_140002000(32 个 dword,py_eval 读出):
idx: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
chr: ! " # $ % & ' ( ) * + , - . / 0
val: 4 -1 7 12 1 9 -1 3 11 14 6 13 -1 8 10 -1
idx: 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
chr: 1 2 3 4 5 6 7 8 9 : ; < = > ? @
val: -1 -1 -1 -1 -1 -1 -1 -1 -1 15 5 -1 -1 -1 0 3
综合 ( 的异或修正后,有效的 16 个符号与数值一一对应(本题的“词典”):
| 符号 | ? | % | ( | @ | ! | ; | + | # | . | & | / | ) | $ | , | * | : |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
注:
(在表中为 3,但sub_140001520对其异或 1,实际映射为 2,正好与@(3) 区分开,避免冲突。
4.2 Name → 16 字节(sub_1400013E0)
v10[0] = xmmword_140002080; // = 00 01 02 ... 0F(16 字节)
v5 = 0; v3 = 0;
for (i = 0; i < name_len; i++) {
v6 = name[i] ^ (name[i] >> 4); // 半字节折叠
v5 += v6; // 字节累加和(mod 256)
v7 = ((v3 ^ v6) & 0xF); // v3 每轮 += 3
swap(v10[i & 0xF], v10[v7]); // 表内置换
v3 += 3;
}
// 输出 16 字节:
out[k] = v10[(v5 + k) & 0xF]; k = 0..7
out[8] = v10[(v5 & 0xF) ^ 8];
out[k] = v10[(v5 + k) & 0xF]; k = 9..14
out[15] = v10[(v5 - 1) & 0xF];
即:以 00..0F 为初始表,按名字逐字节做 RC4 风格的交换置乱,再按累加和 v5 偏移取出 16 字节(其内容仍是 015 的一个排列)。**校验本质 = 比较两个由 015 组成的排列是否一致。**
5. 求解
5.1 思路
对任意 Name:
- 用
sub_1400013E0的逻辑算出 16 字节排列P; - 对
P中每个值查符号表(值 → 符号)得到 16 个符号; - 按
4-4-4-4分组、以-连接,即得 Serial。
5.2 求解脚本(solve_rosetta.py)
TABLE = (4, 0xFFFFFFFF, 7, 12, 1, 9, 0xFFFFFFFF, 3, 11, 14, 6, 13,
0xFFFFFFFF, 8, 10, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 15, 5, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0, 3)
def sym_val(c): # sub_140001520
i = ord(c) - 33
if not (0 <= i < 32) or TABLE[i] == 0xFFFFFFFF:
return -1
return TABLE[i] ^ (1 if c == '(' else 0)
INV = {}
for c in map(chr, range(33, 65)):
v = sym_val(c)
if v >= 0: INV.setdefault(v, c)
def name2bytes(name): # sub_1400013E0
v10, v5, v3 = list(range(16)), 0, 0
for i, ch in enumerate(name):
v6 = (ord(ch) ^ (ord(ch) >> 4)) & 0xFF
v5 = (v5 + v6) & 0xFF
j = i & 0xF
v10[j], v10[((v3 ^ v6) & 0xF)] = v10[((v3 ^ v6) & 0xF)], v10[j]
v3 += 3
out = [v10[(v5 + k) & 0xF] for k in range(8)]
out.append(v10[(v5 & 0xF) ^ 8])
out += [v10[(v5 + k) & 0xF] for k in range(9, 15)]
out.append(v10[(v5 - 1) & 0xF])
return out
def serial_for(name):
P = name2bytes(name)
s = ''.join(INV[v] for v in P)
return s[:4] + '-' + s[4:8] + '-' + s[8:12] + '-' + s[12:16]
5.3 验证公开示例对
name : CAAA1C964B15D9B3
computed P : [12, 10, 8, 5, 11, 15, 13, 2, 7, 4, 1, 14, 9, 0, 6, 3]
serial vals : [12, 10, 8, 5, 11, 15, 13, 2, 7, 4, 1, 14, 9, 0, 6, 3]
EXAMPLE MATCH: True
脚本结果与题目公开对完全一致,证明算法还原无误。
6. 最终答案
Name : KCTF
Serial : &/@$-,*?:-!#)%-;+(.
端到端验证(运行真实 EXE)
$ printf '%s\n%s\n' 'KCTF' '&/@$-,*?:-!#)%-;+(.' | ./kctf_rosetta.exe
========================================
KCTF 2026 ROSETTA TERMINAL
========================================
Sixteen symbols. Sixteen meanings.
One surviving record is the dictionary.
Name : Serial :
[+] The symbols have found their meanings.
[+] Calibration accepted. Key correct!
(exit code 0)
同时以公开对 CAAA1C964B15D9B3 / $/.;-):,(-#!%*-&?+@ 做对照测试同样返回 exit 0,双向验证通过。
7. 总结
- 题目为「Rosetta 石碑」式换表题:Serial 是 015 排列的符号化编码(
!@子集中的 16 个符号),Name 经 RC4 式置换同样产生 0~15 的排列,二者相等即通过。 - 关键点:① Serial 固定 19 字符、
-位置由位图0x4210指定;② 符号表是「字典」;③ Name 长度不要求 16(示例为 16,KCTF 为 4 同样成立),置换由名字逐字节驱动。 - 求解只需正向实现
sub_1400013E0+ 查表逆映射,无需爆破。
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
最后于 2026-8-12 15:20
被阿捏利编辑
,原因:
赞赏
他的文章
赞赏
雪币:
留言: