Skip to content

方法论

本书中的每个事实都来自对可自由再分发的 libtpu-0.0.40-cp314 PyPI wheel(manylinux_2_31_x86_64)中 libtpu.so 的静态逆向工程:这是一个 781,691,048 字节的 ELF64 shared object,build-id 为 89edbbe81c5b328a958fe628a9f2207d。build-id 是明确的锚定;wheel 的包元数据报告版本为 0.0.40。另一个 wheel 的每个地址都会不同。

摘要

本页描述整个重建是如何完成的:二进制来自哪里、用什么工具读取、分析产出了什么、本书其余部分中的每个主张在写下之前如何交叉核对、哪些内容无法恢复以及原因、读者如何重复这项工作,以及这样做的法律依据。它是 证据与引用约定 的流程对应页,后者定义了其他每一页使用的标注词汇和引用语法。

整本书源自一个在近百万个函数上重复的动作:把单个 shared object 加载进反汇编器,让它恢复代码和数据,把每个函数反编译为 C,并把一切内容——反汇编、反编译函数体、控制流图、类型信息、交叉引用——序列化为机器可读的 sidecar。没有源代码树、没有调试器、没有运行中的 TPU,也没有内部 artifact 进入该流程。这个 wheel 中的 libtpu.so 未被 strip:它的 .symtab 仍然存在,因此本书中类似 C++ 的标识符是从二进制自身符号表读出的 demangled symbols,而不是重构结果。正是这个事实让一个 745 MB 对象变得可处理:每个函数都预先带名,分析者的工作是恢复行为,而不是名称

该方法在设计上是对抗性的。符号名是假设,不是事实;名为 ValidateLength 的函数,在其反编译函数体显示长度比较之前,都被视为尚未验证。每个标题级主张都会对照反编译 C 或原始字节重新检查,只有当多个独立指标——函数体、调用者、引用字符串、dispatch-table 位置——一致时,结论才会被信任。下面的 pipeline 是机制;交叉验证纪律把它的输出从转录稿变成参考资料。

为复现此方法论,契约如下:

  • 获取路径——精确的 wheel、它包含什么,以及为什么再分发使分析合法。
  • 工具及其 pass——IDA Pro 9.x auto-analysis、Hex-Rays decompiler 和 FLIRT library-signature matching,以及其中哪些实际作用于此二进制。
  • Sidecar 家族——保存恢复事实的 JSON artifacts,带已验证计数,让重新实现者知道本书背后有什么证据。
  • 交叉验证规则——一个主张被信任前需要多个独立指标。
  • 已公布下限——decompilation failures、analysis problems 和 firmware walls,它们限定了任何页面可以断言的内容。
源 artifactlibtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64.whl(PyPI,可自由再分发)
分析的二进制libtpu/libtpu.so - 781,691,048 字节,ELF64 DYN,x86-64
Build-id89edbbe81c5b328a958fe628a9f2207d(NT_GNU_BUILD_ID,md5/uuid 形式)
Stripped? - .symtab 存在;大多数函数带 demangled C++ 名称
工具IDA Pro 9.x - auto-analysis + Hex-Rays decompiler + FLIRT
恢复的函数884,832(据二进制元数据)
恢复的字符串1,249,324
Switch tables33,016
逐函数 artifact 文件context/decompiled/disasm/graphs/ 中各 884,843 个
已公布限制516 decompilation failures · 7,915 analysis problems

Pipeline 概览

五个阶段把 wheel 变成 wiki 页面。每个阶段的输出都是下一阶段的输入;纪律体现在第四阶段。

阶段输入动作输出
获取PyPI wheel 名称下载、解压、定位 libtpu.so、记录 build-id + hashes一个锚定的 781,691,048 字节 ELF
分析ELFIDA 9.x auto-analysis:代码/数据恢复、函数边界、xrefs、类型传播一个含 884,832 个函数的 IDB
提取IDBHex-Rays 反编译 + 将每个函数和表序列化为 JSON sidecars 和逐函数文件sidecar 家族 + 884,843x4 个逐函数 artifacts
交叉验证Sidecars + 函数体对每个主张,要求反编译函数体或原始字节支持它;要求多个指标一致一个带地址锚点的主张
写作已验证主张综合为重新实现级页面;标记缺口一个 wiki 页面

注意 - 前三个阶段是机械的,并且在相同 wheel 和相同 IDA 版本下逐字节复现。最后两个阶段是分析判断。


获取

被分析的对象不是泄露或提取的内部构建。它是已发布 Python wheel libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64.whl 内的 libtpu.so,也就是 pip install libtpu 获取来提供 TPU PJRT plugin 的同一 artifact。wheel 是普通 ZIP archive;解压后得到一个 libtpu/ 包目录,其 payload 就是本书描述的 shared object。

wheel 目录恰好包含六个 payload 文件,全部属于分析表面:

文件大小作用
libtpu.so781,691,048 BPJRT plugin - 主要分析目标,本书主题
sdk.so22,541,240 B伴随 shared object,并行分析(sibling extraction)
__init__.py1,131 B定位并加载 plugin 的 Python entry shim
LICENSE229 Bwheel 的 license file
THIRD_PARTY_NOTICES.txt731,537 B捆绑的 third-party attributions - 确认静态链接的开源组件
SDK_THIRD_PARTY_NOTICES.txt103,306 Bsdk.so 的同类文件

被分析文件的身份以三种方式锚定,因此任何读者都能确认自己持有相同字节:大小(781,691,048)、从 NT_GNU_BUILD_ID note 读取的 GNU build-id 89edbbe81c5b328a958fe628a9f2207d,以及文件的 SHA-256。readelf -h 报告它是面向 Advanced Micro Devices X86-64 的 ELF64 DYN(shared object),readelf -S 显示活跃 .symtab——这是未 strip 断言的基础。

易错点 - build-id 采用内核的 md5/uuid 形式,而不是更常见的 SHA-1 形式;它是 16 字节 digest,file 将其报告为 BuildID[md5/uuid]。不要在这里期待 40 个十六进制字符的 SHA-1 build-id。上面的 32 个十六进制字符值是正确的。

注意 - libtpu.so 保留完整 .symtab,并由 file 报告为 not stripped。因此本书中的每个具名函数都是直接从二进制读取的 demangled symbol,不是重构结果;这也是为什么恢复名称从来不是瓶颈,恢复行为才是。


工具与提取

分析工具

一个工具产生了每个原始事实:IDA Pro 9.x,以三种能力运行。

  • Auto-analysis 遍历 ELF,恢复 section/segment 布局,识别函数边界,跟随控制流,构建全局交叉引用数据库,并在恢复的代码中传播类型。在此二进制上,它解析出 884,832 个函数和 33,016 个 switch/jump tables。
  • Hex-Rays decompiler 把每个函数从 x86-64 提升为类 C 的伪代码函数体。这些函数体——而不是原始反汇编——是行为主张的主要证据,因为 switch 或 guard compare 在 C 中可读,而在一屏 mov/cmp/jne 中并不直观。
  • FLIRT(Fast Library Identification and Recognition Technology)匹配已知 library routines 的字节模式签名,因此静态链接的 memcpy 或 libstdc++ helper 会被标记为对应名称,而不是从头重新分析。

注意 - FLIRT 在此二进制的主要提取 pass 上贡献了个识别匹配(元数据记录 flirt_matches: 0)。这是预期结果,不是缺陷:该二进制已经因幸存的 .symtab 拥有丰富符号,library routines 到来时已经预先命名,FLIRT 没有剩余可添加内容。

Sidecar 家族

Auto-analysis 和反编译不是交付物;它们序列化后的输出才是。每个函数和结构化表都会导出到一组 JSON sidecars,外加四类逐函数 artifact 文件(反编译 C、原始反汇编、context bundle 和控制流图)。本书针对这些 sidecars 写成,而不是针对实时 IDA 会话,因此每一页都可从磁盘文件复现。

Sidecar 家族如下,并列出已验证的存在性及每项携带的计数或大小:

Sidecar保存内容已验证范围
functions每个恢复函数一条记录(地址、大小、名称)884,832 条记录
namesname/symbol table 表面~847 MB
strings每个恢复的 string literal1,249,324 个字符串
segmentsELF segment / section 布局55 个 segments
data_tables恢复的静态数据表~114 MB
switchesJump/switch dispatch tables33,016 张表
rttiC++ RTTI:type-info、vtables、class hierarchy~65 MB
fixupsRelocations / address fixups~120 MB
xrefs全局交叉引用图(code + data edges)~41 GB
enums恢复的 enumeration types存在(小)
structures恢复的 struct/class layouts存在(~290 KB)
frames逐函数 stack-frame layouts~745 MB
entries导出 / entry-point symbols存在
importsImported / external symbols存在
prototypes外部提供的 prototypes(此处为空)[]
metadata提取 manifest(计数、hashes、mode)存在
problemsIDA 标记的 analysis problems7,915 条记录
per-functiondecompiled/*.cdisasm/*context/*graphs/*各 884,843 个文件

注意 - 逐函数 artifact 计数(884,843)略高于元数据中的 function-record 计数(884,832)。目录计数包含少量 thunk/alias/data-stub entries,它们获得 artifact 文件,但不计为完整函数记录。当页面引用函数计数时,引用 884,832;当引用 artifact 覆盖率时,逐函数目录为每个已分析 entry 保存一个文件。

注意 - 主要 pass 的提取 manifest 记录 decompiled: 0ctree: 0。这是两阶段提取的 artifact,并不表示没有任何内容被反编译。第一 pass 以 fast mode 运行(边界、名称、表、xrefs),并有意推迟 Hex-Rays;反编译函数体和控制流树由后续拆分 pass 产生,得到 884,843 个逐函数 decompiled/*.c 文件,以及其中 884,332 个的 control-flow-tree 覆盖(511 个函数缺口)。只检查 manifest 的 decompiled 计数的读者会得出错误结论:函数体确实存在于磁盘上。


交叉验证纪律

带符号的二进制很有诱惑力:像 xla::tpu::sparse_core::lowering_util::GetPadValue 这样的名称读起来像文档。它不是。名称记录的是原作者如何命名该函数,这是很强的线索,但不是已验证行为;demangled C++ 名称也经常在证明它们的代码变化后继续存在。因此,本书中的每个主张都会对照比名称更直接一层的证据重新检查。

最强证据是反编译函数体(或二进制中逐字节精确的表)确实包含所声称的构造——switch、guard compare、常量、vtable slot。没有单行直接说明角色时,结论依赖多个一致的独立指标:函数的调用者、它引用的字符串、它在 dispatch table 中的位置、它的 frame 形状。单个未经佐证的指标——一个有暗示性的字符串、一个 xref、一个暗示未见行为的名称——只记录为线索,而不是基础。

交叉检查有意使用不同 sidecars,以确保指标真正独立:

  • 函数体 vs. 名称——读取 decompiled/*.c 文件;确认或搁置名称。
  • 调用者 vs. 角色——xrefs 图显示谁调用函数以及如何调用;没有 length-shaped caller 的 "validator" 可疑。
  • 字符串 vs. 行为——引用 "request too large" 的函数佐证 rejection path;stringsdata_tables sidecars 提供这些信息。
  • Dispatch 位置 vs. 用途——switchesrtti sidecars 把函数放入表或 vtable,从而约束它可能是什么。
  • 原始字节 vs. decompiler——当 Hex-Rays 不确定时(它会用自身 warning 标记),disasm/* 文件和原始字节是裁判。

如果某个值是从结构推断而不是直接从二进制读取,> **注意 -**> **易错点 -** 标注会精确标记哪一步是推断。这是本书的核心诚实机制:读者总能看出一个陈述有多直接地被支持。

怪癖 - 最重的交叉验证负担落在有符号的函数上,而不是最少符号的函数上。例如 600 字符的 demangled C++ 模板名(如 pxc::mnemonics::ProtoToEnvMiscGenerated... 家族)具体到让人感觉权威,但这些模板密集函数恰恰是 IDA 分析会绊倒的地方(见下文)。最丰富的名称和最弱的分析经常同时出现;这套纪律正是为了捕捉这个陷阱。


限制:无法恢复的内容

每一页的可信度都依赖这些限制被公布,而不是被隐藏。对单个二进制做静态分析存在硬下限,而该下限有三个不同原因。

Decompilation failures

Hex-Rays 对 516 个函数没有返回 cfunc:decompiler 运行了、拒绝了,该函数只剩反汇编。这些并非随机分布。它们聚集在:

  • 模板爆炸代码——深度嵌套的 C++ templates(mnemonics 层中的 variant-of-strong-int dispatch、一次调用中注册数百个 MLIR ops 的 addOperations<...>),其恢复控制流超过 decompiler 能提升的范围。
  • Imported PLT/GOT stubs——如 strlengetenv__tls_get_addrMallocExtension_Internal_* 之类的 entries 是 import thunks,不是本地代码,没有可反编译函数体。
  • 手写汇编——来自静态链接 libraries 的 cryptographic 和 math routines(bn_sqr8x_montbn_power5_nohwmd5_sha1_final),它们是汇编,没有可恢复的 C。

对于这 516 个函数,本书依赖反汇编、周围 xrefs 和名称;触及其中任一函数的页面都会说明。

Analysis problems

IDA 的 auto-analysis 在反汇编 pass 中标记了 7,915 个问题,记录于 problems sidecar。它们分为几类:disasm_problem(disassembler 无法确信解码的字节序列)、bad_stack(不平衡或无法恢复的 stack frame)、head_problem(指令边界歧义)和 final(持续到分析结束的问题)。与 decompilation failures 一样,它们集中在模板密集的 compiler code(primitive_util::*TypeSwitchAlgebraicSimplifierVisitor)以及静态链接 third-party assemblers(X86AsmParserAArch64AsmParserPPCAsmParser::matchAndEmitInstruction)中。带 bad_stack 标记的函数拥有不可信的 local-variable recovery,任何触及它的页面都会说明。

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

除逐函数失败外,有三类内容原则上对这个文件的静态分析不可见:

  • Firmware blobs——发往 TPU 硬件的 payloads 是嵌入数据,不是 x86 代码。反汇编器看到的是字节,不是指令;这些是墙,会被记录为 opaque regions,而不是猜测。
  • Runtime-only values——任何在加载或运行时计算的内容(dlopen 后解析的 relocations、环境驱动配置、经 ICI/network 到达的值)都没有静态表示。本书记录消费这类值的代码路径,从不记录该值。
  • 伴随的 sdk.so——它在 sibling pass 中分析(94,732 个函数,其中 94,292 个被反编译),是一个更小且大体独立的对象;本书的主要主题是 libtpu.sosdk.so 证据只在二者有可证明交互时引用。

注意 - 这些限制相对于整体是有界且很小的:884,832 个函数中 516 个 decompilation failures 远低于 0.1%,7,915 个 analysis problems 分布在一个含数百万条指令的二进制中。公布下限不是因为它很大,而是因为隐藏下限的逆向工程参考资料会让其上每一页都不可信。


复现

机械阶段可以精确复现;分析阶段复现到相同证据,由判断推动结论。想重新推导任何主张的读者可遵循此路径:

bash
# 1. Acquire the identical artifact from PyPI.
pip download libtpu==0.0.40 --no-deps --python-version 3.14 \
    --only-binary=:all: --platform manylinux_2_31_x86_64

# 2. A wheel is a ZIP. Unpack and locate the plugin.
unzip libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64.whl -d libtpu_wheel
ls libtpu_wheel/libtpu/libtpu.so

# 3. Confirm you hold the same bytes (size + build-id must match the pin).
stat -c%s libtpu_wheel/libtpu/libtpu.so      # -> 781691048
readelf -n libtpu_wheel/libtpu/libtpu.so | grep -i 'build id'
                                             # -> 89edbbe81c5b328a958fe628a9f2207d
```text

此后,分析流程为:把 `libtpu.so` 加载进 IDA Pro 9.x,运行完整 auto-analysis,并反编译。本书引用的逐函数证据是给定虚拟地址处的反编译 C 函数体;在 Hex-Rays 中打开该地址(或读取对应的 `decompiled/<addr>.c` artifact),即可逐行验证主张。每个引用中的地址锚点是该重建的入口点。

> **易错点 -** 复现保真度依赖 IDA *版本*。Auto-analysis heuristics、function-boundary recovery 和 Hex-Rays output 都会在 IDA 大版本之间变化,因此不同版本可能恢复出略有不同的函数计数,或反编译出本书列为 failure 的函数体(反之亦然)。请固定 IDA 9.x,以匹配此处引用的函数和 switch-table 计数。二进制字节不变;对它们的分析会变。

---

## 法律依据

这项工作是出于互操作性和研究目的,对合法获得、公开分发的软件进行逆向工程。二进制根据可自由再分发的条款从 PyPI 下载;没有绕过任何访问控制,也没有在任何时候使用或咨询受版权保护的源代码、内部文档或其他受限材料。每项发现都仅来自对任何人都可下载的已编译二进制的分析。

该活动建立在大西洋两岸成熟的法律之上:

| 法源 | 管辖区 | 确立内容 |
|---|---|---|
| **DMCA §1201(f)** | US | 为互操作性进行逆向工程是反规避规则的法定豁免。 |
| **Sega v. Accolade** (9th Cir. 1992) | US | 当反汇编是访问不可保护功能元素的唯一手段时,它属于 fair use。 |
| **Sony v. Connectix** (9th Cir. 2000) | US | 为构建可互操作产品而在逆向工程中的中间复制属于 fair use。 |
| **EU Directive 2009/24/EC, Art. 6** | EU | 为实现互操作性,在规定边界内,无需权利人授权即可反编译。 |

本书记录的是*二进制做什么*——其功能行为、数据布局和决策逻辑——这些是不受保护的思想和操作方法,而不是任何源文本的受保护表达。贯穿始终的来源纪律(二进制静态分析且仅静态分析)正是使工作保持在这些边界内的原因:没有复制受保护源码,因为从未持有过源码。

---

## 交叉引用

- [证据与引用约定](front/evidence-conventions.md) - 本页流程产生的标注词汇和引用语法;阅读任何其他页面前先读它。
- [Forensics Overview](forensics/overview.md) - 二进制本身的结构起点:sections、大小和直接对照字节确认的标题级计数。
- [ELF Anatomy](forensics/elf-anatomy.md) - `segments` sidecar 记录的 segment/section 布局,在 `readelf` 层面读取。
- [Dispatch-Table Taxonomy](forensics/dispatch-table-taxonomy.md) - 如何读取 33,016 张 switch tables 以及 RTTI/vtable 图,即行为页面的结构化证据骨架。