离线二进制诊断¶
inspect_binary_layout / inspect-binary-layout 回答一个显式文件包含什么:节/段布局、原始符号/重定位、静态链接名称和缓解措施指标。它无需活跃的 Hopper、Ghidra 或 IDA 目标即可工作。CTF 是其中一种可能用途;崩溃分诊、兼容性和普通二进制检查都使用同一模块化能力。
自带引擎¶
初始实际验证覆盖 Linux x64、ELF64 x86-64 小端 EXEC/DYN/REL。提供一个绝对路径的 Python 可执行文件,其环境已包含未改动的 pwntools 4.15.0、pyelftools 0.33 和 Unicorn 2.1.2:
REA_PWNTOOLS_PYTHON=/absolute/isolated-env/bin/python \
rea inspect-binary-layout ./selected.elf --json
REA 从不安装 Python、软件包或 GDB,不会修改用户初始化文件,不会将所选对象作为宿主进程启动,也不会请求运行时库解析。Python 进程使用隔离模式和自有缓存。精确的上游配置记录在上游溯源中。
该适配器跳过 pwntools 内核配置和版本发现:这些内存内容扫描不提供本报告的任何字段。普通 IKCFG_ST 数据不会被解释为内核配置请求,虚拟零填充的 LOAD 尾部根据其头部进行报告,而不会为该扫描物化这些尾部。GOT/PLT 推断和所宣称的静态缓解措施指标仍使用固定的上游实现。头表文件范围在依赖查找之前进行检查。结构清单和便捷映射引擎仍分别解析快照;要消除这一剩余重复,需要保留上游 GOT/PLT 和缓解措施事实。
解读结果¶
- 工件路径、SHA-256 和大小标识原始选中的字节。所有解析读取的都是自有稳定快照;原始文件保持不变。
- 地址、偏移、长度和标志均为十六进制字符串。链接地址不是运行时地址。运行时加载基址保持为 null。EXEC/DYN 条目为零表示不存在;可重定位条目则不适用。
- 每个符号保留其表和条目索引。其报告的值可能表示未定义、对齐、绝对值、无地址、未知节索引、节偏移、TLS 偏移或链接虚拟地址。重复名称仍为独立条目。SHN_XINDEX 在所选取的上游表示中仍保持未解析;其外部索引表不会被静默视为已解析。
- 可重定位对象具有节相对重定位,且不声明可执行入口。带符号的 REL/RELA 重定位加数为十进制字符串。打包的 RELR 表保留完整原始字节和上游解码的偏移量作为派生证据,每个偏移的打包字位置和隐式加数均未知。整体重定位清单完整性仍未知。 可重定位的 SHN_UNDEF 目标或非活动 SHT_NULL 头部仍是显式未知,并带有原始报告的节索引和偏移。
- REL/RELA 符号引用保留其原始表和条目索引。正索引通过已验证的符号表解析;符号索引为零表示零符号值,不进行表查找,包括未链接任何表的情况。格式错误的引用会连同受影响的节和符号索引一起失败。
- 节名称表和符号表要求声明 SHT_STRTAB 链接。SHN_UNDEF 明确表示没有节名称:显示为空,原始名称和位置未知,但保留原始名称偏移。扩展节名称索引通过节零解析,并经过相同的验证。
- 符号表要求声明 SHT_STRTAB 链接。名称偏移和终止符必须保持在声明的字符串表内;格式错误的引用会在无关字节成为名称之前失败。动态依赖使用 DT_STRTAB/DT_STRSZ 配合唯一的文件后备映射;歧义或无后备的映射明确不受支持。
- 名称显示字符串对不透明字节使用 UTF-8 替换,包括无节依赖名称和解释器路径。原始名称字节和字符串表范围在可解析的情况下保留观察到的标识;范围包含终止 NUL,而 base64 字节排除它。
- NOBITS 和 NULL 节不提供文件字节。报告的文件后备范围在公共边界再次对照快照大小检查。PT_NULL 载荷字段未使用:报告的数字保留,文件后备为空,解释的权限为 null。文件大小为零的段也没有文件字节。
- 无节动态映像仍报告依赖名称,并在可唯一映射处提供原始字节和文件位置。缺少符号/重定位节表并不证明这些运行时表不存在。上游 RELRO/canary 启发式算法依赖节,在这种情况下可能不完整;其报告的候选保留明确的覆盖限制。
- DT_NEEDED/PT_INTERP 是报告的名称,而非解析后的运行时路径。GOT/PLT 映射是派生的便捷视图,可能合并别名。PLT 推断可以在 Unicorn 中模拟选定的指令;其输出是静态候选。Exec 系统调用跟踪观察宿主进程启动。映射完整性仍未知,原始上游警告以内联方式返回。
- Canary、PIE、NX、可执行栈和 RELRO 均为静态推断。NX 和可执行栈是独立的上游指标:在 x86-64 上,可执行栈可能伴随未知(
null)的 NX 指标。ET_DYN 可能是共享库,而缺少 canary 符号并不证明每个函数都未受保护。
当完整布局 Evidence 已保留时,inspect_analysis_view / inspect-analysis-view 无需重新运行解码器即可投影摘要、缓解措施或链接方面、某个节或符号,或一个稳定页面。CLI 一次性调用传递可移植的内联 Evidence JSON;MCP 会话使用保留的 evidence_id。页面 limit 是必需的,并受工具契约中记录的已测量 MCP stdio 预算限制。空的保留节或符号集合没有有效的条目索引。其页面视图仍可返回空页面。格式错误的经验证的布局输入仍会被拒绝,并在 source.normalized_result 下附带所属模式的字段安全诊断,包括字段路径和类型或值原因。Evidence 认证和解码器验证仍是独立的边界。
通过 MCP,inspect_binary_layout 使用 "detail": "summary" 时会将会话中完整的布局证据保留下来,仅返回其摘要视图。其 normalized_result.parent_evidence_id 引用所保留的布局,供后续的节、符号、缓解或链接视图使用。默认的 complete 详细级别和 CLI 输出保持不变。
完整结果具有 32 MiB 输入、64 MiB 回复、1 MiB 合并诊断和 30 秒自有命令截止时间。REA_PWNTOOLS_MAX_INPUT_BYTES 单独提高输入上限,最高可达 1 GiB;回复和截止时间上限不变。回复预算适用于解码器记录;CLI/MCP 证据封装会增加副本和编码开销。固定的 MCP SDK 默认使用 10 MiB 接收缓冲区。大型完整结果需要调用方配置 StdioClientTransport({ maxBufferSize: ... }) 以及匹配的服务器端 REA_MCP_MAX_RESPONSE_BYTES。参见 MCP 交付预算。这些设置影响的是交付,而非解码器覆盖范围或限制。大型扩展索引验证使用 256 MiB,并在其独立的可选 MCP 通道中设置显式的五分钟请求超时。REA 不会静默截断结果。Python 将资源的软限制降低为至多 3 GiB 虚拟地址空间、30 CPU 秒和 64 MiB 文件输出。继承的更严格软/硬限制会被保留,有效值会在结果限制中返回。虚拟地址空间不是 RSS;Unicorn 需要大型虚拟映射。限制失败时不会出现部分成功,所属进程/根目录清理独立于取消而完成。
核心转储文件、GDB 会话/状态/控制、可选的 pwndbg 增强和指令检查是由 #969 跟踪的独立增量。此增量不对这些功能或其他操作系统/架构做出实际支持声明。
类型化解码器失败会在原始失败类别和原因之外,保留有界的 captured_output(stdout、stderr 和显式截断标志)。成功的 diagnostics 也会公开监督者的 truncated 标志。超出完整诊断预算的输出会被拒绝,包括解码器写入有效回复时也是如此;清理和延迟取消会复用观察到的标志。内存分配失败使用 resource_constraint,并包含有效资源限制(或显式未知)以及针对内存的恢复指导。单独的 MemoryError 并不能确定具体是哪个分配失败,也不能确定哪个特定预算已耗尽。无效的过小符号条目会在 REA 报告其源范围之前被拒绝。
桥接器保留 1 MiB 用于报告分配失败。如果连错误报告或序列化都因 MemoryError 失败,其保留的退出状态会以未知的有效限制保留资源分类。保留的退出需要桥接器实际失败分支写入的匹配私有标记。裸启动器退出或缺失/不可写的标记会保留进程诊断,但资源原因未经验证。仅信号终止本身绝不会被归类为观察到的内存耗尽。小型引导程序还会在适配器导入、编译和初始化期间捕获可捕获的 MemoryError。引导程序运行之前的 Python 解释器启动失败会保留其观察到的进程诊断,而不猜测原因。SIGXCPU 保留 CPU 资源诊断和针对 CPU 的恢复建议。观察到的 EFBIG 写入或 SIGXFSZ 终止会保留文件大小资源诊断和针对文件大小的恢复建议。如果紧张的文件限制也阻止了限制记录的写入,则有效值仍保持未知。桥接器在分析之前将实际限制记录到自有存储中;缺失或格式错误的限制报告保持未知,并保留其读取失败信息。仅收到信号并不能确定其确切原因。动态标签要求在 PT_DYNAMIC 内有一个完整的 DT_NULL;解释器名称和范围在第一个 NUL 处结束,任何填充仍由原始段范围表示。
Ghidra 进程失败¶
当失败的 Ghidra 启动或操作输出报告 java.lang.OutOfMemoryError 时,REA 返回 resource_constraint。Java 堆或 GC 开销耗尽会包含针对堆的恢复建议。Metaspace 和数组大小失败不会建议增加堆;本机线程耗尽会报告为未知限制的容量约束。
configured_jvm_max_heap 记录 REA 的启动构建器选择的显式 -Xmx 值。这是一个配置事实,不是实测内存使用量,也不能证明有效的 JVM 限制;JVM 环境选项可能会影响该限制。当 Windows 脚本包装器选择 JVM 时,该值保持未知。历史 stdout 和 stderr 仍然可用,但分类仅使用失败操作的 failure_output。保留的 stdout 和 stderr 共享 8 MiB 预算,但每一个已读取的字节都会扫描 java.lang.OutOfMemoryError:failure_output.observed_oome 记录失败操作中每个流里最新已确认 token 的有界行及其字节偏移,即使在预算耗尽后也是如此。这些观察结果独立承担 OOME 检测;以类名前缀结尾的保留文本无法确认完整 token。line_complete 区分已知行结束和不完整的尾部上下文;invalid_utf8 标记替换解码的上下文。没有观察到 OOME 的不完整操作输出会保留原始失败代码,并说明无法排除内存原因。仅不完整本身不能确认内存约束,也不能证明堆特定建议的合理性。Token 上下文保持在失败操作的检查点内;不同标识符中的类名前缀不是 OOME 证据。尾部裸 token 需要观察到的流 EOF 才能确定其边界。输出排空最多等待一秒,但可能因提前取消或启动截止时间而更早结束。output_incomplete 标记在流关闭之前拍摄的快照;清理所有权仍归会话持有,直到释放成功。
内核或容器的 OOM killer 以 SIGKILL 终止 Ghidra,且不会打印 OutOfMemoryError。当进程或协议故障记录了 exit_signal: "SIGKILL" 且没有 stop_requested 时,REA 会报告该信号,而不是通用的 rea doctor 建议。进程监督器在 REA 开始停止 Ghidra 后、发送任何信号之前就记录 stop_requested: true,因此 REA 自身的 SIGKILL 永远不会被报告为外部终止。
在启用了 cgroup v2 的 Linux 上,REA 还会在启动前和终止后各读取一次自身 cgroup 的 memory.events,通过 /proc/self/mountinfo 中的 cgroup2 挂载点定位该组。memory_cgroup 记录组路径、oom_kill_before、oom_kill_after 和 memory_max。如果计数上升,REA 返回 resource_constraint,其中 resource: "memory",且 reported_limits 中包含 cgroup_memory_max。该计数涵盖 REA 所在 cgroup 及其子组中的每个进程;当组被共享时,可能包括 REA 进程树之外的进程,因此计数上升只表明内核 OOM killer 终止了该 cgroup 或其子组中的某个进程,而不表明它选择了哪个进程。memory_max 是以字节为单位的限制,当组未设置任何限制时为 "max",当无法读取时为 null;父组的限制不会被读取。该限制同样涵盖 Ghidra 的原生反编译器进程,其数量随可用 CPU 数量变化,因此建议不会推荐增大 Java 堆。当会话已经完成某项操作时,建议保留使用 open_binary 重新打开目标的指令。如果没有 cgroup v2 内存组,或计数未上升,则故障仍为 execution_failure,并将该信号列为可能的内存不足终止。
可移植 PE 资源¶
inspect_pe_resources / inspect-pe-resources 在不使用活动目标、原生引擎、Python 或 Windows SDK 的情况下检查显式的 PE32 或 PE32+ 文件:
完整的 Evidence 通过路径、字节数和 SHA-256 标识稳定的工件快照。资源保留独立的类型、名称和语言身份:数字 ID 与 UTF-16 名称互不相同,后者的原始字节也会被报告。目录头、目录项、数据项和负载保留原始文件偏移量和长度。每个不透明负载都有自己的 SHA-256;代码页和保留的数据项字段均被保留。
每个 directories[] 行保留其身份 path 和匹配的传入 entry_locations:对于根目录两者均为空,随后包含类型以及可选名称。空分支即使没有负载叶节点,也保留这些身份。共享目录字节会为每个传入路径生成一行,包含原始目录范围以及每个路径自己的条目位置。这些行计入元数据预算。存在的空根目录与不存在的资源数据目录(directory: null,directories: [])保持区别。
RT_GROUP_ICON 记录暴露原始图像描述符和 RT_ICON 候选。仅当存在具有相同数字 ID 和语言的图标时,引用才会解析。缺失的引用、其他语言候选以及声明大小不匹配都会保持明确。编码的尺寸会被保留(零表示 256 像素)。这是静态引用检查;不会进行图像解码、语言回退、资源加载或所选文件执行。
不存在的资源数据目录是完整的空清单。格式错误的头、截断的表、循环、重复的兄弟身份、无支撑或模糊的 RVA 映射、不完全相同的重叠范围以及不支持的树形状都会显式失败,而不是返回部分清单。精确共享的负载范围受支持。支持的树有三个级别:类型、名称和语言。其他资源负载格式保持不透明。
max_file_bytes 默认为 64 MiB(最大 512 MiB);max_entries 默认为 16384,这是所测得的常规 Windows 资源映像中的最大条目数,并留有裕量(最大 65536)。CLI 选项为 --max-file-bytes 和 --max-entries。元数据限制为 8 MiB;超过任何预算都会失败,且不会截断。大型 Evidence 信封可能需要调用方配置的 MCP 传输接收缓冲区,因为信封副本会增加开销。请求在快照读取、目录遍历和负载哈希期间会响应取消。该操作仅读取常规文件,并拒绝符号链接。
字节熵分类¶
inspect_byte_entropy / inspect-byte-entropy 在不使用分析引擎、不执行、不提取、不访问网络的情况下,对任何本地常规文件进行特征分析。
rea inspect-byte-entropy ./firmware.bin --block-size 4096 --json
rea inspect-byte-entropy ./firmware.bin --offset 4096 --length 65536 --json
{
"name": "inspect_byte_entropy",
"arguments": { "path": "/artifacts/firmware.bin", "detail": "summary" }
}
完整的 Evidence 包含 artifact(原始路径、字节大小和 SHA-256)、block_size、window、file_entropy、thresholds、blocks、runs 和 limitations。对于一个 4096 字节的零填充文件,熵字段如下:
{
"block_size": 4096,
"window": { "offset": 0, "length": 4096 },
"file_entropy": 0,
"thresholds": { "high": 7.2, "low": 1 },
"blocks": [{ "offset": 0, "length": 4096, "entropy": 0 }],
"runs": [{ "offset": 0, "length": 4096, "classification": "low" }]
}
偏移量和长度均为十进制字节数。数据块从请求的 offset 处开始(默认为零);省略的 length 延伸到 EOF。显式超出 EOF 的窗口会失败,而不是静默裁剪。最后一个数据块可能较短。block_size 必须是 256 到 65536 之间的 2 的幂,默认为 4096。一个窗口最多可产生 32,768 个块行;更大的请求会在文件扫描前以容量错误失败。请选择较短的窗口或较大的块大小。SHA-256 和 file_entropy 始终覆盖整个文件,包括在选择了窗口时也是如此。空文件和空窗口均被接受;空熵按约定为零。该工具读取文件所报告的大小。报告大小为 0 的文件(例如 Linux /proc 条目)不返回任何块,并附带一项限制:结果并不表示该文件为空。在读取过程中会检查文件身份、大小和时间戳;检测到变化会产生错误,而不是生成 Evidence。
香农熵以每字节比特数报告,取值范围为 0 到 8,四舍五入到小数点后四位。报告熵值至少为 7.2 的相邻块构成 high 连续段;不超过 1.0 的块构成 low 连续段。其他块会中断连续段。熵并不能证明加密,该指标也无法区分加密数据与压缩数据。小尺寸块可能低估熵值;其结果仅作为初步排查线索。
MCP detail: "summary" 会在会话中保留完整 Evidence,并返回文件熵、块/连续段计数、阈值、所选窗口以及最长的十个连续段(并列时按文件偏移排序)。可使用其 parent_evidence_id 检索完整 Evidence,或通过 inspect_analysis_view 请求其摘要。CLI 通过同一服务返回完整 Evidence。摘要详情需要能够保留 Evidence 的会话。
读取操作使用一个块大小的缓冲区和一个 256 项直方图。完整输出可能非常庞大,因为它包含每个所选块对应的一行,而且摘要交付仍会保留完整列表。可通过缩短长度或增大块大小来缩减输出。完整 Evidence 及其序列化所需内存与所选块数量成正比。32,768 行上限约束了实际保留的块对象及其序列化表示:下文所述达到上限大小的连续段在实测主机上产生了 1,514,877 字节的序列化输出,并使 V8 堆占用增加 21.2 MiB。因此,该上限独立于文件大小约束此输出;它并非可移植的进程内存保证。进一步缩小窗口或增大块大小可减少保留的行数。
本地服务实测(macOS arm64,Node 24.18.0,100,000,000 字节文件,4096 字节块,文件已位于操作系统缓存中):首次进程运行耗时 1.113 秒墙钟时间 / 1.148 秒 CPU,峰值 RSS 为 145.3 MiB;同一进程内第二次运行耗时 1.015 秒 / 1.035 秒 CPU,累计峰值 RSS 为 155.8 MiB。每次读取 100,000,000 字节,产生 24,415 个块。测量包括 Evidence 构建、摘要投影和 JSON 序列化,不涉及子进程。完整 Evidence 序列化为 1,122,303 字节,其摘要为 2,705 字节。一个达到上限的 134,217,728 字节稀疏文件,使用 4096 字节块,产生了 32,768 行和 1,514,877 字节序列化输出;在该进程中 V8 堆占用增加 21.2 MiB,RSS 增加 18.1 MiB。这项独立测量使用了相同的 macOS arm64 主机和 Node 版本。未测量冷磁盘 I/O、token 数量和智能体调查;此前也没有等效工具可供基准对比。这些数据并不能确立可移植的峰值内存上限、保留 Evidence 的恒定内存占用,或更快的智能体任务完成速度。
可移植的原生导出¶
inspect_native_exports / inspect-native-exports 可在任何受支持的主机上读取显式 PE 或 ELF 映像的导出表,无需活动目标、原生引擎、objdump 或 Windows SDK:
完整 Evidence 通过路径、字节数和 SHA-256 标识稳定的工件快照。完整交付仍是默认行为。如需较小的初始 MCP 回复,可选择 detail: "summary";这会保留完整 Evidence,并返回工件标识、表覆盖范围和行数。可将其 normalized_result.parent_evidence_id 与 inspect_analysis_view 配合使用:
{
"source": {
"kind": "retained-evidence",
"evidence_id": "ev_<parent digest>"
},
"view": {
"kind": "native-exports",
"collection": "exports",
"names": ["selected_name"],
"fields": ["name"],
"offset": 0,
"limit": 32
}
}
名称精确匹配,并保留每个同名版本或别名。可选的 indices 选择原始行位置;当同时提供名称过滤器时,两者取交集;空过滤器不选中任何内容。source_index 始终保留,并连同父 ID 一起标识原始导出行或问题行。字段默认为完整行;空字段列表仅返回其标识。ELF 的 name 选择还会返回原始字节和编码限定信息。其他格式的字段会被拒绝。collection: "issues" 对完整诊断记录分页,可附带原始索引。offset 和 limit 默认为 0 和 64,并按原始顺序应用于匹配行。
视图 coverage 描述所选页面;summary.coverage 或 exports.coverage 保留源表的覆盖范围。已耗尽或空的页面并不会使部分源数据变为完整。保留的视图绝不会重新打开文件。CLI 使用相同的投影器,并且摘要需要持久化 bundle:
rea inspect-native-exports ./libexample.so --detail summary --evidence_output ./exports.bundle.json --json
# Pass the summary's parent ID and the same view as JSON to inspect-analysis-view,
# together with --evidence-bundle ./exports.bundle.json.
- PE 行重现导出目录:每个已使用的导出地址表槽位及其带偏置的
ordinal(OrdinalBase加上槽位索引),每个名称一行(别名共享同一个序号),address为首选基址虚拟地址;当槽位转发到另一个 DLL 时则为forwarder文本。未使用的零地址槽位会被省略;仅有序号的导出其name为 null。名称-序号表存储无偏置的 16 位 EAT 索引;报告的带偏置序号可能超过 16 位。目录的 DLL 名称、序号基址和时间戳都会被报告;没有导出目录的映像是一个完整的空清单。 - ELF 行列出
.dynsym中已定义的符号,包括全局、弱或 GNU 唯一绑定,以及默认或受保护可见性。每行保留value(按存储原样保留st_value;位 0 在 32 位 ARM 上标记 Thumb 代码)、size、type和binding。ELF 字符串表不声明文本编码。包含非 ASCII 字节的名称在这些字节构成有效 UTF-8 时按 UTF-8 解码;name_utf8_decodable记录非 ASCII 名称字节是否为有效 UTF-8,name_bytes_base64保留这些原始字节(ASCII 名称为null)。当它们不是有效 UTF-8 时,name使用保留字节的 Latin-1 映射,原始字节仍可用。较早的保留结果对新添加的编码字段报告为 null。无版本符号的version为 null。有版本符号会报告其 GNU 索引、从.gnu.version_d或.gnu.version_r解析出的名称、相应的name_bytes_base64和name_utf8_decodable事实,以及来自.gnu.version的hidden标志。版本名称使用与符号名称相同的 UTF-8 有效即解码、否则保留字节的 Latin-1 显示映射;ELF 不声明任一字符串的文本编码。较早的保留结果对新添加的版本名称编码字段报告为 null。"unknown"表示无法读取符号版本表。隐藏版本不是按名称dlsym查找所选择的默认版本。当两个表命名同一索引时,定义名称优先。
无法读取的名称、超出范围的名称序号以及无法读取的转发器会列在 issues 中;其条目不会被报告,覆盖状态为 partial。PE 导出名称是有界、以空字节结尾的字节字符串;当虚拟字节无歧义时,它们可能跨越相邻的、由文件支持的节区映射。间隙、零填充和冲突映射仍属于无法读取的名称。未解析的符号版本会保留其行:当没有定义或需求项引用该索引时,版本名称为 null;当版本表无法读取时,version 为 "unknown";覆盖状态同样为 partial。没有空条目的动态符号表是格式错误的。没有节头的 ELF 为 unsupported。有节头时,如果观察到 PT_DYNAMIC 段,但缺少 SHT_DYNSYM 节,也为 unsupported,包括仅包含非活动空节的节目录。从该段枚举动态符号尚未实现。如果存在的节目录既没有动态符号表也没有动态段,则报告一个完整空清单。Mach-O 导出通过 inspect_macho 报告。
max_file_bytes 默认为 256 MiB(最大 512 MiB;CLI 参数 --max-file-bytes)。PE 头中的导出目录与节中的导出目录读取方式相同。超过 262144 个条目、超过 262144 行或保留名称超过 16 MiB 的导出表或动态符号表会失败,且不会部分成功。导出行不表明符号在运行时可达、可安全调用或已加载。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 rea2/rea