Skip to content

证据与引用约定

本书中的每一项事实都来自对 libtpu-0.0.40-cp314 wheel 中 libtpu.so 的静态逆向工程:这是一个 781,691,048 字节的 ELF64 共享对象,build-id 为 89edbbe81c5b328a958fe628a9f2207d(无歧义锚点;wheel 的包元数据报告版本为 0.0.40,运行时报告的 0.103 并不是二进制中的字面字符串,因此以 build-id 为准)。所有地址都是该单一二进制中的绝对虚拟地址;换一个 wheel,每个地址都会不同。

摘要

这是本书其余部分的校准页。其他每一页都会对一个闭源二进制提出主张:某个函数做 X、某个结构字段表示 Y、某个分发表有 N 条轴线;读者能够信任这些主张的唯一方式,就是确切知道它们是如何得出的。本页固定了这份契约。它说明所有事实的唯一来源(使用 IDA Pro 9.x 对未 stripped 的 libtpu.so 做静态分析),列出提示标记及其含义,固定地址和符号的引用风格,并枚举本次提取无法恢复的内容,让读者知道可信度的底线在哪里。

全书的内部规则很简单:所有分析都来自对二进制的静态逆向工程;没有使用源代码或任何其他受限材料。 全书中看起来像 C++ 标识符的名称,都是直接从二进制自身符号表读取并 demangle 后的符号;该对象没有被 stripped;这些名称不是猜测,也不是从外部源码树拉取的。某个名称不出现在二进制中时,页面要么说明该主张是推断出来的,要么不提出该主张。

先读一次本页。之后,本书其他位置的每个 > **QUIRK —** 提示和每个 sub_ADDR 引用,都带有精确且预先约定好的含义。

使用本书时,契约如下:

  • 一个二进制,一个来源。 每个地址、偏移、计数和符号,都可追溯到对上方版本钉住所描述的单一 ELF 的静态分析。没有任何内容来自运行该库、调试器或任何源码树。
  • 主张都有锚点。 每个主张都指向拥有同一二进制的读者可以独立找到的内容:地址、偏移、符号、字符串或表项。
  • 缺口会被说明,而不是隐藏。 反编译器在部分函数上失败,反汇编器也标记了数千个分析问题。这些限制在此公开,因此信任是经过校准的,而不是默认假设的。
源二进制libtpu/libtpu.so(位于 cp314 manylinux wheel 中),781,691,048 字节
Build-id89edbbe81c5b328a958fe628a9f2207d(NT_GNU_BUILD_ID,md5/uuid 形式)
提取工具IDA Pro 9.x — 反汇编器 + Hex-Rays 反编译器
是否 stripped? — 完整符号表;822,847 个函数带有 demangle 后的 C++ 名称
恢复的函数884,832(881,784 个有名 / 3,048 个匿名 sub_
证据形式反编译 C 函数体 + 反汇编 + 一组 JSON sidecar
提示标记QUIRK · GOTCHA · NOTE

证据来源

来源只有一个:版本钉住所指二进制的静态分析。该分析使用 IDA Pro 9.x 执行一次,其输出物化为页面可引用的两类内容:反编译/反汇编函数体,以及索引二进制结构的一组 JSON sidecar。没有页面会引用运行时 trace、日志、头文件或源码文件,因为这些都没有被使用。

反编译器与反汇编器

主要证据是 IDA 的 Hex-Rays 反编译器对每个函数输出的结果(类 C 伪代码),并由原始 x86-64 反汇编支撑。该二进制很大,745 MiB,约 884.8 K 个函数,并且绝大多数都带符号,因此反汇编器几乎能为所有内容恢复真实名称,而不是只有 sub_ 占位符。页面中的 ### Algorithm 块是这些反编译函数体的带注释改写,不是逐字转录;原始 sub_ADDR 会保留在注释中,因此任何主张都能与其建模的函数交叉核对。

符号名会被视为假设,而不是事实:一个名为 Validate… 的函数,如果检查函数体后发现它只记录日志并返回,那么文档会依据其函数体而不是名称来描述它。当名称和函数体不一致时,本书遵循函数体并标记这种差异。

sidecar 家族

除函数体外,提取过程还会产生一组 JSON sidecar,每个 sidecar 索引二进制的一个结构侧面。页面会引用承载所需事实的那个 sidecar。全书反复出现的有:

Sidecar它索引的内容
functions每个恢复的函数:地址范围、大小、调用者/被调用者、demangle 后名称、栈帧、栈变量、switch 和 try 块计数。
names符号/名称表:地址 ↔ 符号。
strings每个字符串字面量及其地址;多数行为推断的锚点。
segmentsELF segment/section 布局、权限、范围(与 readelf 交叉核对)。
data_tables恢复的常量/数据表(跳转表、描述符数组)。
switches恢复的 switch 分发结构及其跳转目标。
rttiC++ RTTI 记录:类型名、类层级、vtable 绑定。
fixups镜像中的重定位 / 指针 fixup。
xrefs交叉引用:谁调用/读取/写入每个地址。

sidecar 并不独立于函数体;它们是同一次提取按不同侧面查看的结果;但多个侧面之间的一致性,正是证明某个角色主张的依据,哪怕没有单独一行代码直接说出该角色。

已验证的提取范围

这些摘要数字已直接用二进制及其 sidecar 确认,是本书其余部分建立其上的规范锚点:

数量来源
恢复的函数884,832functions sidecar(长度)
↳ 有名 / 匿名881,784 / 3,048functions(名称前缀拆分)
↳ 带 demangle 后 C++ 名称822,847(约 93 %)functionsdemangled 非空)
字符串字面量1,249,324strings sidecar
RTTI 记录160,351rtti sidecar
数据表40,313data_tables sidecar
Switch 分发33,016switches sidecar
指针 fixup1,069,659fixups sidecar

该对象没有被 stripped,这是信任层面最重要的单一事实:约 99.66 % 的函数有名称,约 93 % 带有完整的 demangle 后 C++ 签名,因此多数角色主张都从真实符号开始,而不是从 sub_ 猜测开始。


提示词汇

本书使用三种 blockquote 标记:粗体文本,绝不使用 emoji;用于把读者注意力拉到容易被正文淹没的内容上。每种都有固定含义:

标记表示一行定义
> **QUIRK —**反直觉事实。某件真实但违背显然假设的事;重新实现者若按显然假设行事就会出错。
> **GOTCHA —**陷阱。朴素实现会静默出错的地方:它能编译、能运行,却产生错误结果。
> **NOTE —**澄清。一个重要点;它既不是陷阱,也不反直觉,但很容易漏掉。

引用风格

主张通过一套小而固定的引用语法锚定到二进制。学一次后,其他位置的每个锚点都能一眼解析。

  • 地址是绝对虚拟地址,十六进制,位于版本钉住所描述的镜像中:0xfe21da0sub_E635524。它们不是文件偏移,也不是相对于 section 的 RVA。裸 sub_ADDR 是 IDA 对该 VA 处匿名函数的占位名称。
  • 符号在 demangle 形式有助于阅读时,会同时引用 mangled 与 demangled 形式。 二进制存储 mangled C++ 名称(例如 _ZNSt3__u...);页面展示可读的 demangle 形式,并保留 mangled 符号以便精确查找。当页面在伪代码中使用友好名称时,它所建模的真实 sub_ADDR 会位于同一行注释中。
  • 结构体和对象字段按 base+offset 引用。 字段写作 object+0x18code_object+512,也就是结构内部的字节偏移;因为二进制没有字段名,偏移本身就是字段身份。结构布局表使用 Field | Offset | Type | Meaning 列。
  • 表、switch 和数据按其锚点地址引用,并在相关时附带索引。 例如“位于 0x… 的分发表,slot 7”,或“sub_… 中的 switch(总计恢复 33,016 个 switch)”。
  • 字符串按其字面文本和地址引用。 依赖字符串的行为推断会写出该字符串,使验证者能在 strings sidecar 中找到它并判断推断是否成立。

背后的规则是:每个主张都指向拥有同一二进制的读者可以独立找到的内容。 如果一条陈述无法锚定到地址、偏移、符号、字符串或标志位,页面要么说明这一点,要么不作该陈述。

NOTE — 绝对 VA 只有在 .text.rodata.lrodata 中才等于原始文件偏移。对驻留在 .data 中的结构体或表,文件偏移是 VA − 0x400000;在 .data.rel.ro 中则是 VA − 0x200000。对这些 section 中的数据,直接用裸 VA 通过 xxd/objdump seek 会读到错误字节;应先减去 section delta。完整 section map 见 ELF Anatomy

NOTE — vtable slot 索引从 vptr 开始计量,而 vptr 是 _ZTV 符号 + 0x10,即越过两字 header(offset-to-top,然后是 type_info 指针)。因此 call *0xN(%rax) 会落在 slot (0xN − 0x10)/8;如果从裸 _ZTV 地址计算,会多算一个 header(0x10)。请用真实调用点交叉核对任何 slot 编号。vtable 布局在 RTTI & Vtable Census 中解码。

一个完整锚点示例

本书中典型的带锚点句子会一次性承载引用语法的每一层。例如,某页可能写道:

text
位于 `sub_FE21DA0` 的 shape-inference 分发器
(`_ZNSt3__u10__function13__policy_funcI…11__call_funcI…E`) 从
`ctx+0x10` 读取 op kind 并据此 switch(33,016 个已恢复 switch 之一);
default 分支返回 `kInvalidArgument`。
```text

拆开来看,这一句给验证者四个独立把手:用于跳转的**绝对 VA** `sub_FE21DA0`、用于确认这是正确函数的 **mangled 符号**、被读取字段的 **base+offset** `ctx+0x10`,以及作为分发机制的 **switch**;每一项都可对照二进制和匹配的 sidecar 检查。

---

## 已知提取限制

提取覆盖面很广,但并不完整;校准信任的读者需要了解失败面,就像需要了解成功面一样。三类缺口很重要。

### 反编译器无法恢复的函数

Hex-Rays 反编译器未能为一小组函数生成 C 函数体;记录的错误是 `idaapi.decompile returned no cfunc`。整个提取中,**516 个函数**被这样标记;它们往往是最棘手的情况(深层嵌套模板实例化、`__policy_func` 调用分发器、过大的函数体)。对于这些函数,只有原始反汇编存在,因此行为主张依赖反汇编和 `xrefs`,而不是某行反编译代码。触及这些函数的页面会明确说明。

### 反汇编器分析问题

IDA 在恢复过程中标记了 **7,915 个分析问题**。这些并不全是“缺失函数”;它们是分析器不确定代码/数据边界、栈帧或指令头的位置。细分如下:

| 问题类型 | 计数 | 含义 |
|---|---|---|
| `final` | 4,188 | 分析到达固定点,但仍有残余歧义。 |
| `rolled` | 1,659 | 某个决策在重新分析期间被回滚。 |
| `disasm_problem` | 942 | 反汇编器无法干净解析为指令的位置。 |
| `bad_stack` | 574 | 栈指针跟踪不一致;此处的 frame/stack-var 主张更弱。 |
| `head_problem` | 545 | 指令头对齐不确定。 |
| `illegal_addr` | 7 | 引用了任何已映射区域之外的地址。 |

这些问题不会推翻摘要计数(这些计数已与原始二进制交叉核对),但少部分逐函数细节,尤其是靠近 `bad_stack` 位置的栈变量布局,不如大多数内容可靠。

### 静态分析在结构上看不到的内容

有些事实根本不在静态镜像中,再多反编译也无法恢复:

- **仅运行时值** — 初始化时协商出的实际缓冲区大小、真实设备拓扑、由环境驱动的旋钮值。本书记录使用这些值的*代码路径*,而不是这些值本身。
- **分析器无法解析的间接层背后的数据** — 目标依赖运行时状态的计算跳转或 vtable 调用,会表现为间接边,而不是具体被调用者。`xrefs`/`switches` sidecar 会解析它们能解析的部分;其余部分按形状识别。
- **不透明 blob 的语义** — 压缩区域或 descriptor-pool 区域会按形状和入口点识别,但不会完全解码,除非有专门页面进行解码。

> **GOTCHA —** demangle 后的符号名是一份馈赠,但它们描述的是*原作者给函数起的名字*,不一定描述它在此构建中的行为。一个名为 `Validate…` 的函数,如果检查函数体后发现它只记录日志并返回,那么文档会依据其**函数体**而不是名称来描述它。当名称和函数体不一致时,本书遵循函数体并标记这种差异;任何只从名称重新推导主张的读者也应如此。

---

## 交叉引用

- [How to Read This Book](how-to-read.md) — 阅读路径与 dependency-flow 理由。
- [Codename Cheat-Sheet](codename-cheatsheet.md) — 同级 front-matter 页面;本页证据语法对应的词汇表。
- [Methodology](../methodology.md) — 完整流程:获取、工具、交叉验证纪律和法律依据。
- [Binary Forensics Overview](../forensics/overview.md) — 规范摘要结构计数,已对原始二进制确认;也是此处复用的版本钉住数字来源。
- [Dispatch-Table Taxonomy](../forensics/dispatch-table-taxonomy.md) — 如何读取 33,016 个 switch 和 40,313 个数据表。
- [RTTI & Vtable Census](../forensics/rtti-vtable-census.md) — 160,351 条 RTTI 记录如何变成类层级主张。
- [ELF Anatomy](../forensics/elf-anatomy.md) — 本书引用的每个绝对地址背后的 segment/section 布局。