2.2 r2pipe可编程逆向


2.2 r2pipe可编程逆向

本节摘要:r2pipe 是 radare2 的编程绑定:以 r2 -q0 方式拉起一个 r2 进程,通过一根管道收发命令,Python、Go 等多语言皆可接。本节先讲清这根管子的模型——r2 变成一个「命令进、文本或 JSON 出」的服务,cmd 与 cmdj 两种调用形态;然后给最小示例验证连通;核心是实战脚本 extract_functions.py:aflj 拿函数清单、pdc 逐个取伪代码文本、按字段组装并截断,落成 demo_functions.json——与第 1.3 节 Ghidra 侧导出同构的备料。最后讨论备料形态的设计原则:函数级切分为什么是对的(对照《语义代码检索》第 3 章)、体量预算怎么定、截断要留什么痕迹——这份 JSON 将在第 04 章进入 RAG 向量库与模型提示词。

学习目标

  • 画出 r2pipe 的进程与管道模型,说出 cmd 与 cmdj 的区别。
  • 独立跑通 Python 经 r2pipe 列函数的最小示例。
  • 编写批量导出「函数 + 伪代码」JSON 的脚本并处理截断。
  • 按下游模型消费需求设计备料字段与体量预算。
  • 说清函数级切分与 AST 切分的异同与取舍。

一、r2pipe:一根管子接通两个世界

第 2.1 节的命令都是人敲的;r2pipe 让程序来敲。模型很简单:

┌──────────────┐ 命令字符串(如 "aflj") ┌──────────────┐ │ Python 脚本 │ ─────────────────────────▶ │ r2 进程 │ │ (r2pipe) │ ◀───────────────────────── │ (r2 -q0) │ └──────────────┘ 文本 或 JSON(cmdj 解析) └──────────────┘ 同一进程内可反复收发;结束时 r2.quit() 收摊 ​

关键设计:r2 不嵌入 Python,Python 也不嵌入 r2,两个进程用管道对话。好处是解耦——r2 崩了重启即可,语言随意换(官方提供多语言绑定);代价是每次 cmd 都是字符串协议,命令拼写错误要到运行时才暴露。

安装:pip install r2pipe(第 0.2 节环境清单已含),r2 本体在 PATH 里即可。

二、最小示例:列函数

# r2pipe_min.py —— 最小示例:列函数,验证管道连通 import r2pipe r2 = r2pipe.open("bin/demo_stripped", flags=["-A"]) print(r2.cmd("afl")) # 文本形态:人看的 funcs = r2.cmdj("aflj") # JSON 形态:程序用的 print("函数数量:", len(funcs)) print("第一个函数:", funcs[0].get("name"), hex(funcs[0].get("offset", 0))) r2.quit() ​

对 demo_stripped 预期输出十几行函数列表,函数数量在十个上下(含库导入桩,视平台与链接方式浮动)。如果这里报错,九成是 r2 不在 PATH——回第 0.2 节自检 r2 -v。

三、实战:批量导出函数与伪代码 JSON

目标:产出与第 1.3 节 Ghidra 侧同构的数据——每条记录含名字、地址、体量,再加大模型真正要读的东西:伪代码文本。

# extract_functions.py —— r2pipe 批量导出函数与伪代码,为模型备料 import json import r2pipe TARGET = "bin/demo_stripped" OUT = "out/demo_functions.json" MAX_TEXT = 4000 # 每个函数伪代码的截断上限(字符,示意) r2 = r2pipe.open(TARGET, flags=["-A", "-q"]) funcs = r2.cmdj("aflj") or [] records = [] for item in funcs: name = item.get("name", "") offset = item.get("offset", 0) size = item.get("size", 0) if size < 16 or name.startswith(("sym.imp.", "loc.")): continue # 跳过导入桩与太小的碎片 text = r2.cmd("pdc @ %s" % name) or "" text = text.strip() records.append({ "name": name, "offset": offset, "size": size, "pseudocode": text[:MAX_TEXT], "truncated": len(text) > MAX_TEXT, }) r2.quit() with open(OUT, "w", encoding="utf-8") as fh: json.dump(records, fh, ensure_ascii=False, indent=2) total = sum(len(rec["pseudocode"]) for rec in records) print("exported %d functions, %d chars -> %s" % (len(records), total, OUT)) ​

运行后 out/demo_functions.json 里每条记录形如:

# out/demo_functions.json —— 导出结果示例(节选,示意) [ { "name": "fcn.00401189", "offset": 4198792, "size": 76, "pseudocode": "do {\n var_2h = ... ^ 0x5a;\n} while (...);", "truncated": false } ] ​

工程细节三个:其一,-q 安静模式减少管道噪音;其二,过滤规则(size 下限、名字前缀)按对象调,CTF 样本常见大量小碎片函数;其三,pdc 是内置伪反编译,若需 Ghidra 级质量可装 r2ghidra 插件换 pdg,脚本只需改一个命令字符串。

四、为 LLM 备料:数据形态设计

这份 JSON 的最终读者是第 04 章的大模型。备料好坏直接决定那边的上限,三条原则:

原则 做法 理由
函数级切分 一函数一条记录 恰好装进一次提示词;函数是行为的最小完整单元
体量预算 每条截断并打 truncated 标 超长文本会撑爆上下文;截断必须可发现
保留地址 offset 字段不能丢 模型说「fcn.00401189 可疑」时,人要能一秒跳回 r2 / Ghidra

关于函数级切分,与源码侧对照:《语义代码检索》第 3 章《代码切分》讲 AST 级切分——按语法树切块,兼顾语义完整与检索粒度。二进制侧没有语法树,只有分析器恢复的函数边界,所以本书直接取函数级:粒度更粗,但边界由调用约定与分析器保证,且与 r2 / Ghidra 的交互单元天然对齐。

体量预算的量级感(示意,随模型代际浮动):单函数伪代码数百到数千字符;一个中型样本全部函数合计几万到几十万字符——后者显然不能整包塞进提示词,这正是第 4.2 节要用 RAG 检索的原因:入库的是本章的 JSON,查询时只取最相关的几个函数。

五、从批处理到智能体的伏笔

本章结束了「人驱动工具」的阶段,手里多了两样东西:

  • 命令直觉(2.1 节):seek、afl、pdf、axt——这些命令将在第 04 章由模型来发。
  • 备料格式(2.2 节):函数级 JSON——第 4.2 节进 RAG 向量库,第 4.3 节被验证流水线消费。

(文字流程图)demo_stripped ──▶ r2 -A ──▶ aflj 函数清单 ──▶ pdc 伪代码 ──▶ demo_functions.json ──▶(第 04 章)RAG 入库 / 假设验证。

本节要点回顾

  • r2pipe 模型:两个进程一根管道,cmd 出文本、cmdj 出 JSON。
  • extract_functions.py 产出与 Ghidra 侧同构的函数级 JSON,含截断标记。
  • 备料三原则:函数级切分、体量预算、保留地址。
  • 二进制侧没有 AST,函数边界由分析器保证——与源码侧切分思路同源、粒度更粗。
  • 这份 JSON 是第 04 章 RAG 与验证流水线的输入。

地基篇(第 00~02 章)到此完工:Ghidra 立起工程化阅读,r2 立起命令行与备料。下一章起把工具交给模型——先从 GhidraMCP 开始,让模型直接列出函数、读伪代码、改名字、追引用。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U