对于简单的结构化数据的二进制文件,可以通过 hexdump 指定 dump 的格式来方便的查看。但它的 man page 写的非常难懂,我把自己的理解做些记录。
假定某个二进制文件的每个元素对应于下列 C struct:
1 2 3 4 5 6 |
|
用下面的 Ruby 脚本可以生成一个测试的二进制文件:
1 2 3 4 5 6 7 |
|
在 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 使用起来就方便直观很多。有空的时候自己实现一个看看。