给AI编码助手装上逆向工程技能:reverse-skill路由包设计与实践
经常有朋友问我“你用 AI 编码客户端写代码效率不错那能不能让它直接帮我分析一个二进制比如从 dump 里找逻辑、解析个固件、看看某段汇编在干嘛”答案在过去通常很尴尬。你丢给它一个.elf或者.bin它倒是能很热情地回复“这段代码可能在做 XX”可你真跟着它去验证往往会发现函数名是猜的偏移量是错的系统调用也没对上。我一开始也以为是模型能力不够后来想明白了——不是它笨是缺少一套让编码 Agent 知道“逆向工程该按什么步骤走、每一步该调哪个工具、怎么把工具输出变成下一步决策依据”的作业流程。所以我花了不少时间整理了一个 reverse-skill 技能路由包。它不是一个“万能逆向脚本”而是一套面向 AI 编码客户端的分层指令集告诉模型遇到可执行文件、固件、内存转储这类输入时应该先做什么、后做什么、什么场景调 Ghidra / radare2 / 系统工具链以及哪些请求必须被拦下。这篇文章就把这个包的设计思路、目录结构、典型工作流和踩坑经验完整写出来给同样想在 AI 编码流程里引入逆向工程能力的人一个可复用的参考。1. 当 AI 编码助手面对“非代码”工作流reverse-skill 解决的问题1.1 我的翻车现场先说个真实场景。某次我要快速确认一个 Linux 二进制是不是加壳的顺手在编码助手对话框里贴了文件路径让模型“帮我看看这个文件有什么可疑点”。它立刻给我列了分析结论写得有模有样什么“入口点偏移异常”“疑似 UPX 壳”“可能包含反调试”。我按照它的结论去用工具复核结果发现它根本没实际运行任何二进制分析工具所有结论都是基于文件名和文件大小“脑补”出来的。后来我测了很多次问题几乎都出在三个地方模型读不懂原始字节它擅长的是读文本和生成文本。你让它直接读二进制文件它拿到的往往是打开失败的报错或者被截断的乱码。即便它能调用工具也没有一个清晰的“流程优先级”。是先看文件头还是先拉字符串看完了 section table 接下来干嘛这些操作背后有完整的分析逻辑如果没人告诉它它就只能随机挑一个看起来像样的工具命令。逆向分析工具的输出通常又长又杂。radare2 反汇编一个大函数可以打印几百行Ghia 的 headless 导出也可能生成几十个文件。Agent 不知道哪些内容该保留、哪些该丢弃、哪些该写进中间结果文件结果就被刷屏信息淹没在上下文窗口里来回打转。这三个问题叠加造成了“看着在干活实际在胡说”的观感。reverse-skill 的切入点就在这里不是换更强的模型而是给模型一套定义好输入、输出、步骤和边界的路由指令让它在动手前至少先搞清楚“我在分析什么对象应该走哪条路径”。1.2 Agent 不是不做逆向而是没有“作业流程”大多数 AI 编码客户端在写代码时表现不错因为写代码的本质是“高频地生成文本并交给编译器验证”。逆向工程则完全相反它更接近一门实验科学。你需要先观察文件的外部特征再带着假设去查内部结构验证一个猜想后才会进入下一层。步骤之间有强依赖关系前面错了后面全错。举例来说拿到一个 ELF 文件完整的分析路径通常是先用file确认目标架构和链接方式。算哈希并去公开情报或本地历史库匹配看是不是已知样本。用readelf/llvm-readelf查看 section 和 program header特别关注入口点、权限位异常的段。用strings或更稳的rabin2 -z抽取可打印字符串但要区分“代码里的路径提示”和“数据里残留的误报”。如果怀疑加壳检查 entropy、section 命名、入口点是否落在常见 unpack stub 范围。只有经过以上静态判别后才轮到反汇编单函数、追踪调用链、或者上动态分析。这是一个合格的逆向研究员脑内默认的流程可它没有被写进 AI 编码客户端的“工作记忆”。缺这套流程Agent 就等于让一个刚入行的新人直接做报告不告诉他参考手册在哪、仪器的开关在哪个位置。这正是我觉得“技能路由”比单纯堆提示词有意义的原因。1.3 技能路由包不是提示模板堆砌你可能接触过一些“提示词工程”方案把一大段逆向分析要求粘进 system prompt。效果嘛我第一次测试也觉得有点用但很快发现了它的边界提示词太多太全反而把模型绕晕每次启动都重读一遍消耗大量上下文提示词如果太短又不含足够的分支判断标准。reverse-skill 的“路由”思路借鉴的是后端 API 网关的做法。它不试图在每个任务里把所有分析知识复述一遍而是把整个分析流程拆成若干独立的小模块路由先做判断再只把任务导向某个子模块。比如一个 Python 字节码文件和一个 ELF 可执行文件虽然都叫“逆向分析”但走的工具链完全不同。路由先把任务分到.pyc 分析路线或原生二进制分析路线后续模块才能给出有针对性的指令。这种设计还有一个隐藏好处可维护。想改进字符串提取逻辑只需要调整dispatchers/strings_plus.sh不用重新折腾 6000 字的系统提示词。2. 包的骨架reverse-skill 的分层结构与路由表2.1 reverse-skill 目录设计先看整体结构这基本就是包的实际布局reverse-skill/ ├── SKILL.md # 技能入口与总路由规则 ├── classify/ │ ├── binary.md # 识别 ELF/PE/Mach-O/固件镜像等 │ ├── source_proto.md # 识别 .pyc/.class 等中间产物 │ └── deny.txt # 直接拒绝的请求关键词与判断原则 ├── routes/ │ ├── static_elf.md # ELF 静态分析分路 │ ├── static_pe.md # PE 静态分析分路 │ ├── dynamic_sandbox.md # 动态沙箱分路 │ └── firmware.md # 固件拆包与文件系统还原分路 ├── dispatchers/ │ ├── ghidra_headless.sh # Ghidra 无头导出行为摘要 │ ├── radare2_scan.py # radare2 指令扫描收敛 │ ├── rabin2_info.py # 元数据与字符串摘要 │ └── run_with_timeout.sh # 给外部命令加统一超时 └── rules/ ├── evidence_first.md # 任何结论必须先附工具证据 ├── no_guess.md # 禁止反推地址与模式匹配 └── output_contract.md # 输出必须落到中间文件再总结这里必须说清楚SKILL.md是唯一被 AI 编码客户端直接加载的文件其他 markdown 文件通常是按需读取的参考子路由。很多 AI 编码框架会限制单次工具说明的数量不能一次性塞进去 20 个文件所以 SKILL.md 更像是一个“总机”。2.2 为什么是 31 层在尝试过一版把所有命令直接写进单个文件的做法之后我很快就后悔了原因很实际一次函数调用描述里塞的内容越多模型反而越容易漏用。于是我改成三层结构。第一层是入口SKILL.md。它负责两件事判断任务是否适合走逆向流程如果适合给出任务分类并指向具体 route。第二层是路由文件routes/*.md。每个路由文件描述一种类型目标的处理顺序并在指定步骤调用 dispatchers 里的脚本。第三层是执行脚本dispatchers/*.sh/py。它们封装外部工具对外只暴露简单的参数约定并负责控制超时与输出长度。额外的一层是 rules它不属于路由逻辑而是“所有路由都必须遵守的约束”。把这些约束从子模块里抽出来是为了防止模型在进入深度分析后忘记底线。打个比方SKILL.md 是前台接待routes 是不同科室的医生dispatchers 是护士和化验室rules 则是整家医院的规章制度。没有规章制度的科室接诊操作流程也许能跑通但出了风险没人兜底。2.3 路由分支怎么选实际路由判断并不复杂我在 classify 模块里定义了一组非常基础的判别优先级。下表可以说明思路观察结果路由去向典型工具file识别为 ELFroutes/static_elf.mdreadelf, rabin2, Ghidra检测到 PE 头 MZroutes/static_pe.mdobjdump, pefile, Ghidra扩展名为 .pyc 或开头为 magic 16CF/42 0D.pyc 分析dis, decompyle3, pycdc大块数据且没有可执行头routes/firmware.mdbinwalk, file, dd样本包含外联地址、动态解密特征routes/dynamic_sandbox.mdstrace, gdb, 沙箱这套分类看着简单但足以挡住最常见的误区。之前模型会把一个 QNX 固件镜像当成 Linux ELF 来处理现在 classify 阶段会先读文件头而不是猜扩展名错误率大幅降低。3. 从软件包接入到跑通第一个分析任务最小配置与工作流3.1 接入 AI 编码客户端主流的 AI 编码客户端现在基本都支持某种“项目技能”或“Agent 技能”机制。如果你用的是 Claude Code / Cline / Continue 这一类通常只需要维护一个.claude/skills或.cursor/skills目录。把 reverse-skill 目录整体放进去并确认 SKILL.md 的名字能被客户端扫描到即可。minimal 配置供参考mkdir -p .claude/skills cp -r reverse-skill .claude/skills/ # 若客户端支持 agent 指令级加载也可在项目说明中声明引用需要留意一个细节不同客户端对技能包的加载方式不同有的把 SKILL.md 内容常驻进上下文有的则只把“技能存在”放进工具列表等模型决定是否读取。如果你的客户端是后者建议在技能包里明确要求模型“碰到与逆向、分析二进制、固件相关任务时先读取 SKILL.md 再回复”。否则模型看不到路由内容技能包就完全失效了。3.2 一次典型会话的完整过程假设输入请求是分析项目目录下的 sample_mystery 文件告诉我它是否加壳能否提取到入口逻辑。带路由后模型的执行顺序大致是这样读取 SKILL.md识别这是一个二进制文件逆向任务定位到 classify/binary.md。运行file sample_mystery确认文件类型。如果工具不在 PATH 中route 文件会给出安装建议而不是让模型硬猜。计算 SHA256并对照本地known_samples数据库。这一步看似多此一举实际上能够节省大量时间重复的样本没必要从头分析。进入 ELF 静态路由。执行rabin2 -I -z sample_mystery提取 loader 信息和字符串再把关键输出重定向到中间文件避免刷屏。对 entrypoint 周边做熵值检查。如果熵值接近 8判定可能是加壳/加密段此时会提示“不要继续静态反汇编整个文件”并调用 Ghidra headless 来定位 stub。输出结构化结果包含文件哈希、编译器线索、section 异常、可疑字符串、入口点初步判断与证据路径。从命令执行的粒度看第 4 步和第 5 步是核心。没有路由时模型可能直接从第 1 步跳到“用 gdb 调试”然后卡死。有路由后它会像人一样先低风险、低成本地收集信息再决定是否需要深入。3.3 为什么必须让证据落在文件里reverse-skill 里一个反复强调的规则是所有工具的核心输出都应先落到.analysis/中间目录再让模型基于摘要进行推理。这是被教训逼出来的。最初我让 radare2 直接打印反汇编结果模型确实读到了但几百行输出把上下文窗口塞满了。从那以后我把 dispatchers 都改成“文件优先”模式脚本把完整结果归档只输出前 N 行摘要或者统计特征给模型。比如反汇编函数脚本先输出函数长度、引用字符串、分支数量等摘要等模型觉得某个函数是分析关键时才按需调radare2_scan.py --function 0x4010b0精读具体反汇编。这就是给 Agent 装上“望远镜 显微镜”的过程。它先看全貌再决定要不要放大某个点而不是一上来就贴脸观察细胞壁。4. 两个代表性实战场景与路由带来的具体变化4.1 场景一CTF 逆向题自动拿到 flag 的算法路径CTF 题目是 reverse-skill 最合适的试验场因为它规则清晰、目标明确、且完全合规。我的一个常见任务是给定一个rev_me二进制要求从内部算法里推出 flag。没有技能包时模型容易给出类似“flag 藏在字符串里直接 strings”的答案。真实题面往往没这么简单字符串可能会被 XOR 混淆正确路径是先识别入口点附近的函数找到主逻辑。发现某个函数大量引用加密后的字节数组但没有直接输出。跟踪数据流发现运行时会对数组做异或并把结果作为比较目标。生成一个小的 Python 脚本对数组做同样的异或操作得到 flag。reverse-skill 的路由不会替模型解决算法难题但它能确保模型按这个顺序走从入口点、到引用关系、再到数据流、最后到反推算法。技能包里的routes/static_elf.md会建议模型在遇到比较指令cmp/memcmp时停下来先找比较的目标数据来自哪个地址再往下分析。这是新手经常略过的关键节点也是 Agent 最容易“绕过正确答案”的地方。跑了十来个题目后我发现稳定复现率有明显提升。最典型的改进是模型终于不再直接输出“疑似答案”的乱码而是会写脚本验证再把验证结果附在分析报告里。4.2 场景二授权样本的快速初筛另一个更贴近安全运营的场景拿到一批来源明确的、有授权处置的样本目录需要先按恶意特征粗筛。这类文件里可能有大量静态打包的 Python 或者 Go 程序直接交给模型并不合适。一次实际批量处理中reverse-skill 的流程效果相当好for f in ./samples/*; do sha256sum $f file $f | cut -d: -f2 rabin2 -I $f 2/dev/null | head -20 done路由在这里发挥的作用不是“分析”而是“不做过度分析”。它会在过滤完文件类型和哈希之后直接给出排序建议相同编译器指纹的样本归为一组减少重复拆解。这种把任务分成“快速筛查”和“深度分析”两层的能力是我在纯提示词方案里很难写出来的。当然这里必须再强调一次护栏reverse-skill 内置了deny.txt对于没有授权说明的目标比如来路不明的商业软件安装包、涉及绕过授权验证的请求模型会直接拒绝走分析流程。我在规则里写了三条硬约束分析对象必须来自自有程序、公开 CTF 题目、漏洞库样例或已获授权的研究数据集。不得协助移除、绕过或篡改任何授权/DRM/许可校验机制。动态分析必须运行在隔离环境禁止在宿主生产环境直接执行未知样本。有了这些硬约束reverse-skill 才能放心在团队里共享而不是变成一把没有安全锁的万能钥匙。4.3 什么场景不值得用路由包不是所有任务都要上逆向路由。遇到下面几类请求SKILL.md 会直接建议不要使用逆向流程只是单纯查一个符号名或 API 用法看文档即可。目标源码头文件还能拿到优先读源码而不是反编译。用户只是需要针对某个编译警告做修复这类任务应该走常规编码技能。这看起来有点“劝退”但我觉得这是技能包里最重要的设计之一让模型知道自己的能力边界比让它接下所有活更重要。一个什么都能答但都不靠谱的助手在实际工程里会变成灾难。5. 高发坑位与缓解手段我实际踩过的问题清单5.1 事实与猜测之间缺一堵墙在没有 dispatchers 的时候模型很容易“根据寄存器名称猜函数用途”。一个函数里出现strlen的引用它就可能推断这是在检查密码长度——但实际许多序列化和编码库也会调用 strlen。我采用的缓解策略是 evidence_first 规则任何结论行必须自带一条证据证据格式可以是命令输出片段或文件地址。禁止模型输出“我认为可能是”而没有对应证据支撑的判断一旦需要“可能”就应当显示地标记为待验证假设并在下一步安排验证动作。这种输出纪律是纯 prompt 很难长期维持的但把它写进 route 并让每个 dispatcher 都返回结构化证据后准确性明显改善。5.2 工具链不齐是个陷阱不是借口模型在本地执行objdump失败后有时会直接“修正记忆”转而用一个并不存在的工具路径去继续尝试结果当然还是失败。我在 dispatchers 的run_with_timeout.sh里固定加了一步命令执行前先which tool检查如果找不到立刻进入提示安装或缺省方法的路径不再继续执行后续依赖命令。这个判断非常小但很重要。一个无法产生新信息的循环会白白消耗大量上下文和时间。让 Agent 快速感知“工具存在与否”并给出下一步可操作建议比如“没有安装 radare2可用 apt 安装或者回退到 objdump”比反复试错高效得多。5.3 上下文窗口管理是持续博弈逆向工具的一个特点是输出量大且不可控。Ghidra 无头分析可能产生大量 intermediate 文件radare2 的aaa默认分析会持续数分钟并输出海量日志。目前 reverse-skill 的应对方式是三层漏斗dispatcher 层只回传摘要不回传全文。摘要超过一定行数时继续压成统计特征。模型需要细节时再用明确的地址范围或函数名去精读。这个“分层读取”模式有点像我处理大日志线上排障先看错误总数、时间分布、最高频异常再定位具体 trace。AI 编码客户端也一样它不能把整个二进制“吞进”上下文能拿到的是精心设计过的观察窗口。5.4 动态分析的隔离与超时技能包里的动态沙箱路由是最容易出问题的部分。如果你不小心让模型在真实环境中执行未知文件轻则环境脏掉重则产生实际安全问题。所以我把它设计成一个独立的子路由默认不启用。只有用户显式传入“可在沙箱环境运行”的开关时模型才会启动 dynamic_sandbox且启动前必须候选是虚拟网卡隔离快照并预设命令超时。实践心得建议在业务里用一个固定前缀的沙箱目录让 Agent 只能操作该目录下的文件不能访问其他路径。用run_with_timeout.sh为所有命令加上 5 秒默认超时避免某条分析命令卡死拖垮整个任务。5.5 路由误判与“拉不回来”最开始时路由判断会被一些看起来像二进制的文本文件带偏。比如 Linux 的脚本文件以#!开头理论上也是可执行文件但不该走 ELF 静态分析。后来 classify 里加了一条先读前 16 字节如果文件头是文本而非二进制 magic直接回退到“阅读脚本源码”路径。这个案例让我明白路由表不是一次就能写完美的它需要在实际误判中持续修正。reverse-skill 里专门维护了一个route_regressions目录每次出现误判都把样本特征和错误路径记下来作为后续分类规则的测试用例。这其实和逆向工程本身的“样本积累”思路是一模一样的。6. 长期维护 reverse-skill 的几个建议这套技能包已经成了我日常工作流的一部分但我也很清楚它离“产品级”还有不少距离。如果你准备在自己的项目里落地类似方案有四个维护层面的体会分享给你。第一技能包要跟着工具链升级走。radare2、Ghidra、binwalk 这些工具都在快速迭代命令参数时有变化。dispatcher 里的命令如果写得太具体老版本和新版本可能不兼容。我现在的处理是把大版本号写进配置遇到命令失败先看工具版本再决定是否调整参数。第二把一次成功分析沉淀成反向用例。每次跑通一个不错的分析路径我会把输入文件特征、路由链路和输出摘要进行脱敏后记录下来作为未来路由改进的参考。这比凭感觉改提示词靠谱得多。第三小心你的技能包被模型“绕过”。有些客户端支持用户直接修改 SKILL.md 或用更强指令覆盖约束。如果你把护栏写得太软模型可能为了完成分析而忽略授权判断。我现在把 deny 判断放在 classify 的最前面并让 dispatchers 在每次执行外部工具前都会重新校验一次输入路径是否在允许清单内相当于双重闸门。第四不要指望一个包解决所有逆向问题。符号执行、反混淆、程序切片这些更高级的需求目前靠技能路由只能做很浅的接入。reverse-skill 的价值是让 Agent 在常规分析里稳定发挥把高级分析工具作为可选的专家系统再接进来而不是让一个 Agent 假装自己是 IDA Pro。如果你平时也做安全研究或逆向分析并且希望 AI 编码助手别只当“代码生成器”而当半个分析员我建议你从最小的路由切片开始试。先拿一个你自己熟悉的文件类型写一个只有三步的分类规则和两个 dispatcher跑通之后再把更多路由加进来。技能包是我亲手验证过可行性的一个方向但真正好用的方案一定是在你手头样本上磨过几轮之后长出来的。