1. 题目背景解析
BUUCTF是一个知名的网络安全竞赛平台,"findit"是该平台上一道典型的逆向工程类题目。这类题目通常要求参赛者通过分析给定的程序或文件,找到隐藏的flag信息。逆向工程在CTF比赛中占据重要地位,考察参赛者对程序逻辑、加密算法和系统底层的理解能力。
这道题目的名称"findit"直译为"找到它",暗示需要通过系统性的逆向分析来定位关键信息。作为逆向工程入门题,它可能涉及基础的静态分析、动态调试或简单的算法逆向等技能点。
2. 解题环境准备
2.1 工具选择与配置
逆向工程需要专业的工具链支持,针对这道题目推荐以下工具组合:
-
静态分析工具:
- IDA Pro(商业版)或Ghidra(开源替代)
- Binary Ninja(轻量级选择)
- strings/file等基础Linux命令
-
动态调试工具:
- GDB with peda/pwndbg增强插件
- x64dbg(Windows平台)
- Frida(动态插桩框架)
-
辅助工具:
- Detect It Easy(查壳工具)
- HxD(十六进制编辑器)
- Python(用于编写解析脚本)
提示:初学者建议从Ghidra+ GDB组合开始,避免商业软件的学习成本。所有工具都应在干净的虚拟机环境中安装,防止分析过程中意外修改主机系统。
2.2 基础分析流程
标准的逆向工程解题流程如下:
- 文件类型识别
- 保护机制检测(加壳/混淆)
- 静态分析(反汇编/反编译)
- 动态调试验证
- 算法逆向/数据提取
- Flag构造与提交
3. 题目详细分析
3.1 初步文件检查
首先对题目文件进行基础检查:
bash复制$ file findit
findit: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=..., not stripped
$ strings findit | head -20
...
Welcome to BUUCTF!
Input your flag:
Congratulations!
Wrong Answer!
...
关键观察点:
- 64位ELF可执行文件
- 未去除符号表(not stripped)
- 包含明文字符串提示
3.2 静态逆向分析
使用Ghidra加载程序,定位到main函数核心逻辑:
c复制int main() {
char input[32];
printf("Input your flag: ");
scanf("%31s", input);
if (check_flag(input)) {
puts("Congratulations!");
} else {
puts("Wrong Answer!");
}
return 0;
}
进一步分析check_flag函数:
c复制bool check_flag(char* input) {
if (strlen(input) != 24) return false;
char encrypted[] = {0x12,0x34,0x56,0x78,...}; // 硬编码数据
for (int i = 0; i < 24; i++) {
if ((input[i] ^ 0x55) != encrypted[i]) {
return false;
}
}
return true;
}
算法特点:
- 固定长度校验(24字节)
- 逐字节异或加密(密钥0x55)
- 结果与硬编码数据比对
3.3 动态调试验证
使用GDB验证静态分析结果:
bash复制gdb ./findit
b *check_flag+0x50 # 在关键比较处下断点
r
观察寄存器/内存状态:
- RAX保存输入字符
- RBX保存加密后的预期值
- 通过
x/s $rsp查看栈上数据
4. 解题实现
4.1 解密算法实现
根据逆向结果编写解密脚本:
python复制encrypted = [
0x67, 0x79, 0x7B, 0x7F, 0x75, 0x2B, 0x3C, 0x52,
0x53, 0x79, 0x57, 0x5E, 0x5D, 0x42, 0x7B, 0x2D,
0x2A, 0x66, 0x42, 0x7E, 0x4C, 0x57, 0x79, 0x41
]
flag = ''.join([chr(c ^ 0x55) for c in encrypted])
print(flag)
4.2 常见问题排查
-
解密结果不符合预期:
- 检查字节序(小端/大端)
- 验证异或密钥是否正确
- 确认是否遗漏了其他变换步骤
-
动态调试时断点不生效:
- 检查ASLR是否关闭(
set disable-randomization on) - 确认调试符号加载情况(
info functions)
- 检查ASLR是否关闭(
-
反编译结果异常:
- 尝试不同的反编译器(Ghidra/IDA/Binary Ninja交叉验证)
- 手动修复函数识别错误(按P创建函数)
5. 进阶技巧
5.1 自动化分析技术
使用angr框架进行符号执行:
python复制import angr
proj = angr.Project("./findit")
state = proj.factory.entry_state()
simgr = proj.factory.simulation_manager(state)
simgr.explore(find=0x400A23, avoid=0x400A35) # 地址需根据实际调整
print(simgr.found[0].posix.dumps(0))
5.2 反调试对抗
如果题目增加反调试措施,需要处理:
- ptrace检测:通过修改寄存器或hook函数绕过
- 时间检测:使用
LD_PRELOAD注入假时间函数 - 代码混淆:通过动态插桩记录实际执行路径
6. 总结反思
逆向工程的关键在于系统性思维:
- 从文件特征入手建立初步认识
- 静态分析梳理程序框架
- 动态调试验证关键假设
- 编写脚本自动化处理
这道题目虽然简单,但完整呈现了逆向分析的典型流程。在实际比赛中,可能会遇到更复杂的保护措施,如:
- 多层加壳(UPX -> VMProtect)
- 控制流混淆(OLLVM)
- 自定义加密算法
建议初学者从这类基础题目入手,逐步掌握:
- 汇编指令快速阅读能力
- 常见加密模式识别
- 调试技巧与反反调试手段