KnightCTF 2026 reverse全解
字数 1401
更新时间 2026-01-31 12:09:47
KnightCTF 2026 Reverse Engineering 全解析教学文档
1. E4sy_P3asy 题目解析
1.1 题目基本信息
- 文件类型:KS文件(实为ELF可执行文件)
- 校验方式:MD5哈希碰撞
- Flag格式:KCTF{...}
1.2 核心逻辑分析
1.2.1 基础校验
// 前缀校验
strncmp(s, "KCTF{", strlen("KCTF{")) == 0
// 后缀校验
s[strlen(s)-1] == '}'
// 长度校验:24字符
1.2.2 MD5校验机制
- Salt值:
"KnightCTF_2026_s@lt" - 哈希目标:23个已知MD5哈希值
- 构造方式:
MD5(salt + str(i) + flag_char)
1.3 破解脚本实现
import hashlib
import string
def md5_hex(s: str) -> str:
return hashlib.md5(s.encode()).hexdigest()
def solve_easy():
salt = "KnightCTF_2026_s@lt"
hashes = [
"781011edfb2127ee5ff82b06bb1d2959", "4cf891e0ddadbcaae8e8c2dc8bb15ea0",
# ... 完整哈希列表见原文
]
charset = string.printable # 可打印字符集
flag_chars = []
for i, target_hash in enumerate(hashes):
for char in charset:
test_str = f"{salt}{i}{char}"
if md5_hex(test_str) == target_hash:
flag_chars.append(char)
break
return "KCTF{" + "".join(flag_chars) + "}"
1.4 假Flag机制
- 存在另一组Salt:
"G00gleCTF_s@lt_2026" - 长度要求:24字符
- 输出提示:"decoy flag"
最终Flag:KCTF{_L0TS_oF_bRuTE_foRCE_:P}
2. KrackM3 题目解析
2.1 题目特征
- 文件类型:Linux可执行文件
- 校验方式:复杂的状态机校验
- 保护机制:动态调试对抗
2.2 核心逻辑分析
2.2.1 基础格式校验
// 长度必须为32字节
if (strlen(input) != 0x20) return FAIL;
// 格式校验:KCTF{...}
if (*(uint32_t*)input != 0x4654434B) return FAIL; // "KCTF"
if (input[4] != '{') return FAIL;
if (input[31] != '}') return FAIL;
2.2.2 核心校验函数 sub_401590
- 维护16字节状态寄存器
- 使用4组常量数据进行校验
- 每轮计算依赖前一轮状态
2.3 动态求解方法
2.3.1 状态初始化
# 初始状态
r8 = 0xB1EB4606F35CF7F9
r9 = 0x881DB3E005D90DFF
state = [0x42, 0x19, 0xA7, 0x5C, 0xD3, 0x0E, 0x91, 0x2F,
0x33, 0x77, 0xB1, 0x08, 0xE9, 0x4D, 0x12, 0x6A]
2.3.2 逐字节爆破算法
def solve_krackm3():
target_bytes = [...] # 从内存中提取的32字节目标值
flag = ["?"] * 32
flag[0:5] = list("KCTF{")
flag[31] = "}"
# 初始化状态
current_state = init_state()
for position in range(32):
if flag[position] != "?": # 已知字符
update_state(current_state, ord(flag[position]))
continue
# 枚举当前字节
for candidate in range(256):
temp_state = current_state.copy()
result_byte = simulate_round(temp_state, candidate, position)
if result_byte == target_bytes[position]:
flag[position] = chr(candidate)
current_state = temp_state
break
return "".join(flag)
最终Flag:KCTF{_R3_iS_FuNR1gHT?_EnjOy_r3_}
3. ReM3 题目解析
3.1 题目要求
- 输入长度:29字节
- 校验方式:多层变换+常量比较
3.2 核心逻辑分析
3.2.1 假Flag机制
// FNV-1a 64位哈希校验
if (fnv1a_hash(input) == 0xE76FA3DABA5D6F3A) {
printf("KCTF{hash_passes_but_fake!!!}");
}
3.2.2 真Flag校验流程
- 变换函数:
sub_14C0()对输入进行加密 - 分段比较:与3组内存常量进行比对
- 偏移0-7:8字节
- 偏移8-17:10字节
- 偏移18-26:9字节
3.3 逆向求解算法
3.3.1 解密函数实现
def decrypt_reM3():
# 密文数据
cipher_segments = [
[0xDC,0x6B,0xBB,0x4D,0xFD,0x25,0xE4,0x7E,0xC3,0x26], # 10字节
[0xF5,0x72,0xAB,0x96,0xFC,0x8D,0x55,0x10,0x93,0xC1], # 10字节
[0xFD,0x81,0x46,0x5B,0x7E,0x33,0x83,0x8F,0x2F] # 9字节
]
cipher = bytes(cipher_segments[0] + cipher_segments[1] + cipher_segments[2])
# 逆向变换参数
C1 = 0x2F910ED35CA71942
v2, v3, v4 = 0, 0, -61 & 0xFFFFFFFF
plain = []
for i in range(29):
c = cipher[i]
c1_byte = (C1 >> (8 * (i & 7))) & 0xFF
# 逆向ROL8操作
v6 = (v3 + c1_byte) & 0xFF
v7 = rol8(c, 5) # ROL8(c, 5)
v8 = (v7 - v6) & 0xFF
v9 = (v8 ^ v2) & 0xFF
plain.append(v9)
# 更新状态
v2 = (v2 + v4) & 0xFFFFFFFF
v3 = (v3 - v4) & 0xFFFFFFFF
v4 = (v4 - 13) & 0xFFFFFFFF
return "".join(chr(b) for b in plain)
最终Flag:KCTF{w3Lc0m3_T0_tHE_r3_w0rLD}
4. ReM3_Again 题目解析
4.1 题目特征
- 输入长度:38字节
- 保护机制:置换表+流加密
4.2 核心逻辑分析
4.2.1 置换表生成
def generate_permutation_table():
perm = list(range(256))
state = 0xF5A4ADA5
for pos in range(255, -1, -1):
# Xorshift32 PRNG
state = (state ^ (state << 13)) & 0xFFFFFFFF
state = (state ^ (state >> 17)) & 0xFFFFFFFF
state = (state ^ (state << 5)) & 0xFFFFFFFF
j = state % (pos + 1)
perm[pos], perm[j] = perm[j], perm[pos]
# 生成逆置换表
inv_table = [0] * 256
for i, val in enumerate(perm):
inv_table[val] = i
return inv_table
4.2.2 加密变换流程
- 数据拼接:将3段rodata数据拼接成38字节参考串
- 流加密:使用置换表进行逐字节变换
- 状态更新:每轮更新16字节内部状态
4.3 完整求解算法
4.3.1 参考数据提取
def get_reference_data():
x_r0 = [0x0F,0x5F,0x6F,0xF7,0x01,0x0F,0xB2,0x18,0xEE,0xD3,0x37,0x84]
x_r1 = [0xD2,0x4C,0xA4,0x79,0x4A,0xF7,0x18,0xCF,0x68,0x78,0x26,0xFC,0x37]
x_r2 = [0xD0,0x8B,0x99,0x5A,0xE7,0xF5,0x46,0x37,0x82,0x48,0xFB,0xFE,0x0B]
return x_r0 + x_r1 + x_r2 # 总共38字节
4.3.2 主变换函数
def solve_rem3_again():
ref_data = get_reference_data()
inv_table = generate_permutation_table()
# 初始状态(注意字节序)
init_state = [0x42,0x19,0xA7,0x5C,0xD3,0x0E,0x91,0x2F,
0x33,0x77,0xB1,0x08,0xE9,0x4D,0x12,0x6A]
state = init_state.copy()
C2 = 0x6A124DE908B17733
output = []
for i in range(38):
# 获取常量字节
const_byte = (C2 >> (8 * (i & 7))) & 0xFF
# 状态索引计算
idx = (state[i % 16] + const_byte) & 0xFF
idx2 = (ref_data[i] + i) & 0xFF
# 逆置换操作
v10 = inv_table[idx2]
v11 = (v10 - idx) & 0xFF
output.append(v11)
# 状态更新
state[i % 16] = rol8(state[i % 16], 3) ^ v11
return "".join(chr(b) for b in output)
最终Flag:KCTF{aN0Th3r_r3_I_h0PE_y0U_eNj0YED_IT}
5. 通用逆向工程技术总结
5.1 常见保护技术
- 哈希校验:MD5、FNV等哈希算法验证
- 置换表:动态生成的S-box增加逆向难度
- 流加密:基于内部状态的逐字节加密
- 虚假路径:多个校验通道迷惑分析者
5.2 破解方法论
- 静态分析:IDA Pro反编译理解程序逻辑
- 动态调试:GDB/LLDB跟踪程序执行流程
- 约束求解:Z3等工具自动化求解
- 暴力枚举:针对小范围密钥空间的爆破
5.3 代码实现要点
- 精确复现原程序的位运算和算术操作
- 注意大小端序和整数溢出处理
- 验证中间结果确保逆向正确性
本教学文档完整覆盖了KnightCTF 2026中4个Reverse题目的技术细节和解决方案,为CTF逆向工程学习提供了完整的参考案例。