-
-
[分享]KCTF 2026 - Rosetta Calibration
-
发表于: 2026-8-13 20:49 12
-
题目概述
kctf_rosetta.exe 是一个 64 位 Windows 控制台程序(PE32+ / x86-64),纯静态链接,无加壳,.text 段仅约 0x556 字节。
- 已知合法键对:
- Name :
CAAA1C964B15D9B3 - Serial :
$/.;-):,(-#!%*-&?+@
- Name :
- 目标:求出 Name =
KCTF的 Serial。 - 程序提示语:
Sixteen symbols. Sixteen meanings. One surviving record is the dictionary.(16 个符号 ↔ 16 个含义,唯一的幸存记录就是“字典”)
解题环境
ubuntu + objdump + wine
一、基本信息收集
file kctf_rosetta.exe
# PE32+ executable for MS Windows 6.00 (console), x86-64
sha256sum kctf_rosetta.exe
# caaa1c964b15d9b3250be0dc64412e5f64c29e297a971c226e8dd705d6b87e67 (与 PUBLIC_INFO 一致)
strings -a kctf_rosetta.exe
# Serial : / Name : / [-] Calibration rejected. / [-] Invalid name format.
# [-] The inscription cannot be translated.
# [+] The symbols have found their meanings. / [+] Calibration accepted.
# 导出: ExitProcess / GetStdHandle / ReadFile / WriteFile (KERNEL32.dll)
程序逻辑非常直白:读入 Name → 读入 Serial → 一系列校验 → 输出成功/失败信息。
二、逆向算法
用 objdump -d kctf_rosetta.exe 得到完整反汇编,逐段还原。
1. 输入与格式校验
Name 长度(0x14000123E):
lea -0x21(%rbx),%eax ; eax = len - 0x21
cmp $0xffffffe0,%eax
jae 0x140001260 ; 合法则继续
; 否则打印 "Invalid name format."
利用无符号比较技巧:len ∈ [1, 32] 才合法。
Name 字符范围(0x140001270 循环):
add $0xdf,%cl
cmp $0x5e,%cl
jae error
等价于要求每个字符 c ∈ [0x21, 0x7E](可打印 ASCII ! ~ ~)。
Serial 长度(0x140001288):
cmp $0x13,%r12d
jne 0x140001354 ; 必须恰好 19 字符
Serial 分隔符(0x140001292 起):
mov $0x4210,%ebp ; 二进制 0100 0010 0001 0000 -> bit4/bit9/bit14
bt %r14d,%ebp
jb 0x1400012b0 ; 这三位必须是 '-'
即 Serial 格式为 XXXX-XXXX-XXXX-XXXX,第 4、9、14 位为 -,其余 16 位是“符号”。
2. 字典解码(Serial → 16 个 nibble)
函数 0x140001540:
add $0xdf,%cl ; 相当于 c - 0x21 (对 '!'..'@')
cmp $0x1f,%cl
ja return -1
mov 0x140002000(%rax*4),%eax ; 查字典表
0x140002000 处的 32 个 DWORD 就是“字典”(每个索引对应一个字符 !~`@`):
idx: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
val: 04 - 07 0c 01 09 - 03 0b 0e 06 0d - 08 0a -
idx: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
val: - - - - - - - - - - 0f 05 - - - 03
字符到 nibble 的唯一映射(注意函数 0x140001520 对 '(' 额外 ^1,即 '(' 表值 3 → 实际 2,弥补表中缺失的 nibble 2):
| nibble | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
|---|---|---|---|---|---|---|---|---|---|----|----|----|----|----|----|
| 字符 | ? | % | ( | @ | ! | ; | + | # | . | & | / | ) | $ | , | * | : |
校验中还有:
- 解码值必须
≤ 0xF(合法 nibble); - 用位掩码
edi检查 16 个 nibble 互不重复,最终edi == 0xFFFF; - 数量必须恰好 16 个。
即 Serial 的 16 个符号解码成 0~15 的一个排列。
3. Name → 期望 nibble(核心函数 0x1400013E0)
参数:rcx = name, edx = len, r8 = 输出 16 字节。
uint8_t T[16] = {0,1,2,...,15};
uint32_t sum = 0, r9 = 0;
for (int i = 0; i < len; i++) {
uint8_t c = name[i];
uint32_t nib = (c >> 4) ^ c; // 高半字节 ^ 低半字节
sum += nib;
uint32_t j = (nib ^ r9) & 0xF; // r9 = 3*i
uint32_t k = i & 0xF;
swap(T[k], T[j]); // 交换
r9 += 3;
}
int p = sum & 0xF;
for (int k = 0; k < 16; k++)
out[k] = T[(p + k) & 0xF];
说明:
r9每轮累加 3,等价于3*i,注意它并不是死代码,它参与交换下标j的异或运算。- 输出位置 8 在汇编里用
T[(p & 0xF) ^ 8],因x ^ 8 == (x+8) & 0xF,等价于T[(p+8)&0xF],只是编译器的优化写法。 - 输出第 15 位用
T[(p-1) & 0xF],同样等价于(p+15)&0xF。
4. 最终比对
movdqa 0x40(%rsp),%xmm0 ; Serial 解码出的 16 个 nibble
pcmpeqb 0x30(%rsp),%xmm0 ; Name 计算出的 16 个期望 nibble
pmovmskb %xmm0,%eax
xor $0xffff,%eax
jne rejected
Serial 解码得到的排列必须与 Name 推导出的排列逐字节相等。
三、编写 Keygen 并验证
按上述算法用 Python 实现,先用已知合法键对自检:
TABLE = [0x04,-1,0x07,0x0c,0x01,0x09,-1,0x03,
0x0b,0x0e,0x06,0x0d,-1,0x08,0x0a,-1,
-1,-1,-1,-1,-1,-1,-1,-1,
-1,0x0f,0x05,-1,-1,-1,0x00,0x03]
INV = {4:'!', 1:'%', 7:'#',12:'$',9:'&',3:'@',11:')',14:'*',
6:'+',13:',',8:'.',10:'/',15:':',5:';',0:'?',2:'('}
def name_nibbles(name):
T = list(range(16)); s = 0; r9 = 0
for i, ch in enumerate(name):
c = ord(ch)
nib = (c >> 4) ^ c
s += nib
j = (nib ^ r9) & 0xF
T[i & 0xF], T[j] = T[j], T[i & 0xF]
r9 += 3
p = s & 0xF
return [T[(p+k) & 0xF] for k in range(16)]
def make_serial(name):
n = name_nibbles(name)
s = ''.join(INV[v] for v in n)
return s[0:4]+'-'+s[4:8]+'-'+s[8:12]+'-'+s[12:16]
自检结果:对合法 Name CAAA1C964B15D9B3 生成的 Serial 恰为 $/.;-):,(-#!%*-&?+@,与题面完全一致,算法正确。
四、求解目标
Name = KCTF
期望 nibbles = [9,10,3,12, 13,14,0,15, 4,7,11,1, 5,6,2,8]
Serial = &/@$-,*?:-!#)%-;+(.
五、真机验证(Wine)
安装 wine 后直接运行验证:
$ printf 'KCTF\n&/@$-,*?:-!#)%%-;+(.\n' | wine kctf_rosetta.exe
...
[+] The symbols have found their meanings.
[+] Calibration accepted. Key correct!
用错误的 Serial 测试则被拒绝:
$ printf 'KCTF\nAAAAAAAAAAAAAAAAAAA\n' | wine kctf_rosetta.exe
[-] The inscription cannot be translated.
最终答案
Name : KCTF
Serial : &/@$-,*?:-!#)%-;+(.
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。