LWN:针对二进制格式的一些好用工具!

关注了就能看到更多这么棒的文章哦~

Some useful tools for binary formats

February 28, 2023
This article was contributed by Koen Vervloesem
FOSDEM
AI assisted translation
https://lwn.net/Articles/924133/

Linux 用户经常使用文本文件;grep、awk 和 sed 等工具是他们工具箱中的标准工具。但是,当试图从二进制格式的文件中提取或编辑数据、分析损坏的媒体文件或解析二进制数据格式时,这些工具就显得力不从心了。布鲁塞尔举行的 FOSDEM 2023 有一个专门针对处理二进制数据的开源程序的二进制工具相关的讨论议题。

基于行的文本文件可以用标准工具处理,但是对于以文本形式存储结构化数据的数据格式,如 JSON、YAML 和 XML,有更好的工具。对于 JSON,命令行处理器 jq 已经很受欢迎了。它也是至少两个名为 yq 的工具的灵感来源,这些工具可以处理 YAML、JSON、XML 和其他基于文本的格式:一个由 Mike Farah 开发,另一个由 Andrey Kislyuk 开发。

fq, or jq for binary formats

在 FOSDEM 上,Mattias Wadman 介绍了他开发的工具 fq,他称之为“二进制格式的 jq”。他开发这个工具是因为他在工作中经常处理媒体文件,想要有一个命令行工具来帮助调试损坏的媒体文件,寻找一些不寻常的值或数据结构,或者只是对多个媒体文件进行数据提取任务自动化处理。

Wadman 解释说,他非常喜欢 jq,因为它的语法非常适合 CLI,用来查询、显示和转换 JSON 数据。这种语法简洁而可组合,很容易对数据结构中的元素进行迭代和递归,而且有强大的选择和转换数据的手段。他希望对二进制格式的数据也有同样的可能性,所以他写了 fq 工具。这用 Go 语言编写的,是基于一个 Go 实现的 jq 工具来开发的。两个项目都是 MIT 许可的。

Fq 的语法表现力很强。在最简单的形式中,fq d file 就可以递归地显示解码后数据结构的树形结构,但它会把长 array 截断;使用 fq dd file 则显示所有字节。使用 fq . file 只显示树形结构的上层。也可以选择满足某些条件的值,用来提取文件数据结构中特定部分,或者计算出这些数值的直方图(histogram)。

fq 就可以采用这种方式在命令行上快速完成数据处理任务,它也可以作为 shell 脚本的一部分。此外可以使用 fq -i . file 作为一个交互式的读取-求值-打印循环(REPL)shell 来使用。其中会有自动补全功能,可以轻松地发现文件中的结构,并在结构组成的树中来回浏览。使用 fq 的另一种方式是作为脚本解释器。当经常使用相同的 fq 命令时,可以创建一个像这样的脚本:

#!/usr/bin/env fq -d mp3 -rf
[.frames[].header | .sample_count / .sample_rate] | add

这个例子是通过对 mp3 文件来选择所有 frame 的头部,将每个 header 中记录的 sample 数量除以其采样率,然后将所有结果相加,用来计算 MP3 文件播放的持续时间。同样地,可以处理许多其他格式的文件。该项目还提供了一些文档来介绍如何实现新的格式的解析。

Kaitai Struct

虽然 fq 是一个很有用的命令行工具,但 Kaitai Struct 工具则比它更侧重于以一种声明式(declarative)、语言无关(language-neutral)的方式创建二进制文件结构的解析器。Kaitai Struct 的开发者和维护者 Petr Pučil 谈到了他是如何发现这个项目的。他想要创建一个 MIDI 编辑器,在这个过程中,他就开始编写一个 SoundFont 2(.sf2)文件的解析器。“这很难,一点都没有什么乐趣”,Pučil 认为。但当他发现了 Kaitai Struct,这让他能够在一天内做到之前花了两个月的事情。

那么 Kaitai Struct 是什么呢?从根本上说,它是一种用于描述任意二进制数据结构的声明式语言。数据格式是在.ksy 文件中描述的,使用了 YAML 格式。这一点尤其重要,因为可以用作格式描述的正式规范定义(formal specification)。截至撰写本文时,Kaitai Struct 的格式库有 181 种规范,包括 ZIP 存档格式、GPT 分区表、可执行文件使用的 ELF 格式(the Executable and Linkable Format)以 AVI (Audio Video Interleave)这种多媒体容器格式。

除此之外,Kaitai Struct 还是一个解析器生成器(parser generator)。Mikhail Yakshin 在 2016 年发布了它的第一份源代码时,Kaitai Struct 就能够将.ksy 文件编译成 Java 和 Ruby 代码用来解析 structure。到 2017 年,该项目已经支持 8 种语言,截止撰写本文时,它能够为 11 种语言创建解析器:C++/STL、C#、Go、Java、JavaScript、Lua、Nim、Perl、PHP、Python 和 Ruby。在他的演讲中,Pučil 宣布计划开发 Rust、C 和 Julia 解析器生成器。

“这真的是一种 '一次编写,到处使用' 的方式”,Pučil 解释说。一旦一个二进制格式在.ksy 文件中描述好了,Kaitai Struct 的编译器就能够为这 11 种编程语言生成解析代码的源代码。编译器 ksc 使用 GPLv3 许可证发布,但生成代码所使用的运行时库使用 MIT 或 Apache-2.0 许可证。因此生成的代码甚至可以由专有(proprietary)应用程序来使用。

Ksc 有一个有趣的特性:它的目标之一不是某一种编程语言,而是 Graphviz 的 dot 语言。生成的.dot 文件可以用 Graphviz 来转换成一个图表(diagram),显示.ksy 文件中定义的数据结构。这是一种可视化二进制数据格式的简单方法,比如说也许可以作为其文档的一部分。

Kaitai Struct 还附带了一些其他实用工具。使用 ksv 命令,可以使用.ksy 文件来解析目标文件,将结构和原始数据并排可视化地展现。用户可以打开和关闭树状结构中的某一部分,从而来交互式地探索这些数据。随 ksv 包附带的另一个命令是 ksdump。这也可以用来对树状结构进行可视化,但非交互式的方式。它仅仅将特定数据的解析结构以 YAML、JSON 或 XML 格式来 dump 出来显示到 terminal 终端上。

在开发.ksy 文件时还有另一个方便的工具就是 Kaitai Web IDE。这是一个 online 编辑器和可视化显示工具,在编辑.ksy 文件时可以显示原始数据和解析对象树。这种即时反馈可以让格式规范的开发速度更加快。Web IDE 使用 GPLv3 许可证,并且也可以在本地运行。

Pučil 以一个最近的进展来结束了他的演讲:Kaitai Struct 团队已经花了 6 个月开发序列化支持。序列化正是解析动作的相反的流程:解析是从二进制数据创建结构(所以它读取二进制数据),序列化是将结构转换为二进制数据(所以它会写入二进制数据)。NLnet Foundation 对序列化工作提供了资金资助。目前,Kaitai Struct 有了一个 Java 序列化生成器可以用了,并且据 Pučil 说,Python 和 C#实现也将在两个月内准备好。

有了序列化之后,Kaitai Struct 可以用来编辑一些现有的二进制格式文件,也能从结构来创建新文件,甚至将文件在不同格式之间转换。然而,当前序列化实现功能还相当少。用户必须要显式地对数据结构中的所有内容进行设置,包括长度、偏移量和 magic number 等。Kaitai Struct 只检查序列化数据的一致性。另一个缺陷是一旦创建了用于序列化的数据流,就无法在之后对齐 size 进行调整。

GNU poke

GNU poke 项目在 FOSDEM 的二进制工具讲座中有两次演讲。José E. Marchesi 介绍了这个工具的基本情况和项目的最新进展。GNU poke 是一个交互式、可扩展的结构化二进制数据编辑器,可以从命令行使用。它与简单的十六进制编辑器不同,GNU poke 不仅可以让用户以原始字节流的方式来编辑数据,还可以以结构化的方式编辑数据。Marchesi 称 GNU poke 对于逆向工程和原型设计特别有用。

当然,poke 程序需要知道用户正在使用的数据格式,这就是为什么这个程序附带了近 50 个“pickle”。pickle 是指用 Poke 语言编写的脚本,可以读写特定的某种二进制格式。一些 pickle 甚至非常复杂,以至于它们在由自己专门的 package 来发布,比如 poke-elf 用来编辑 ELF 对象文件、可执行文件、共享库和 core dump 文件。

用户也可以编写自己的 pickle 来编辑其他二进制格式的文件。Poke 语言是具备静态类型和垃圾回收的语言。一个 pickle 包括了解析和序列化数据格式所需的 type、变量和函数的定义。因此,编写一个自定义 pickle 并将其加载到 GNU poke 中,就可以创建自定义的二进制数据编辑器了。

GNU poke 的基本功能是在一个 C 库 libpoke 中实现的,这样就可以将其功能集成到其他工具中。Marchesi 演示了一个例子,其中 libpoke 被集成到了 GDB 中。“由于 GDB 擅长调试而 GNU poke 擅长处理数据,所以将两者结合起来就得到了一个在两项任务上都非常出色的工具”,Marchesi 总结道。

除了 GNU poke 的主页外,还有其他两个网站可以了解到更多关于这个工具的信息。其中 Pokology 是一个由 poke 用户和开发者维护的社区驱动的网站;而 Applied Pokology 是 Marchesi 关于 GNU poke 的博客。

GNU poke 的贡献者 Mohammad-Reza Nabipoor 介绍了这个工具的另一个用户接口。在最近发布的 GNU poke 3.0 版本中,Nabipoor 贡献了 poke 守护进程 poked 。它链接到了 libpoke,并通过 Unix socket 来将其功能暴露给其他程序。这样,就可以有多个客户端同时与 poked 交互。

Nabipoor 使用 GNU poke 来解析蓝牙格式,在 poked 的基础上构造了 pacme ,他称之为“受 acme 启发的 GNU poke 接口”。他所说的 acme 是 Rob Pike 从贝尔实验室分布式操作系统 Plan 9 中开发出来的文本编辑器。

Pacme 由一堆小型 C 程序组成,被称为 poklet ,它们与 poked 交互,执行 Poke 代码并处理结果,也实现了用户接口中的几个主要组件,如 REPL 、byte dump、树状视图和 Poke 数据结构编辑器。关于用户界面方面,pacme 使用了 tmux 工具,把各种 poklet 都在各自独立窗格(pane,是 tmux 中的名词)中显示。有一些预定义的布局,并且用户也可以打开 tmux pane 并且手动排列,然后在其中打开 poklet 来创建出自定义的界面。

Binary toolkit

总而言之,这些工具在处理各种二进制数据格式时可能会派上用场。对于在命令行上提取和过滤数据,fq 提供了一种易于使用的方式。要编辑二进制数据,GNU poke 是一个强大的工具。要解析或者序列化二进制数据到某一种编程语言(需要是它能支持的语言)中的话,Kaitai Struct 提供了一种灵活的方法。

虽然这三个工具各有其用途,但每个都有自己的格式库,这有点遗憾。这些格式之间有很多重叠,因此存在相当多的重复工作。不过,Kaitai Struct 正在与 Python 数据解析器和构建器项目 Construct 积极合作,并且双方都有一些想法。Kaitai Struct 编译器会将 Construct 作为其目标之一,这就允许将 .ksy 文件转换为使用 Construct 的声明式语言描述相同数据格式的 Python 文件。在二进制数据格式领域中各个项目之间如果能更多地进行这样的合作,将会有更多的成果。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

format,png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值