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"

最终FlagKCTF{_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)

最终FlagKCTF{_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校验流程

  1. 变换函数sub_14C0() 对输入进行加密
  2. 分段比较:与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)

最终FlagKCTF{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 加密变换流程

  1. 数据拼接:将3段rodata数据拼接成38字节参考串
  2. 流加密:使用置换表进行逐字节变换
  3. 状态更新:每轮更新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)

最终FlagKCTF{aN0Th3r_r3_I_h0PE_y0U_eNj0YED_IT}

5. 通用逆向工程技术总结

5.1 常见保护技术

  1. 哈希校验:MD5、FNV等哈希算法验证
  2. 置换表:动态生成的S-box增加逆向难度
  3. 流加密:基于内部状态的逐字节加密
  4. 虚假路径:多个校验通道迷惑分析者

5.2 破解方法论

  1. 静态分析:IDA Pro反编译理解程序逻辑
  2. 动态调试:GDB/LLDB跟踪程序执行流程
  3. 约束求解:Z3等工具自动化求解
  4. 暴力枚举:针对小范围密钥空间的爆破

5.3 代码实现要点

  • 精确复现原程序的位运算和算术操作
  • 注意大小端序和整数溢出处理
  • 验证中间结果确保逆向正确性

本教学文档完整覆盖了KnightCTF 2026中4个Reverse题目的技术细节和解决方案,为CTF逆向工程学习提供了完整的参考案例。

 全屏