逆向工程 Reverse

REVERSE ENGINEERING — 把程序「拆开」看个明白

定义与应用领域(选自 CTF Wiki)

Reverse engineering, also called back engineering, is the process by which a man-made object is deconstructed to reveal its designs, architecture, or to extract knowledge from the object. —— from Wikipedia

软件代码逆向主要指对软件的结构、流程、算法、代码等进行逆向拆解和分析。主要应用于:

  • 软件维护:没有源码时修复/更新老软件
  • 软件破解:绕过授权、破解注册码
  • 漏洞挖掘:分析二进制找漏洞(与 Pwn 紧密相关)
  • 恶意代码分析:分析病毒、木马的行为

CTF 竞赛中的逆向

涉及 Windows、Linux、Android 平台的多种编程技术,要求利用常用工具对源代码及二进制文件进行逆向分析,掌握 Android 移动应用 APK 文件的逆向分析,掌握加解密、内核编程、算法、反调试和代码混淆技术。

对选手的要求

  • 熟悉操作系统、汇编语言、加解密等相关知识
  • 具有丰富的多种高级语言的编程经验
  • 熟悉多种编译器的编译原理
  • 较强的程序理解和逆向分析能力
📌 新手别被吓到:CTF 逆向入门题(easyre 级别)其实只要会「用 IDA 打开 → F5 → 读字符串」就够了,汇编功底是在一道道题里慢慢积累的。

常规逆向流程(选自 CTF Wiki)

  1. 使用 strings / file / binwalk / IDA 等静态分析工具收集信息,并根据这些静态信息进行 google/github 搜索
  2. 研究程序的保护方法,如代码混淆、保护壳及反调试等技术,并设法破除或绕过保护
  3. 反汇编目标软件,快速定位到关键代码进行分析
  4. 结合动态调试,验证自己的初期猜想,在分析的过程中理清程序功能
  5. 针对程序功能,写出对应脚本(Python),求解出 flag

定位关键代码 tips

  • 分析控制流:查看 IDA 生成的控制流程图(CFG),沿着分支循环和函数调用,逐块阅读反汇编代码
  • 利用数据、代码交叉引用:输出提示字符串可以通过数据交叉引用找到调用位置;用户输入函数(如 scanf、gets)的 API 调用位置就是关键代码

逆向 Tips(选自 CTF Wiki,句句是经验)

  • 编码风格:每个程序员的编码风格都不同,熟悉开发设计模式的同学能更迅速地分析出函数模块功能
  • 集中原则:程序员开发时往往习惯将功能相关的代码或数据写在同一个地方,分析时可以查看关键代码附近的函数和数据
  • 代码复用:最大的源代码仓库 Github 是最主要的来源,用特征(字符串、代码风格)在 Github 搜索可能发现类似代码,恢复缺失的符号信息
  • 七分逆向三分猜:合理的猜测往往能事半功倍,遇到可疑函数看不清逻辑,不妨根据蛛丝马迹猜测其功能,在不断的猜测验证中接近真相
  • 区分代码:必须能区分哪些代码是人为编写的,哪些是编译器自动附加的;哪些是库函数,哪些才是出题人自己写的代码。不要花时间在出题人以外的代码上
  • 耐心:给予足够的时间,总是能将一个程序分析得透彻

动态分析

动态分析的目的在于定位关键代码后,在程序运行的过程中,借由输出信息(寄存器、内存变化、程序输出)来验证自己的推断或是理解程序功能。主要方法有:调试、符号执行、污点分析。

常用工具(选自 CTF Wiki · Tools)

分类工具说明
静态分析IDA Pro逆向标配,F5 一键反编译伪代码
静态分析GhidraNSA 开源的免费 IDA 替代品
静态分析jadx / DnspyAndroid APK(Java)与 .NET 程序反编译
动态调试gdbLinux 调试器,配合 pwndbg 插件更好用
动态调试x64dbg / OllyDbgWindows 调试器
约束求解z3微软约束求解器,逆向算法题神器
模拟执行angr符号执行框架,自动求解

入门只需要两样:IDA Pro(免费版 IDA Free 即可)+ 一个十六进制编辑器。先学会:打开文件 → 看函数列表 → 双击 main → F5 看伪代码。

进阶专题一览(选自 CTF Wiki,先混个脸熟)

  • 算法和数据结构识别:识别 TEA/XTea/RC4/AES/DES/MD5/SHA 等加密算法;图、树、哈希表等数据结构在汇编中的形态
  • 代码混淆:OLLVM、movfuscator、花指令、虚拟化(VM)、SMC(自修改代码)—— 对应反混淆:模拟执行、符号执行
  • 保护壳:UPX 等压缩壳;脱壳方法:单步跟踪法、ESP 定律、一步到达 OEP、内存镜像法等
  • 反调试:IsDebuggerPresent 检测、SEH 异常处理、时间差检测、自调试等
  • 非常规逆向思路:任意架构/格式(lua、python、java、webassembly、固件、chip8、risc-v 等)。方法学通用:阅读官方文档 → 官方工具 → 前辈教程
📌 完整内容见 CTF Wiki · 软件逆向工程简介。

入门挑战 · 练习题(附 WP 与直达链接)

BUUCTFeasyre静态分析★☆☆ 去题目
📖 查看 WP 题解

场景:下载附件(一个可执行文件),运行后让你输入 flag。附件在 BUUCTF 题目页「下载附件」按钮。

1第一步先 strings:Linux 下 strings 附件 | grep flag,有时直接出答案。
2IDA 打开:函数窗口找 main → 双击进入 → 按 F5 反编译。
3读伪代码:发现程序把输入与一个字符串比较,flag 明文写在程序里(形如 flag{...})。
// F5 伪代码(示意)
int main() {
    if ( strcmp(input, "flag{This_Is_a_Easy_Re}") == 0 )
        puts("right");
    else
        puts("wrong");
}
4拿 flag:flag{This_Is_a_Easy_Re}(以题目实际为准)

💡 Reverse 新手第一课:先 strings,再 IDA,最后才考虑动态调试。

攻防世界open-source源码审计★☆☆ 去题目
📖 查看 WP 题解

场景:题目直接给你 open-source.c 源码文件 —— 逆向不一定要反汇编,读源码也是一种。

1读源码:main 函数要求三个命令行参数,并逐个校验:
if (first != 0xcafe) { ... }                      // 参数1 必须等于 0xcafe = 51966
if (second % 5 == 3 || second % 17 != 8) { ... }  // 参数2 满足 second%5==3 且 second%17==8
if (strcmp("h4cky0u", argv[3])) { ... }           // 参数3 必须是 h4cky0u
unsigned int hash = first * 31337 + (second % 17) * 11 + strlen(argv[3]) - 1615810207;
printf("flag{%x}\n", hash);                       // 按表达式计算输出
2凑参数:参数 2 取 8(8%5=3 ✓,8%17=8 ✓)。
3两种解法:① 直接把三个参数传给程序运行:./open-source 51966 8 h4cky0u;② 用 Python 算(注意 32 位整型溢出取低 32 位):
first, second, third = 0xcafe, 8, "h4cky0u"
h = first * 31337 + (second % 17) * 11 + len(third) - 1615810207
print("flag{%x}" % (h & 0xffffffff))

💡 学会「自己动手算」很重要:很多逆向题最终就是写一个脚本把算法逆回来。