FlatBuffers 注释二进制(Annotated Binary)与无效二进制测试全解析
2026/9/20 10:22:48 网站建设 项目流程

FlatBuffers 注释二进制(Annotated Binary)与无效二进制测试全解析

【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers

本篇技术指南围绕 FlatBuffers 仓库中tests/annotated_binary/tests/README.md所记录的无效二进制测试集合展开,系统讲解flatc --annotate如何把二进制 FlatBuffer 逐字节注释为可读文本,并深入分析 22 个手工构造的损坏二进制用例——它们分别篡改根表偏移、vtable 尺寸、字段偏移、字符串/向量长度、union 类型值或直接截断文件,以验证注释工具能够精准标记出这些损坏点。读完本文,你将掌握 Annotated Binary 文本格式的五列结构、偏移量(UOffset32/SOffset32/VOffset16)的解读方法,以及如何利用这些测试用例排查二进制数据损坏。

一、Annotated Binary 是什么

Annotated Flatbuffer Binary(.afb)是 FlatBuffers 提供的一种二进制逐字节注释能力:flatc读取一个 schema(.fbs文本 schema 或.bfbs二进制 schema)与按该 schema 序列化出的二进制文件,输出包含全部二进制数据与逐行注释的.afb文本文件。仓库中的官方说明见 docs/source/annotation.md,其核心实现位于 src/annotated_binary_text_gen.cpp 与 src/binary_annotator.h。

注释不仅服务于正常数据的可读化,更重要的用途是诊断损坏的二进制:当一个偏移或长度字段被改成一个非法值(指向缓冲区之外、小于最小合法尺寸等)时,注释输出会在对应行上标记ERROR: ... Invalid offset, points outside the binary.之类的诊断信息,这正是tests/annotated_binary/tests/目录下整套无效二进制测试的验证目标。

1.1 基本用法

flatc --annotate {schema_file} -- {binary_file}...

schema可以是纯文本.fbs,也可以是已经编译好的.bfbsbinary是依据该 schema 生成的 FlatBuffer 二进制。命令会为每个输入二进制生成一个.afb文件。

仓库自带的示例(tests/annotated_binary/README.md):

cd tests/annotated_binary ../../flatc --annotate annotated_binary.fbs -- annotated_binary.bin

该命令在当前目录生成annotated_binary.afb。其中annotated_binary.binannotated_binary.json中的数据经如下命令序列化而来:

../../flatc -b annotated_binary.fbs annotated_binary.json

与之配套的 schema 位于 tests/annotated_binary/annotated_binary.fbs,它定义了一个非常全面的演示模型:包含标量(intlongboolfloatdouble)、枚举(Food)、多个结构体(ToleranceDimensionBuildingLocation)、多个表(BazBarFoo)、三种 union(BarBazMeasurementAny)、标量向量、字符串向量、表向量、结构体向量、union 向量、可空标量(maybe_i32: int32 = null)、默认值字段以及file_identifier "ANNO"root_type Foo。这份 schema 几乎覆盖了 FlatBuffers 的所有数据形态,使注释输出能展示每种形态的二进制布局。

flatc还支持一个相关选项--annotate-sparse-vectors,用于不逐元素注释向量内容(见 src/flatc.cpp 的选项注册,对应解析在 src/flatc.cpp),在处理超长向量时可显著缩减输出。

二、.afb 文本格式详解

annotated_binary.afb是文本格式的权威示例。整个文件按偏移量从小到大排列,每一行的第一列即该区域相对缓冲区起点的十六进制偏移(如+0x003C)。以 tests/annotated_binary/annotated_binary.afb 中实际的一行为例:

vtable (AnnotatedBinary.Bar): +0x00B8 | 08 00 | uint16_t | 0x0008 (8) | size of this vtable

共五列:

  1. 偏移+0x00B8,该区域距缓冲区起点的十六进制字节偏移。
  2. 原始字节08 00,按 FlatBuffers 线格式使用的小端序十六进制字节。
  3. 类型:解释这些字节的类型。既有 FlatBuffers 内部专用类型(如UOffset32SOffset32VOffset16UType8),也有 C++ 风格定宽标量(如uint16_tuint32_tdoublechar[4])。
  4. :将原始字节按紧凑的大端序重新呈现(如0x0008),便于阅读,随后是十进制值(如(8));字符串区域则直接显示原始字符串内容。当类型为绝对偏移量(SOffset32UOffset32)时,还会追加一个Loc: +0xXXXX,指明该偏移实际指向的缓冲区位置。
  5. 注释:关于该值的文本说明,尽量携带已知元数据,例如字段名、字段 id、所属类型、默认值提示等。

2.1 Binary Section 与 Binary Region

.afb 文件在结构上分为两层:

  • Binary Section(二进制节):逻辑上聚合成组的连续字节区域,例如一个Table实例或它的vtable。节可以附带 schema 中定义的关联类型名,如vtable (AnnotatedBinary.Bar):root_table (AnnotatedBinary.Foo):string (AnnotatedBinary.Foo.charlie):vector (AnnotatedBinary.Foo.names):union (AnnotatedBinary.Tolerance.measurement):padding:header:等。节类型在 src/annotated_binary_text_gen.cpp 中通过BinarySectionType枚举映射为headertableroot_tablevtablestructstringvectorvector64unionpaddingunknown等标签。
  • Binary Region(二进制区域):组成某个值(标量或标量数组)的连续字节段;若区域过大,可跨多行输出。

2.2 偏移量的解读

注释中出现的三类核心偏移(对应实现见 src/annotated_binary_text_gen.cpp 的IsOffset判断):

类型宽度含义定位方式
UOffset324 字节无符号偏移,通常指向缓冲区中更靠前的位置字段所在位置减去该值
SOffset324 字节有符号偏移,可为负(例如表内指向 vtable 的偏移)当前地址加上(带符号)该值
VOffset162 字节vtable 内的字段偏移相对所属 table 起始位置

例如在 annotated_binary.afb 中,根表第一条目:

root_table (AnnotatedBinary.Foo): +0x0044 | 3A 00 00 00 | SOffset32 | 0x0000003A (58) Loc: 0x000A | offset to vtable

0x0044 + 0x3A = 0x007E,但注释显示Loc: 0x000A,说明SOffset32的定位规则是按符号偏移回退(0x0044 - 0x3A = 0x000A),与 vtable 实际所在位置一致。而字符串引用则相反:

+0x0074 | C8 01 00 00 | UOffset32 | 0x000001C8 (456) Loc: 0x023C | offset to field `name` (string)

0x023C - 0x1C8 = 0x74,即UOffset32从自身字段位置向前回溯定位目标。两种偏移的精确计算公式在 tests/annotated_binary/README.md 中说明为"取决于上下文"——这正是注释工具依据二进制区域类型分别计算Loc的原因。

三、无效二进制测试:验证损坏可被识别

3.1 测试思路

tests/annotated_binary/tests/README.md的核心内容是:基于annotated_binary.bin手工构造一系列损坏二进制,每个文件只修改某个偏移或长度/尺寸条目,使其指向非法位置(超出缓冲区、小于最小合法尺寸、截断在字段中间等),然后用flatc --annotate生成对应的.afb,通过检查注释输出中的ERROR/WARN标记证明这些损坏可以被注释工具发现

测试文件分三组存放于 tests/annotated_binary/tests/:

  • 22 个invalid_*.bin:手工损坏的二进制输入;
  • 22 个invalid_*.afb:对每个损坏二进制运行注释工具后的输出,其中包含错误标记。

3.2 运行命令

原文档给出的运行方式(从仓库根目录出发):

cd tests/annotated_binary ../../flatc --annotate annotated_binary.fbs tests/invalid_root_offset.bin ...

即用annotated_binary.fbs作为 schema,一次性对多个tests/目录下的损坏二进制执行注释。批量执行时可借助仓库自带的自动化脚本 tests/annotated_binary/generate_annotations.py:脚本定位仓库根目录与flatc可执行文件(Windows 下为flatc.exe),遍历test_files列表中的 22 个tests/invalid_*.bin以及正常文件annotated_binary.bin,逐一调用flatc --annotate生成.afb。该脚本是整套测试可重复运行的关键:任何对 schema 或注释逻辑的改动,都可以通过重跑脚本快速重建全部.afb基准文件。

3.3 22 个损坏用例逐项解析

下表完整列出原文档记录的每一个损坏二进制及其损坏手法:

二进制文件损坏方式
invalid_root_offset.bin将前两个字节从4400改为FFFF,产生一个大于二进制文件长度的根表偏移
invalid_root_table_vtable_offset.bin0x0044处两个字节从3A00改为FFFF,指向二进制文件之外
invalid_root_table_too_short.bin将文件截断到0x46字节,切入了根表的 vtable 偏移字段
invalid_vtable_size.bin0x000A处两个字节从3A00改为FFFF,vtable 尺寸大于文件长度
invalid_vtable_size_short.bin0x000A处两个字节从3A00改为0100,vtable 尺寸小于最小合法值 4 字节
invalid_vtable_ref_table_size.bin0x000C处两个字节从6800改为FFFF,所引用表尺寸大于文件长度
invalid_vtable_ref_table_size_short.bin0x000C处两个字节从6800改为0100,所引用表尺寸小于最小合法值 4 字节
invalid_vtable_field_offset.bin0x0016处两个字节从1000改为FFFF,字段偏移指向文件之外
invalid_table_field_size.bin将文件截断到0x52字节,把一个 Uint32 值截成一半
invalid_table_field_offset.bin将文件截断到0x96字节,把一个 UOffset32 值截成一半;同时把0x90处两个字节从DC00改为FFFF,指向超出文件的部分
invalid_string_length_cut_short.bin将文件截断到0xAD字节,把字符串长度 Uint32 值截成一半
invalid_string_length.bin0x00AC处两个字节从0500改为FFFF,字符串长度大于文件长度
invalid_vector_length_cut_short.bin将文件截断到0x0136字节,把向量长度 Uint32 值截成一半
invalid_struct_field_cut_short.bin将文件截断到0x5D字节,把结构体字段值截成一半
invalid_struct_array_field_cut_short.bin将文件截断到0x6A字节,把结构体数组字段值截成一半
invalid_vector_structs_cut_short.bin将文件截断到0x0154字节,切入结构体向量内部
invalid_vector_tables_cut_short.bin将文件截断到0x01DE字节,切入表偏移向量内部
invalid_vector_strings_cut_short.bin将文件截断到0x0176字节,切入字符串偏移向量内部
invalid_vector_scalars_cut_short.bin将文件截断到0x01C1字节,切入标量向量内部
invalid_vector_unions_cut_short.bin将文件截断到0x01DE字节,切入 union 偏移向量内部
invalid_union_type_value.bin0x004D处一个字节从02改为FF,union 类型值超出枚举范围
invalid_vector_union_type_value.bin0x0131处一个字节从02改为FF,向量 union 类型值超出枚举范围

其中截断类用例正是通过原文档给出的命令生成的:

truncate annotated_binary.bin --size=70 >> invalid_root_table_too_short.bin

可见这些损坏主要分为四类:

  1. 偏移越界:根偏移、vtable 偏移、字段偏移被改成远超文件长度的值;
  2. 尺寸字段异常:vtable 尺寸、所引用表尺寸被改成大于文件或小于最小合法值(FlatBuffers 规定 vtable 头部两个uint16_t至少占用 4 字节);
  3. 文件截断:把文件切断在标量、字符串长度、向量长度、结构体/表/字符串/标量/union 向量内部,模拟传输或存储中被截断的数据;
  4. union 类型值非法:把UType8类型的 union 判别值改成超出枚举定义的值。

3.4 注释输出中的错误与警告

invalid_root_table_vtable_offset.afb为例(tests/annotated_binary/tests/invalid_root_table_vtable_offset.afb),修改根表 vtable 偏移后,注释输出首先把原本可识别的 vtable 节降级为:

unknown (no known references): +0x0008 | 00 00 3A 00 68 00 0C 00 | ?uint8_t[60] | ..:.h... | WARN: nothing refers to this section.

随后根表条目被标记为错误:

root_table (AnnotatedBinary.Foo): +0x0044 | FF FF 00 00 | SOffset32 | 0x0000FFFF (65535) Loc: 0xFFFFFFFFFFFF0045 | ERROR: offset to vtable. Invalid offset, points outside the binary.

再看invalid_vtable_field_offset.afb(tests/annotated_binary/tests/invalid_vtable_field_offset.afb),vtable 中的单个字段偏移被标记:

+0x0016 | FF FF | VOffset16 | 0xFFFF (65535) | ERROR: offset to field `bar` (id: 4). Invalid offset, points outside the binary.

注释工具的输出规则可以总结为:

  • ERROR标记:出现在被篡改的偏移/长度字段所在行,明确指出"Invalid offset, points outside the binary"(偏移越界)或尺寸不合法等诊断信息;
  • WARN: nothing refers to this section.:出现在因引用被破坏而"失联"的节上,节类型退化为unknown (no known references),字节类型显示为?uint8_t[N]
  • 截断类损坏:表现为某个标量、长度或偏移值只剩一半字节,注释会按照不完整的数据给出其解读,从而暴露数据不完整的事实。

这种"错误精确到字节、警告定位到节"的呈现方式,让开发者能一眼看出损坏发生在哪个偏移、影响了哪个字段,是排查序列化数据损坏的利器。

四、从源码理解注释与校验逻辑

注释工具的实现分为两层:

  1. 遍历与分析:src/binary_annotator.h 负责按 schema 递归遍历缓冲区,依据UOffset32/SOffset32/VOffset16追踪 vtable、table、struct、string、vector、union 等所有区域,并维护区域之间的引用关系;
  2. 文本渲染:src/annotated_binary_text_gen.cpp 将分析结果渲染为五列文本。其OutputConfig结构(见 src/annotated_binary_text_gen.cpp)控制输出细节:max_bytes_per_line = 8(每行最多 8 字节)、offset_max_char = 4、分隔符|include_vector_contents(与--annotate-sparse-vectors联动)等。

在 src/flatc.cpp 中,--annotate选项(src/flatc.cpp)接收 schema 文件名存入options.annotate_schema,之后加载并解析 schema(src/flatc.cpp),最终对每个输入二进制调用注释生成。文件头部的"Schema file / Binary file"注释行与逐节输出均由该流程产生。

值得注意的一点:原文档中注释示例出现过SOffet32/Offset32的拼写,实际实现中使用的类型枚举为UOffsetSOffsetUOffset64(见 src/annotated_binary_text_gen.cpp),本文统一采用后者。

五、如何自行复现与扩展验证

如果你想在本仓库中复现这套测试:

  1. 构建flatc:参考 docs/source/building.md 或直接使用仓库预编译产物;
  2. 生成全部.afb:确保flatc位于仓库根目录(脚本从根目录定位flatc),然后运行:
python3 tests/annotated_binary/generate_annotations.py

脚本会重新生成annotated_binary.afb以及tests/下全部 22 个invalid_*.afb(列表见 tests/annotated_binary/generate_annotations.py)。

  1. 手动验证单个用例:例如检查根偏移损坏:
cd tests/annotated_binary ../../flatc --annotate annotated_binary.fbs -- tests/invalid_root_offset.bin

然后打开生成的tests/invalid_root_offset.afb,观察头部的offset to root table行是否带有ERROR标记。

  1. 构造你自己的损坏用例:参照invalid_root_table_too_short.bin的做法,先用flatc -b从 JSON 生成二进制,再用truncate截断或直接编辑字节(修改偏移/长度字段),最后运行--annotate观察诊断输出——这既是学习 FlatBuffers 线格式布局的最好方式,也是为tests/目录贡献新测试用例的标准流程。

六、小结

tests/annotated_binary/tests/目录通过 22 个精心构造的损坏二进制,系统验证了 FlatBuffers 注释二进制工具的诊断能力:无论是根偏移、vtable 尺寸、字段偏移越界,还是各类字段/向量被截断,抑或 union 类型值非法,flatc --annotate都能在.afb输出的对应字节行上给出明确的ERROR标记,并将失联区域标记为unknown (no known references)附以WARN。这套机制让二进制序列化数据的损坏定位精确到字节级,是 FlatBuffers 调试与数据完整性验证体系中极具实战价值的一环,也是理解 FlatBuffers 底层线格式(偏移、vtable、对齐、向量与 union 布局)最直观的教材。

【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询