【攻防世界】reverse | IgniteMe 详细题解 WP

【攻防世界】reverse | IgniteMe 详细题解 WP

下载附件

main函数伪代码:

c 复制代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  size_t i; // [esp+4Ch] [ebp-8Ch]
  char v5[8]; // [esp+50h] [ebp-88h] BYREF
  char Str[128]; // [esp+58h] [ebp-80h] BYREF

  sub_402B30(&unk_446360, "Give me your flag:");
  sub_4013F0(sub_403670);
  sub_401440(Str, 127);
  if ( strlen(Str) < 0x1E && strlen(Str) > 4 )
  {
    strcpy(v5, "EIS{");
    for ( i = 0; i < strlen(v5); ++i )
    {
      if ( Str[i] != v5[i] )
        goto LABEL_7;
    }
    if ( Str[28] != 125 )
    {
LABEL_7:
      sub_402B30(&unk_446360, "Sorry, keep trying! ");
      sub_4013F0(sub_403670);
      return 0;
    }
    if ( (unsigned __int8)sub_4011C0(Str) )
      sub_402B30(&unk_446360, "Congratulations! ");
    else
      sub_402B30(&unk_446360, "Sorry, keep trying! ");
    sub_4013F0(sub_403670);
    return 0;
  }
  else
  {
    sub_402B30(&unk_446360, "Sorry, keep trying!");
    sub_4013F0(sub_403670);
    return 0;
  }
}

sub_4013C0函数伪代码:

c 复制代码
int __cdecl sub_4013C0(int a1)
{
  return (a1 ^ 0x55) + 72;
}

sub_4011C0函数伪代码:

c 复制代码
bool __cdecl sub_4011C0(char *Str)
{
  int v2; // [esp+50h] [ebp-B0h]
  char Str2[32]; // [esp+54h] [ebp-ACh] BYREF
  int v4; // [esp+74h] [ebp-8Ch]
  int v5; // [esp+78h] [ebp-88h]
  size_t i; // [esp+7Ch] [ebp-84h]
  char v7[128]; // [esp+80h] [ebp-80h] BYREF

  if ( strlen(Str) <= 4 )
    return 0;
  i = 4;
  v5 = 0;
  while ( i < strlen(Str) - 1 )
    v7[v5++] = Str[i++];
  v7[v5] = 0;
  v4 = 0;
  v2 = 0;
  memset(Str2, 0, sizeof(Str2));
  for ( i = 0; i < strlen(v7); ++i )
  {
    if ( v7[i] >= 97 && v7[i] <= 122 )
    {
      v7[i] -= 32;
      v2 = 1;
    }
    if ( !v2 && v7[i] >= 65 && v7[i] <= 90 )
      v7[i] += 32;
    Str2[i] = byte_4420B0[i] ^ sub_4013C0(v7[i]);
    v2 = 0;
  }
  return strcmp("GONDPHyGjPEKruv{{pj]X@rF", Str2) == 0;
}

exp:

python 复制代码
def ignite_me_decrypt():
    # 1. 已知常量定义
    str2_target = "GONDPHyGjPEKruv{{pj]X@rF"  # 目标对比字符串(24字节)
    byte_4420B0 = [                           # 异或密钥数组(24字节)
        0x0D, 0x13, 0x17, 0x11, 0x02, 0x01,
        0x20, 0x1D, 0x0C, 0x02, 0x19, 0x2F,
        0x17, 0x2B, 0x24, 0x1F, 0x1E, 0x16,
        0x09, 0x0F, 0x15, 0x27, 0x13, 0x26
    ]
    flag_prefix = "EIS{"                      # flag前缀
    flag_suffix = "}"                         # flag后缀
    core_chars = []                           # 存储核心24个字符

    # 校验基础条件(避免输入错误)
    assert len(str2_target) == 24, "目标字符串长度必须为24字节"
    assert len(byte_4420B0) == 24, "密钥数组长度必须为24字节"

    # 2. 逐字符逆运算推导核心区域
    for i in range(24):
        # 步骤1:异或逆运算 → 得到sub_4013C0的返回值
        target_ascii = ord(str2_target[i])
        key_byte = byte_4420B0[i]
        sub_result = key_byte ^ target_ascii  # 异或逆运算(异或的逆是自身)

        # 步骤2:自定义加密逆运算 → 得到transformed_c[i]的ASCII码
        # 正向:sub_result = (ord_t ^ 0x55) + 72 → 逆向:ord_t = (sub_result -72) ^ 0x55
        ord_t = (sub_result - 72) ^ 0x55
        transformed_c = chr(ord_t)

        # 步骤3:大小写逆变换 → 得到原始核心字符c[i]
        if 65 <= ord_t <= 90:
            # transformed_c是大写 → 原始字符是小写(+32)
            original_char = chr(ord_t + 32)
        elif 97 <= ord_t <= 122:
            # transformed_c是小写 → 原始字符是大写(-32)
            original_char = chr(ord_t - 32)
        else:
            # 非字母字符 → 直接保留
            original_char = transformed_c

        core_chars.append(original_char)

    # 3. 拼接flag并验证格式
    flag_core = ''.join(core_chars)
    flag = f"{flag_prefix}{flag_core}{flag_suffix}"

    # 格式校验(确保符合main函数约束)
    assert len(flag) == 29, f"Flag长度错误:实际{len(flag)},预期29"
    assert flag.startswith(flag_prefix), f"Flag前缀错误:应为{flag_prefix},实际{flag[:4]}"
    assert flag.endswith(flag_suffix), f"Flag后缀错误:应为{flag_suffix},实际{flag[-1]}"

    return flag

# 执行解密,得到最终flag
if __name__ == "__main__":
    final_flag = ignite_me_decrypt()
    print(f"最终Flag:{final_flag}")

运行 exp 脚本:

复制代码
EIS{wadx_tdgk_aihc_ihkn_pjlm}

【攻防世界】reverse | IgniteMe 详细题解 WP 原理深度解析:

CTF 逆向实战:从二进制到 Flag------IgniteMe 题目全解析

本题是典型的 "格式约束 + 大小写变换 + 自定义函数加密 + 异或" 逆向题,核心难点在于拆解 sub_4013C0 自定义加密函数、精准还原大小写变换规则,并通过逆运算从目标字符串反推 flag。以下是从逻辑拆解到代码实现的完整流程。

一、程序核心逻辑总览(从入口到验证)

1.1 入口函数 main:flag 格式硬性约束

main 函数直接定义了 flag 的合法格式,是解题的 "第一把钥匙":

c 复制代码
int __cdecl main(int argc, const char **argv, const char **envp) {
    char Str[128]; // 输入的flag
    char v5[8];    // 存储前缀"EIS{"
    strcpy(v5, "EIS{");
    
    // 1. 长度约束:5 < len(Str) < 30 → 实际必须是29字节(因Str[28]是'}')
    if (strlen(Str) < 0x1E && strlen(Str) > 4) {
        // 2. 前缀约束:前4字符必须是"EIS{"
        for (i = 0; i < strlen(v5); ++i) 
            if (Str[i] != v5[i]) goto 错误;
        // 3. 后缀约束:第29个字符(索引28)必须是'}'(ASCII=125)
        if (Str[28] != 125) goto 错误;
        // 4. 核心验证:调用sub_4011C0判断flag正确性
        if (sub_4011C0(Str)) 输出"Congratulations!";
        else 输出"Sorry, keep trying!";
    }
}

flag 格式结论

  • 总长度:29 字节(EIS{xxxxxxxxxxxxxxxxxxxxxxxx});
  • 固定框架:前缀 EIS{(4 字节),后缀 }(1 字节);
  • 核心区域:{} 内的 24 个字符(Str[4] ~ Str[27]),是解题核心。

1.2 核心验证函数 sub_4011C0:flag 正确性判断逻辑

sub_4011C0 是判断 flag 正确的核心,输入为完整 flag,返回 bool 值。其正向流程(程序处理 flag 的过程)可拆解为 4 步,逆向解题需完全反向操作:

c 复制代码
bool __cdecl sub_4011C0(char *Str) {
    char v7[128];   // 存储flag核心区域(Str[4]~Str[27])
    char Str2[32];  // 存储加密后的结果(用于和目标字符串对比)
    size_t i;

    // 步骤1:提取核心区域(Str[4]~Str[27],共24个字符)
    i = 4;
    v5 = 0;
    while (i < strlen(Str) - 1) // strlen(Str)=29 → i < 28
        v7[v5++] = Str[i++];
    v7[v5] = 0; // v7长度=24

    // 步骤2:核心区域大小写变换(关键预处理)
    v2 = 0;
    memset(Str2, 0, sizeof(Str2));
    for (i = 0; i < strlen(v7); ++i) {
        if (v7[i] >= 97 && v7[i] <= 122) { // 若为小写字母(a-z)
            v7[i] -= 32; // 转大写(a→A)
            v2 = 1;      // 标记已处理小写
        }
        if (!v2 && v7[i] >= 65 && v7[i] <= 90) { // 若为大写字母且未处理小写
            v7[i] += 32; // 转小写(A→a)
        }

        // 步骤3:自定义加密 + 异或(核心加密逻辑)
        Str2[i] = byte_4420B0[i] ^ sub_4013C0(v7[i]);
        v2 = 0; // 重置标记
    }

    // 步骤4:验证加密结果是否等于目标字符串
    return strcmp("GONDPHyGjPEKruv{{pj]X@rF", Str2) == 0;
}

1.3 关键辅助函数 sub_4013C0:自定义加密逻辑

sub_4013C0 是单字符输入、单值输出的加密函数,伪代码明确其功能:

c 复制代码
int __cdecl sub_4013C0(int a1) {
    // a1是输入字符的ASCII码(char隐式转int)
    return (a1 ^ 0x55) + 72; 
}

功能拆解 :输入字符 c,返回 (ord(c) ^ 0x55) + 72ord(c) 是字符的 ASCII 码)。

1.4 密钥数组 byte_4420B0:异或密钥

从内存地址 0x4420B0 提取前 24 字节(对应核心区域长度),作为异或密钥:

python 复制代码
byte_4420B0 = [
    0x0D, 0x13, 0x17, 0x11, 0x02, 0x01,
    0x20, 0x1D, 0x0C, 0x02, 0x19, 0x2F,
    0x17, 0x2B, 0x24, 0x1F, 0x1E, 0x16,
    0x09, 0x0F, 0x15, 0x27, 0x13, 0x26
]

二、逆向推导核心:从 "正向流程" 到 "逆运算"

解题的本质是将 sub_4011C0 的正向流程反向执行,核心公式和规则如下:

2.1 正向流程公式(程序加密)

对核心区域的每个字符 c[i](原始 flag 中的字符),程序执行:

plaintext 复制代码
1. 大小写变换:c[i] → transformed_c[i](变换后的字符)
2. 自定义加密:sub_4013C0(transformed_c[i]) = (ord(transformed_c[i]) ^ 0x55) + 72
3. 异或运算:Str2[i] = byte_4420B0[i] ^ sub_4013C0(transformed_c[i])
4. 验证:Str2 == "GONDPHyGjPEKruv{{pj]X@rF"

2.2 逆运算公式(解题解密)

已知 Str2[i](目标字符串第 i 个字符),反推 c[i](原始 flag 字符),步骤如下:

plaintext 复制代码
1. 异或逆运算:sub_4013C0(transformed_c[i]) = byte_4420B0[i] ^ ord(Str2[i])
2. 自定义加密逆运算:ord(transformed_c[i]) = (sub_4013C0_result - 72) ^ 0x55
3. 大小写逆变换:transformed_c[i] → c[i](原始字符)

2.3 大小写变换的 "正向 + 逆向" 规则

正向变换(程序处理)
原始字符 c [i] 类型 变换结果 transformed_c [i] 标记 v2
小写字母(a-z,97-122) 大写字母(A-Z,-32) 1
大写字母(A-Z,65-90)且 v2=0 小写字母(a-z,+32) 0
非字母(数字、符号等) 不变 0
逆向变换(解题核心)

根据 transformed_c[i] 的类型,反推原始字符 c[i]

transformed_c [i] 类型 原始字符 c [i] 推导规则 示例
大写字母(A-Z,65-90) 小写字母(+32)→ c [i] = chr (ord (tc)+32) tc='G'(71)→ c='g'(103)
小写字母(a-z,97-122) 大写字母(-32)→ c [i] = chr (ord (tc)-32) tc='y'(121)→ c='Y'(89)
非字母(数字、符号等) 不变 → c [i] = transformed_c [i] tc='{' → c='{'

三、Python 代码实现(完整逆运算)

按照逆运算流程编写代码,每一步对应逆向推导,加入格式校验确保正确性:

python 复制代码
def ignite_me_decrypt():
    # 1. 已知常量定义
    str2_target = "GONDPHyGjPEKruv{{pj]X@rF"  # 目标对比字符串(24字节)
    byte_4420B0 = [                           # 异或密钥数组(24字节)
        0x0D, 0x13, 0x17, 0x11, 0x02, 0x01,
        0x20, 0x1D, 0x0C, 0x02, 0x19, 0x2F,
        0x17, 0x2B, 0x24, 0x1F, 0x1E, 0x16,
        0x09, 0x0F, 0x15, 0x27, 0x13, 0x26
    ]
    flag_prefix = "EIS{"                      # flag前缀
    flag_suffix = "}"                         # flag后缀
    core_chars = []                           # 存储核心24个字符

    # 校验基础条件(避免输入错误)
    assert len(str2_target) == 24, "目标字符串长度必须为24字节"
    assert len(byte_4420B0) == 24, "密钥数组长度必须为24字节"

    # 2. 逐字符逆运算推导核心区域
    for i in range(24):
        # 步骤1:异或逆运算 → 得到sub_4013C0的返回值
        target_ascii = ord(str2_target[i])
        key_byte = byte_4420B0[i]
        sub_result = key_byte ^ target_ascii  # 异或逆运算(异或的逆是自身)

        # 步骤2:自定义加密逆运算 → 得到transformed_c[i]的ASCII码
        # 正向:sub_result = (ord_t ^ 0x55) + 72 → 逆向:ord_t = (sub_result -72) ^ 0x55
        ord_t = (sub_result - 72) ^ 0x55
        transformed_c = chr(ord_t)

        # 步骤3:大小写逆变换 → 得到原始核心字符c[i]
        if 65 <= ord_t <= 90:
            # transformed_c是大写 → 原始字符是小写(+32)
            original_char = chr(ord_t + 32)
        elif 97 <= ord_t <= 122:
            # transformed_c是小写 → 原始字符是大写(-32)
            original_char = chr(ord_t - 32)
        else:
            # 非字母字符 → 直接保留
            original_char = transformed_c

        core_chars.append(original_char)

    # 3. 拼接flag并验证格式
    flag_core = ''.join(core_chars)
    flag = f"{flag_prefix}{flag_core}{flag_suffix}"

    # 格式校验(确保符合main函数约束)
    assert len(flag) == 29, f"Flag长度错误:实际{len(flag)},预期29"
    assert flag.startswith(flag_prefix), f"Flag前缀错误:应为{flag_prefix},实际{flag[:4]}"
    assert flag.endswith(flag_suffix), f"Flag后缀错误:应为{flag_suffix},实际{flag[-1]}"

    return flag

# 执行解密,得到最终flag
if __name__ == "__main__":
    final_flag = ignite_me_decrypt()
    print(f"最终Flag:{final_flag}")

四、运行结果与验证

4.1 代码运行输出

plaintext 复制代码
最终Flag:EIS{wadx_tdgk_aihc_ihkn_pjlm}

(注:实际运行代码后,正确 flag 为 EIS{wadx_tdgk_aihc_ihkn_pjlm},逆运算需精准计算每个字符。)

4.2 正向验证(确保结果正确)

将得到的 flag 代入程序正向流程,验证是否符合预期:

  1. 提取核心区域:wadx_tdgk_aihc_ihkn_pjlm!(24 字符);
  2. 大小写变换:小写字母转大写,大写字母(无小写前置)转小写,非字母不变;
  3. 自定义加密:(ord(tc) ^ 0x55) + 72
  4. 异或运算:与 byte_4420B0 异或后得到 GONDPHyGjPEKruv{``{pj]X@rF
  5. 程序输出 Congratulations!,验证 flag 正确。

五、关键逆向技巧与避坑指南

5.1 核心技巧

  1. 正向流程先梳理 :逆向前必须完整拆解程序的正向处理步骤,确保每个环节无遗漏(如本题的 v2 标记对大小写变换的影响);
  2. 函数逆运算推导 :自定义函数(如 sub_4013C0)的逆运算需严格遵循数学规则(加法逆是减法,异或逆是自身);
  3. 密钥数组精准提取 :密钥长度必须与核心区域一致(24 字节),地址需准确(0x4420B0);
  4. 格式校验不可少:flag 的长度、前缀、后缀是硬性约束,可用于快速排查错误。

5.2 常见坑点

  1. 大小写变换优先级混淆 :正向流程中 "小写→大写" 优先级高于 "大写→小写",逆向时需对应(大写 tc 只能来自小写原始字符);
  2. 自定义函数逆运算顺序错误:必须先减 72 再异或 0x55,不能颠倒(因正向是先异或后加法);
  3. 目标字符串拼写错误 :需完全复制伪代码中的目标字符串(GONDPHyGjPEKruv{``{pj]X@rF),大小写、符号不能错;
  4. 密钥数组提取错误 :内存地址 0x4420B0 开始的前 24 字节需精准提取,多取或少取会导致异或结果错误。

六、总结

本题的解题流程可概括为:

复制代码
格式约束提取 → 核心验证函数拆解 → 正向流程梳理 → 逆运算推导 → 代码实现 → 格式校验

这类 "字符变换 + 自定义加密 + 异或" 的逆向题,核心是 "正向逻辑闭环 + 逆运算精准":先通过反编译代码理清程序如何处理 flag,再逐一反向推导每个步骤,最终通过代码实现自动化计算。

掌握这类题目的关键在于:

  1. 熟练使用 IDA 提取密钥、反编译函数;
  2. 具备 "正向→逆向" 的逻辑转换能力;
  3. 注重细节(如标记变量、函数参数类型、字符串大小写)。

最终得到的正确 flag 可直接提交攻防世界平台,通过本题验证。

相关推荐
小尧嵌入式1 小时前
C++基础语法总结
开发语言·c++·stm32·单片机·嵌入式硬件·算法
霍格沃兹测试开发学社-小明1 小时前
AI来袭:自动化测试在智能实战中的华丽转身
运维·人工智能·python·测试工具·开源
@游子1 小时前
Python学习笔记-Day2
开发语言·python
稚辉君.MCA_P8_Java1 小时前
Gemini永久会员 归并排序(Merge Sort) 基于分治思想(Divide and Conquer)的高效排序算法
java·linux·算法·spring·排序算法
wanderist.1 小时前
Linux使用经验——离线运行python脚本
linux·网络·python
九千七5261 小时前
sklearn学习(3)数据降维
人工智能·python·学习·机器学习·sklearn
你的冰西瓜1 小时前
C++20 新特性详解:相较于 C++17 的主要改进
开发语言·c++·stl·c++20
止观止1 小时前
C++20 Ranges:告别手写循环,像 SQL 一样操作数据
c++·stl·c++20·编程范式·ranges
地平线开发者1 小时前
征程 6 | QAT 新版 qconfig 量化模板使用教程
算法·自动驾驶