CTF比赛打了这么多年,逆向方向一直是我投入精力最多的一块。每次线上线下赛打完,总有人在群里问“逆向题到底应该准备哪些工具”“有没有现成的工具包”之类的问题。说实话,新手在工具准备上卡住的情况太常见了:拿着IDA进去不知道下一步按哪个键,Python环境没配齐,adb连不上模拟器,Frida安装半天报错。这篇文章我就把平时自己整理的一套2026版自整理逆向方向CTF工具包摊开来讲,从目录结构、选型原因到具体使用流程和踩坑记录,一次性说清楚。想入门逆向、想在比赛中提速的朋友,可以对着这份指南搭建自己的工具包。
先说结论:工具包不在于多,而在于“每类场景留一两个顺手且稳定的工具”,并且能做到从拿到题目的第一分钟开始,直接进入分析状态,而不是现场查文档。
1. 先搞清楚逆向题在赛场上的真实分布
在准备工具包之前,得先弄明白逆向方向到底在考什么。如果不知道要面对什么类型的题目,工具包就是无源之水。我统计过近两年国内各大赛事和公开训练平台上的逆向题目,分布基本集中在以下六类。
1.1 主流题型与对应技能点
- 纯Linux二进制逆向:以ELF文件为主,考察加密算法识别、花指令对抗、反调试、VM保护等。这类题出现频率最高,是入门必须过关的基础类型。
- Windows二进制逆向:PE文件为主,涉及DLL注入、壳处理、注册机类题目,也常考资源提取和消息回调逻辑。
- Android逆向:APK、SO库、Frida检测、混淆加固、抽取壳等,是目前存量最大、题目更新最快的方向之一。
- Web前端JS逆向:登录流程模拟、签名算法还原、混淆JS分析,通常和Web方向交叉出现。
- 固件与杂项逆向:路由器固件、单片机固件、Python字节码、Go/Rust程序、易语言程序等,这类题的特点是需要额外准备对应环境的运行和反编译工具。
- 新方向:AI安全类题目、智能合约逆向、云原生场景下的函数还原等,近两年在个人赛和高阶赛里开始露头。
对应到技能点,其实就是四个能力:静态分析看逻辑、动态调试看行为、函数调用链梳理、加密算法还原。工具包的设计必须覆盖这四件事,否则到了赛场上会四处卡壳。
1.2 工具选型背后的逻辑
很多人的工具包是“收藏夹式”的,装了几十个工具,比赛时反而不知道该用哪个。我的原则是“一主一备、场景覆盖”。
每个场景保留一个主力工具、一个备用工具。主力工具要满足三个条件:一是稳定,不会因为目标文件的某个特性直接崩溃;二是跨平台或者至少在Linux下用着顺手;三是社区资料多,遇到问题能快速搜到解决方案。备用工具则是在主力工具失效时顶上,防止比赛中单点故障。
按这个标准,我这边的主力和备用清单大致是这样的:
| 场景 | 主力工具 | 备用工具 |
|---|---|---|
| 静态反编译 | Ghidra | IDA Pro |
| 二进制动态调试 | GDB + pwndbg | radare2 / r2 |
| 安卓反编译 | jadx | GDA |
| 安卓动态分析 | Frida全家桶 | objection |
| 网络抓包 | Burp Suite | mitmproxy |
| Windows调试 | x64dbg | WinDbg |
| 算法求解 | z3 + Python | angr / 手工推 |
为什么静态反编译主用Ghidra而不是IDA?一个重要原因就是Ghidra免费且插件生态好,IDAFLIRT在函数签名识别上确实强,但比赛中不是每个题目都能靠FLIRT一招制敌。更重要的是,Ghidra的脚本接口对Python支持友好,我可以写自动化脚本批量做类型恢复、查交叉引用,这在比赛节奏里很有价值。IDA则更适合处理一些复杂结构体、类型恢复要求高的题目。这套组合在我手上跑了大几十场比赛,几乎没有遇到过两个同时搞不定的情况。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 2026版工具包的目录设计与模块划分
工具包如果只是把压缩包解压,那只能算“文件集合”,不叫工具包。真正好用的工具包必须有清晰的目录结构,让使用者不用翻说明也能猜到每个目录里放的是什么。下面是我自己整理的目录结构,你可以对照着建一套自己的。
2.1 工具包的整体结构
bash复制ctf-reverse-toolkit/
├── 00_QuickStart/
│ ├── README.md
│ ├── install_all.sh
│ └── check_env.sh
├── 01_StaticAnalysis/
│ ├── Ghidra_11.x
│ ├── ida_backup_cfg/
│ ├── DetectItEasy
│ ├── file_hex/
│ └── sign_plugins/
├── 02_DynamicDebug/
│ ├── gdb_setup/
│ ├── pwndbg/
│ ├── radare2_build/
│ └── one_gadget
├── 03_Android/
│ ├── platform-tools/ # adb 工具包
│ ├── jadx/
│ ├── apktool/
│ ├── frida/
│ ├── objection/
│ ├── unidbg/
│ └── emulator_config/
├── 04_Windows_RE/
│ ├── x64dbg/
│ ├── windbg/
│ ├── ExeinfoPE/
│ └── 壳特征库/
├── 05_WebJS/
│ ├── node_env/
│ ├── burp_profiles/
│ ├── js_deobfuscator/
│ └── hook_scripts/
├── 06_Misc/
│ ├── python_tools/ # z3, angr, pwntools 等
│ ├── binwalk
│ ├── firmware_tools/
│ └── pyc_decompiler/
├── 07_Scripts/
│ ├── auto_checksec.sh
│ ├── extract_flag.py
│ ├── solve_template.py
│ └── frida_common/
└── tools_cache/
└── offline_packages/
这个结构有几个好处:一是按分析场景分目录,而不是按“工具类型”分目录,比如把frida放到Android目录里,是因为frida在Android题里用得最多,虽然它也可以用于PC进程,但比赛场景决定了它和adb、apktool放一起更顺手;二是保留一个tools_cache目录,专门存放离线安装包,赛前在没网环境下也能快速恢复环境。
2.2 静态分析与反汇编模块
静态分析模块是整个工具包的根基。01_StaticAnalysis里我放的Ghidra是发行版压缩包,解压即用,不依赖安装。IDA则是带了一份我自己整理过的配置文件备份,包含常用插件路径和色彩主题,因为IDA的配置文件在不同平台间同步容易踩坑,比赛机器上重装时直接覆盖配置就行。
这里还要单独说一下DetectItEasy这个工具。很多新手拿到一个未知文件,直接丢进IDA,结果发现文件头不对,或者是一个压缩包、一个自解压程序,非常浪费时间。DetectItEasy的作用就是快速识别文件类型、编译器特征和壳特征。我用它配合file命令,能在十秒内判断出题目类型,决定下一步是开Ghidra还是先脱壳。
2.3 动态调试与运行环境模块
动态调试模块放在02_DynamicDebug,重点不只是GDB本身,而是pwndbg的配置。pwndbg对堆题、迷宫题、格式化字符串题的调试体验提升非常大,它能把堆布局、寄存器状态、反汇编结果直接可视化。配合one_gadget可以快速查找可用的one_gadget地址,这在利用类题目里几乎是刚需。
另外,我还在这个目录里放了一份glibc多版本环境脚本。不同题目编译时用的glibc版本可能不同,如果本机版本不匹配,动态调试时堆结构会完全对不上。脚本通过patchelf修改二进制文件的动态链接器路径,把题目ELF指向下载好的指定glibc,这个操作在近两年的题目里越来越常见。
2.4 Android与移动端模块
Android模块是工具包体积最大的一块,因为它既有静态工具,又有动态工具,还要模拟器配置。platform-tools就是标准的adb工具包,我保留了一个特定版本,原因后面踩坑部分会细说。jadx用来查看Java层反编译代码,apktool负责资源解码和回编译,frida和objection负责动态Hook和快速信息收集,unidbg则用来在没有完整设备环境的情况下,单独模拟执行SO文件里的加密函数。
2.5 Web与JS逆向模块
WebJS模块是后加进来的。最近两年,逆向方向里JS题的比例明显上升,不只是纯Web方向会出,逆向方向也经常给你一个混淆过的JS文件,让你还原签名算法。这个目录里我放了Node.js环境、Burp的配置备份、一个JS反混淆的脚本集,以及一些常用的Hook模板。
需要说明的是,JS逆向和安卓逆向在思路上有共通之处,都是“定位关键函数 -> 还原算法 -> 模拟调用”的流程,所以统一放到工具包里,比单独维护一套科学合理。
3. 从拿到题目到找flag的完整实操链路
目录结构只是骨架,真正的价值体现在使用链路上。下面我按题型分别讲一套标准操作流程,这套流程是我反复打磨后固定下来的,每一步都有明确目的。
3.1 Linux ELF逆向的标准流程
一道典型的Linux ELF题,我拿到手的操作顺序是这样的:
第一步,用file命令看文件类型,用checksec看安全防护,用strings扫一遍关键字符串。这三个操作要一次做完,目的是建立对二进制的基本认知:是32位还是64位?有没有PIE?有没有canary?有没有UPX壳?字符串里有没有类似“flag”“key”“congratulation”的提示词。
第二步,把文件丢进Ghidra或IDA。定位到main或者entry后,先不急着读汇编,而是看反编译的伪代码中有没有明显的加密函数调用。很多初级题的flag校验就是一个简单的异或或者一个可逆的线性变换,这时候伪代码一眼就能看穿。
第三步,如果静态看不太懂,或者存在反调试、运行时解密,那就上动态调试。我习惯用GDB+pwndbg在关键函数入口下断点,然后用ni或者si单步观察寄存器变化。遇到系统调用,用catch syscall拦截,看程序读取了哪些输入、传入了哪些参数。
第四步,确定了算法逻辑之后,用Python把解密过程复现一遍。这里我通常直接用pwntools写脚本,因为它既能把输入给到程序,也能直接调用本机libc,还能做远程连接。下面是一个模板:
python复制from pwn import *
def solve_local(binary_path):
elf = ELF(binary_path)
context.arch = elf.arch
context.log_level = 'debug'
p = process(binary_path)
p.sendlineafter(b'input:', b'test')
data = p.recvall()
print(data)
这个模板虽然简单,但它保证了“流程可复用”,每道新题只需要改输入格式和算法还原部分,不需要重头搭脚手架。
3.2 Android APK逆向的常规套路
APK题是很多新手最容易没头绪的地方,实际上它比ELF题更有套路。
第一步,先用jadx打开APK,直接看Java层代码。重点搜索onCreate、check、verify、encrypt、native这类关键词,找到校验函数的入口。大部分题目在Java层就能确定核心逻辑是调用了native方法,还是在Java层自己实现的。
第二步,如果调用了System.loadLibrary加载了SO,那就把APK解包,把对应架构目录下的libxxx.so拖出来,丢进Ghidra或IDA分析。SO文件的分析方式和ELF类似,但要注意JNI函数的命名规则,Java_包名_类名_方法名会直接出现在导出表里,沿着这个入口往下跟,一般就能找到真正的加密逻辑。
第三步,动态验证阶段。先在AndroidManifest.xml里确认入口Activity,再启动模拟器安装APK。用frida启动时注入一段基础Hook脚本,看看关键Java方法被调用时传入了什么参数,返回了什么结果。这里有一个很常用的小技巧:Hook Log 输出,能把一些开发者故意留下的调试信息直接打到控制台。
第四步,如果SO里做了混淆或者反调试,比如检测/proc/self/maps里有没有frida特征,最简单的方案是先用objection关闭常见检测,或者改用unidbg在PC上直接调用SO里的函数。unidbg的好处是不需要真实设备,能单步调试native层,很多实战里拖慢节奏的“环境检测”问题它都能绕过去。
3.3 前端JS逆向与Web方向的处理手法
JS逆向类的题目在工具使用上更偏“脚本化”。拿到一个加密的JS文件,我一般分三步走:
第一步,先看代码结构,判断是哪种混淆。常见的混淆手段包括变量名混淆、字符串数组化、控制流平坦化、极限压缩等。对于字符串数组化,可以直接在Node.js里运行JS,把解密字符串的函数执行出来;对于控制流平坦化,用现成的插件做反向转换。
第二步,定位关键函数。通常题目会给出一个输入框或者一段调用示例,我用API Hook的方式,在浏览器里直接Hook可疑函数,观察调用参数和调用栈。另一个思路是搜索cookie、sign、token这些词,定位参与签名计算的函数。
第三步,用Python或者Node.js复现算法。如果算法难度不高,直接翻译成Python调用;如果复杂度较高,也可以直接用Node.js跑原JS文件,通过child_process在Python里调用。
3.4 反调试、混淆与VM保护的处理手法
这是很多选手卡住的主要环节。我的建议是“先识别、再绕行、后分析”。
识别阶段,用strings、DetectItEasy、调试器插件判断题目用了哪类保护。常见的有PTRACE反调试、时间检测、环境变量检测、代码自修改等。绕行阶段,动态调试时优先用LD_PRELOAD劫持掉ptrace,或者直接在GDB里覆盖返回值为0。如果是时间检测,就用faketime或者在调试器里改时间相关返回值。
遇到VM保护是最棘手的。我的经验是不要试图完全还原指令集,而是先找VM的分发入口,把每个handler做的事情记录下来,然后在Python里模拟执行相关操作。这个过程比较枯燥,但成功率很高。只要把handler的语义搞明白,再复杂的VM题也能解出来。
4. 使用指南:初始化运行环境与自动化辅助
工具包做得再完整,环境起不来也是白搭。这里写一下首次初始化和日常使用时的要点。
4.1 首次初始化配置
我建议用Ubuntu 22.04或24.04作为主力环境,Windows端只留x64dbg和WinDbg做PE题,两道线互不干扰。工具包根目录有一个install_all.sh,但我不建议无脑全量安装,而是让它分模块安装,因为很多工具不是每个比赛都要用。
Python环境建议用venv创建独立虚拟环境,不要让pip的包和系统Python混杂。下面是我常用的安装方式:
bash复制python3 -m venv ~/ctf_env
source ~/ctf_env/bin/activate
pip install --upgrade pip
pip install pwntools z3-solver angr frida-tools objection
这里有个易踩的坑:frida-tools和frida-server的版本必须严格匹配,而且PC端安装的Python包版本,和Android设备上运行的frida-server版本也要一致,否则会报“unable to communicate with frida-server”之类的错误。
4.2 常用命令与一页纸速查
工具包再好,命令记不住也用不起来。我做了一张“一页纸命令表”,比赛前扫一眼就能回忆起来,这里摘抄一部分:
| 目的 | 命令 |
|---|---|
| 识别文件类型和壳 | file xxx、detect-it-easy xxx |
| 查看安全防护 | checksec --file=./xxx |
| 快速反汇编 | objdump -d -M intel xxx |
| 动态调试 | gdb -q ./xxx |
| 查看got表 | got 或 i got |
| 搜索字符串 | strings -n 6 xxx |
| 静态反编译 | ghidra / analyzeHeadless |
| APK解包 | jadx -d out ./app.apk |
| 重打包 | apktool d app.apk、apktool b app_out |
| hook Java方法 | frida -U -f com.demo.app -l hook.js |
| 不落地hook | objection -g com.demo.app explore |
| 网络代理抓包 | burpsuite 或 mitmproxy -p 8080 |
| 算法求解 | python3 -c "from z3 import *; ..." |
这张表我放在00_QuickStart/README.md里,每个命令都带一个更详细的示例。自整理的意义就在这里:不是把别人的文档复制一遍,而是把自己用过、验证过的命令沉淀下来。
4.3 自动化脚本辅助
我还会用脚本减少重复劳动。比如auto_checksec.sh,扫描一个目录下所有可执行文件,自动输出每个文件的架构、PIE、canary、NX信息,比赛时批量分析题目文件非常方便。
另一个常用的脚本是extract_flag.py,它的逻辑很朴素:读入一个二进制文件或字符串输出,用正则匹配常见的flag格式,比如flag{...}、ctf{...}、key{...},匹配到就直接打印。看似简单,但在题目多、输出多的时候能省很多眼睛。
python复制import re
import sys
def extract(data: bytes):
patterns = [
rb'flag\{[^}]+\}',
rb'ctf\{[^}]+\}',
rb'key\{[^}]+\}',
rb'[A-Za-z0-9]{20,}'
]
for pattern in patterns:
matches = re.findall(pattern, data)
if matches:
for m in matches:
print(m.decode(errors='ignore'))
print("done")
data = open(sys.argv[1], 'rb').read() if len(sys.argv) > 1 else sys.stdin.buffer.read()
extract(data)
5. 逆向工具包踩坑记录
工具包整理过程中,我踩过不少坑,有些坑真的是不自己踩一遍,看教程根本发现不了。挑几个典型的记录在这里。
5.1 IDA和Ghidra的插件版本不匹配
有一段时间我同时在使用IDA 8.3和一堆第三方插件,但有些插件是基于Python 3.8编译的,当我切换到更新的IDA版本,插件直接加载失败。后来我的做法是把插件目录独立出来,每次升级IDA前先做全量备份,并且只在需要时同步启用插件。Ghidra的扩展模块也有类似问题,不同版本之间的Ghidra扩展不能混用,尤其是脚本里引用了特定API的,升级后必须回归测试。
5.2 Frida版本与设备不匹配导致无法附加
这个问题几乎是安卓逆向新手必踩。PC上安装了最新的frida-tools,但模拟器或者真机上的frida-server是老版本,运行frida-ps -U时直接报错。解决方式是:下载二进制版本时严格匹配PC端Python包的版本号,比如PC端是16.7.19,那Android端就下载对应的frida-server-16.7.19-android-arm64.xz。另外,如果是模拟器,要注意CPU架构是arm64还是x86_64,下错架构同样无法运行。
5.3 GDB调试时断点打在共享库加载前失效
有一次我在调试一道PIE开启的ELF题时,直接在main函数地址上下断点,结果GDB提示断点pending,运行后也没停下来。原因是PIE情况下地址是随机化的,断点需要等程序加载完成后才能解析。解决方案是:先跑起来再下断点,或者使用start命令初始化程序,然后重新找地址。另一个更简单的方法是在__libc_start_main上下断点,等程序走到那里后再利用pwndbg的entry命令修正主函数地址。
5.4 APK重打包后无法正常运行
使用apktool重打包APK时,最容易遇到的两个问题是签名校验和资源混淆。签名校验可以通过在开发机上手动签名解决,但要注意签名工具版本,部分新版Android要求v2签名。还有一道题模拟的是加固APP,重打包后壳的校验直接报错,这时候就别想着重打包了,改用frida的-f模式冷启动注入,或者直接在内存dump脱壳。
5.5 工具包内的旧版工具在比赛环境中失效
还有一个比较隐形的坑:工具包整理半年后,部分工具可能已经不能用了。比如某些在线服务商的API改变了返回格式,某些脚本依赖的库升级后接口变了。所以我的建议是,在每次大型比赛前,花二十分钟跑一遍所有核心工具的自测脚本,确认能正常打开一个样本文件、能正常启动模拟器、能正常连接一次frida。这个“赛前体检”能避免到赛场才发现工具链断裂的尴尬。
6. 工具包的更新与个人使用体会
写了一整篇,最后聊聊工具包怎么长期维护。
我一般把工具包当成一个持续演进的项目来管理,而不是下载一次就扔在那里。每次比赛后,我都会把新遇到的工具、脚本、思路整合回对应的目录,同时把过时的工具移入tools_cache/archive,保持主目录整洁。比较大的变化还会在README.md里写更新记录,方便自己回溯某个改动的原因。
给新入坑的朋友一个建议:不用一开始就照着完整的工具包全量安装,先用最核心的Ghidra、pwndbg、jadx、Frida跑通几道简单题目,建立起“静态分析+动态调试”的基本流程,之后遇到哪类题卡住了,再针对性地往工具包里加东西。工具包的价值不在于“大而全”,而在于每个工具你都亲手用过、知道它的脾气,这样赛场上它才能真正帮到你。
2026版这个工具包,目前已经覆盖了我能想到的绝大多数比赛场景。接下来我计划补两块内容:一是增加更多AI安全相关的分析脚本,这块题目往后会越来越多;二是把工具包的安装脚本做得更智能,支持一键检测依赖并推荐安装方案。如果你也在整理自己的逆向工具包,希望这篇指南能给你一个起点。
