📌 题目信息
| 项目 | 内容 |
|---|---|
| 题目名称 | Lihua's for(李华的 for 循环) |
| 题目来源 | 2021 强网杯青少年专项赛 |
| 题目类型 | Reverse(逆向) |
| 子分类 | CrackMe / 简单异或逆向 |
| 考点 | for 循环、异或 XOR 逆运算、IDA 反编译 |
| 难度 | ⭐⭐ |
| 最终 Flag | flag{a41be465-a50f-4124-b7ba-2766aff6baf2} |
🛠️ 使用工具
- IDA Pro(F5 反编译、读取内存数据)
- Python 3(编写还原脚本与验算)
📝 解题思路
题目描述:李华刚学会了 C 语言的 for 循环,于是出了个 crackme 来考你,输入正确的 flag 会输出 good~,错误则输出 error!。
思路很直观:这是典型的"输入 → 逐字符变换 → 与预存数组比对"的验证模式。我们只需要做两件事:
- 用 IDA 反编译找到验证逻辑(看它把输入怎么变换)。
- 把预存的对比数据
a[]读出来,做逆运算还原出正确的 flag。
第 1 步:定位主函数并反编译
先运行一下程序,确定程序功能。

用 IDA Pro 打开 crackme.exe,看函数列表,用户自定义的核心逻辑在 main(地址 0x401530)。对 main 按 F5 反编译,得到伪代码:
int main(int argc, const char **argv, const char **envp)
{
char flag[48]; // 输入的 flag
int a[44]; // 预存的对比数据
int b[45]; // 每次算出的结果
int i_0, i;
int good;
qmemcpy(a, &unk_403040, 0xA8u); // 把 0x403040 处 168 字节拷进 a(42 个 int)
puts("input flag");
scanf("%s", flag); // 读入 flag
puts(flag);
for ( i = 0; i <= 41; ++i ) // ⭐ 关键循环 1
b[i] = i ^ flag[i]; // 每个字符与下标 i 异或
for ( i_0 = 0; i_0 <= 41; ++i_0 ) // ⭐ 关键循环 2
{
if ( a[i_0] != b[i_0] ) // 和预存数组逐一比对
{ good = 0; break; }
good = 1;
}
if ( good == 1 ) printf("good~");
else printf("error!");
return 0;
}
⚠️ 注意:
flag长度是 42(循环条件i <= 41)。循环i从 0 开始,异或的对象是下标本身,不是某个固定常数——这一点非常关键,很多新手会误以为是固定的 key。
第 2 步:读取出预存数组 a[]
从反编译看到,对比数组 a[] 存放在地址 0x403040,共 0xA8(168)字节。因为 a 是 int[],所以是 42 个 4 字节整数。用 IDA 读取该地址的内存:
0000000000403040 66 00 00 00 6D 00 00 00 63 00 00 00 64 00 00 00
0000000000403050 7F 00 00 00 64 00 00 00 32 00 00 00 36 00 00 00
0000000000403060 6A 00 00 00 6C 00 00 00 3E 00 00 00 3D 00 00 00
0000000000403070 39 00 00 00 20 00 00 00 6F 00 00 00 3A 00 00 00
0000000000403080 20 00 00 00 77 00 00 00 3F 00 00 00 27 00 00 00
0000000000403090 25 00 00 00 27 00 00 00 22 00 00 00 3A 00 00 00
00000000004030A0 7A 00 00 00 2E 00 00 00 78 00 00 00 7A 00 00 00
00000000004030B0 31 00 00 00 2F 00 00 00 29 00 00 00 29 00 00 00
00000000004030C0 16 00 00 00 40 00 00 00 44 00 00 00 45 00 00 00
00000000004030D0 12 00 00 00 47 00 00 00 47 00 00 00 41 00 00 00
00000000004030E0 1A 00 00 00 54 00 00 00 00 00 00 00 00 00 00 00
00000000004030F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
第 3 步:逆运算还原 flag
验证逻辑是 b[i] = i ^ flag[i],且要求 a[i] == b[i]。所以:
a[i] = i ^ flag[i]
=> flag[i] = a[i] ^ i (异或运算可逆)
由于 XOR 有"自反性"(两边再异或一次 i 就能解出 flag[i]),直接写 Python 脚本还原:
# -*- coding: utf-8 -*-
# Lihua's for —— 还原 flag
# 从 0x403040 读出的 a[] 数组 (42 个 int, 每个 4 字节, 小端)
a = [
0x66, 0x6d, 0x63, 0x64, 0x7f, 0x64, 0x32, 0x36, 0x6a, 0x6c,
0x3e, 0x3d, 0x39, 0x20, 0x6f, 0x3a, 0x20, 0x77, 0x3f, 0x27,
0x25, 0x27, 0x22, 0x3a, 0x7a, 0x2e, 0x78, 0x7a, 0x31, 0x2f,
0x29, 0x29, 0x16, 0x40, 0x44, 0x45, 0x12, 0x47, 0x47, 0x41,
0x1a, 0x54,
]
flag = []
for i in range(42):
flag.append(a[i] ^ i) # 逆运算:flag[i] = a[i] XOR i
flag_str = bytes(flag).decode('ascii')
print("还原出的 flag:", flag_str)
# ---- 验算:正推确认 ---- #
ok = all((i ^ flag[i]) == a[i] for i in range(42))
print("验算通过(全程 a[i] == i ^ flag[i]):", ok)
运行结果:
第 4 步:得到 Flag
flag{a41be465-a50f-4124-b7ba-2766aff6baf2}
【Reverse】Lihua‘s for(李华的 for 循环)&spm=1001.2101.3001.5002&articleId=163949044&d=1&t=3&u=2f11084baf4246debe0845bc9f5fe9ac)
917

被折叠的 条评论
为什么被折叠?



