Random Tech Thoughts

The title above is not random

Hexdump Format String 的说明

对于简单的结构化数据的二进制文件,可以通过 hexdump 指定 dump 的格式来方便的查看。但它的 man page 写的非常难懂,我把自己的理解做些记录。

假定某个二进制文件的每个元素对应于下列 C struct:

1
2
3
4
5
6
struct foo {
    int8_t  i8;
    int16_t i16;
    int32_t i32;
    int32_t k32;
} __attribute__((packed));

用下面的 Ruby 脚本可以生成一个测试的二进制文件:

1
2
3
4
5
6
7
a = [8, 16, 32, 32]
b = a.map { |e| -e }

File.open("bindata", "w") do |f|
  f.write(a.pack("csll"))
  f.write(b.pack("csll"))
end

在 dump 该二进制文件时希望把每个元素的内容单独作为一行打印出来。format string 可以告知 hexdump 如何处理二进制的字节。我先给出 hexdump 的调用方式,然后具体解释其中 format string 的含义:

hexdump -e '/1 "%i " /2 "%i " 2/4 "%i " "\n"' bindata

-e 后面跟的就是 format string。每个 format string 可以包含多个 format unit,每个 format unit 可以包含三个部分,其形式如下(方括号表示可选,format 一定要用双引号引起来):

[ [iteration_count]/byte_count ] "format"

例如 2/4 "%i ",其中 2/4 表示循环 2 次,每次处理 4 个字节,"%i " 表示以十进制数打印,format 支持 printf conversion string,不过不支持大小修饰符。几个注意点:

  • 转换字符处理的字节数由 byte count 决定(默认为 4),如果指定了 byte count,则 format 中只能包含一个 conversion string
  • conversion string 支持的 byte count 是有限制的,例如 %i 只支持 1, 2, 4 byte count,不能使用 /3 "%i"(Debian 6 上的 hexdump 不支持 64 bit 的整数,byte count 不能为 8,但是 Minix 的 hexdump 是支持的
  • 上面例子中 format string 最后的 "\n" 是一个只包含 format 的 format unit

如果想在打印十进制的同时以十六进制输出,可以通过多次指定 format string 的方式实现,如下:

hexdump -e '/1 "%2i " /2 "%3i " 2/4 "%3i " " | " ' \
    -e '/1 "%02x " /2 "%04x " 2/4 "%08x " "\n"' bindata
# output
#  8  16  32  32 | 08 0010 00000020 00000020
# -8 -16 -32 -32 | f8 fff0 ffffffe0 ffffffe0

指定多个 format string 时,hexdump 每次处理一个 block 的数据,block 的大小为 format string 中需要字节数最大的那个。(format string 处理数据大小不同时的行为 man page 描述难以理解,现在没有需求就不去弄清楚了。)

hexdump 有一些特殊的 convertion string,例如 %_a[dox] 可以获取当前的 offset:

hexdump -e '"offset %2_ad: " /1 "%i " /2 "%i " 2/4 "%i " "\n"' bindata

理解 format string 以后再看 man page 查找其他功能就会方便很多了。

关于 format string 设计的一点想法

hexdump 的 format string 其实也是一个 mini language,不过觉得它设计得怪怪的,把指定二进制文件格式和指定如何打印混在了一起。

指定二进制文件的格式用 Ruby Array.pack 中的 template string 我觉得更加方便直观,然后可以另外的 print string 来指定如何打印。

例如上面的例子,直接用创建二进制文件时的 template string "csll" 作为 format string, print string 为 "%c %hd %d %d\n"。这样的 mini language 使用起来就方便直观很多。有空的时候自己实现一个看看。

Comments