【攻防世界】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) + 72(ord(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 代入程序正向流程,验证是否符合预期:
- 提取核心区域:
wadx_tdgk_aihc_ihkn_pjlm!(24 字符); - 大小写变换:小写字母转大写,大写字母(无小写前置)转小写,非字母不变;
- 自定义加密:
(ord(tc) ^ 0x55) + 72; - 异或运算:与
byte_4420B0异或后得到GONDPHyGjPEKruv{``{pj]X@rF; - 程序输出
Congratulations!,验证 flag 正确。
五、关键逆向技巧与避坑指南
5.1 核心技巧
- 正向流程先梳理 :逆向前必须完整拆解程序的正向处理步骤,确保每个环节无遗漏(如本题的
v2标记对大小写变换的影响); - 函数逆运算推导 :自定义函数(如
sub_4013C0)的逆运算需严格遵循数学规则(加法逆是减法,异或逆是自身); - 密钥数组精准提取 :密钥长度必须与核心区域一致(24 字节),地址需准确(
0x4420B0); - 格式校验不可少:flag 的长度、前缀、后缀是硬性约束,可用于快速排查错误。
5.2 常见坑点
- 大小写变换优先级混淆 :正向流程中 "小写→大写" 优先级高于 "大写→小写",逆向时需对应(大写
tc只能来自小写原始字符); - 自定义函数逆运算顺序错误:必须先减 72 再异或 0x55,不能颠倒(因正向是先异或后加法);
- 目标字符串拼写错误 :需完全复制伪代码中的目标字符串(
GONDPHyGjPEKruv{``{pj]X@rF),大小写、符号不能错; - 密钥数组提取错误 :内存地址
0x4420B0开始的前 24 字节需精准提取,多取或少取会导致异或结果错误。
六、总结
本题的解题流程可概括为:
格式约束提取 → 核心验证函数拆解 → 正向流程梳理 → 逆运算推导 → 代码实现 → 格式校验
这类 "字符变换 + 自定义加密 + 异或" 的逆向题,核心是 "正向逻辑闭环 + 逆运算精准":先通过反编译代码理清程序如何处理 flag,再逐一反向推导每个步骤,最终通过代码实现自动化计算。
掌握这类题目的关键在于:
- 熟练使用 IDA 提取密钥、反编译函数;
- 具备 "正向→逆向" 的逻辑转换能力;
- 注重细节(如标记变量、函数参数类型、字符串大小写)。
最终得到的正确 flag 可直接提交攻防世界平台,通过本题验证。