理解 Hexdump

本文译自:Making Sense of Hexdump

我经常使用二进制数据,这些数据的格式我可以解释,只要我能以人类可读的形式看到它。大多数文本编辑器用处不大。解决这个问题的一种(许多)方法是使用十六进制转储实用程序。hexdump 非常通用,允许您根据需要查看二进制文件中的结构,一旦您学会了如何使用它,您就可以快速将其应用于许多问题。我认为这很容易算是很酷。

如果您使用过十六进制转储手册页,那么您可能会发现格式语法非常令人生畏。部分格式语法与 C 编程语言中常用的函数几乎相同。我发现十六进制语法很困难,我是一名 C 程序员。事实上,这并不难理解,但是在阅读文档时如果没有示例可以使用,格式选项的应用方式可能并不明显。

内容:

  • 基本用法
  • 仅查看文件的一部分
  • 使用十六进制转储转储分区表的示例
  • 格式字符串简介
  • 获取行输出
  • 转储数据块 (m/n)
  • 拆分结构化数据的各个元素
  • 十进制输出
  • 32位数据
  • 已签名/未签名
  • 向输出添加额外文本
  • 把它放在带有一些布局的 shell 脚本中
  • 这是一个 64 位的世界!
  • 文件中的文本
  • 包含文本和数字数据的文件
  • 带小数部分的数字
  • 我不再知道我在文件中的位置
  • 自定义字段宽度
  • 将一切整合在一起

基本用法

从猜测开始,只需给十六进制一个数据文件的名称,然后获取十六进制转储使用的任何默认格式:

hexdump mydata

0000000 457f 464c 0101 0001 0000 0000 0000 0000
0000010 0002 0003 0001 0000 8430 0804 0034 0000
0000020 22ec 0000 0000 0000 0034 0020 0008 0028
*
0001030 0027 0024 0006 0000 0034 0000 8034 0804

您在那里看到的是 mydata 文件的二进制内容,显示为十六进制的 8 个单独的 16 位值行。每行上的第一个数字是文件中该行上 8 个后续值中第一个值的起始偏移量。* 表示从 0000030 到 0001020 的所有行都与0000020行的值相同,并且对于具有长且对齐的重复序列的大文件,可以使输出保持压缩。请注意,在没有命令行选项的情况下,hexdump 会将整个文件转储到屏幕上,因此请小心使用它来处理大文件。

示例文件的前 16 位是十六进制值 457f。请务必注意,显示的值与英特尔 x86 CPU 上的解释相同。在一些其他类型的 CPU 上,由于字节排序的差异,该值可以输出为 7f45。如果您不熟悉该术语,则可能值得一读,例如在维基百科上。

如果 16 位值不是您要查找的,那么快速查看 hexdump 手册页会发现有几个开关可以选择其他一些预定义的输出格式。只有一个其他选项 -C 可以生成十六进制输出:

# hexdump -C mydata00000000  7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 03 00 01 00 00 00  30 84 04 08 34 00 00 00  |........0...4...|
00000020  ec 22 00 00 00 00 00 00  34 00 20 00 08 00 28 00  |."......4. ...(.|
*
00001030  27 00 24 00 06 00 00 00  34 00 00 00 34 80 04 08  |'.$.....4...4...|

现在,您可以看到单个字节值,甚至可以看到文件中那些部分的文本是普通文本字符。所以,现在我会从“ELF”签名中猜测这个文件可能是一个程序可执行文件。

仅查看文件的一部分

如果你有一个非常大的文件,你只对查看一些数据感兴趣,你可以使用 -n 选项来指定要转储的字节数。以下示例中的 -n 32 导致十六进制转储仅显示文件的 32 个字节。

# hexdump -C**-n 32**mydata
00000000  7f 45 4c 46 01 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 03 00 01 00 00 00  30 84 04 08 34 00 00 00  |........0...4...|
00000020

如果您只对文件内一段距离的数据感兴趣,则可以使用 -s 选项指定从文件开始转储的距离。以下示例中的 -s 16 使 hexdump 显示文件的内容,从文件的开头开始 16 个字节,一直到文件末尾。:

# hexdump -C**-s 16**mydata
00000010  02 00 03 00 01 00 00 00  30 84 04 08 34 00 00 00  |........0...4...|
00000020  ec 22 00 00 00 00 00 00  34 00 20 00 08 00 28 00  |."......4. ...(.|
*
00001030  27 00 24 00 06 00 00 00  34 00 00 00 34 80 04 08  |'.$.....4...4...|

您还可以组合这两个开关,以查看数据子集与文件中的某个偏移量。在此示例中,同时使用 -s 16 和 -n 32 会导致 hexdump 显示文件的 32 个字节,从开头开始 16 个字节。

# hexdump -C**-s 16 -n 32**mydata
00000010  02 00 03 00 01 00 00 00  30 84 04 08 34 00 00 00  |........0...4...|
00000020  ec 22 00 00 00 00 00 00  34 00 20 00 08 00 28 00  |."......4. ...(.|
00000030

您可以使用 -v 选项阻止十六进制转储用 * 替换重复的行。在以下示例中,-v 导致在偏移量 30 十六进制和 40 十六进制处显示以前折叠的重复行:

# hexdump -C -s 16 -n 64**-v**mydata
00000010  02 00 03 00 01 00 00 00  30 84 04 08 34 00 00 00  |........0...4...|
00000020  ec 22 00 00 00 00 00 00  34 00 20 00 08 00 28 00  |."......4. ...(.|
00000030  ec 22 00 00 00 00 00 00  34 00 20 00 08 00 28 00  |."......4. ...(.|
00000040  ec 22 00 00 00 00 00 00  34 00 20 00 08 00 28 00  |."......4. ...(.|
00000050

这涵盖了 hexdump 的基本用法,直到最近它已经足以满足我的所有需求。

使用十六进制转储转储分区表的示例

我想将分区表转储到磁盘上,以便可以通过环回设备挂载它。通过切换 fdisk 以显示扇区大小的单位,很容易从 fdisk 获取必要的信息,但我想我会玩十六进制转储来强迫自己使用格式语法。这就是我们要去的地方:

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 " %3d" 1/1 " %02x" 3/1 " %3d" 2/4 " %9d" "\n"' diskdump.ddimg00   1   1   0 83 254 255 255        63  16771797
00   0   0   0 00   0   0   0         0         0
00   0   0   0 00   0   0   0         0         0
00   0   0   0 00   0   0   0         0         0

分区表由四个 16 字节条目组成,偏移量为 446 字节到磁盘(即在典型磁盘的第一个扇区中)。下面是具有单个分区的磁盘映像的示例:

# hexdump -C -s 446 -n 64 diskdump.ddimg000001be  00 01 01 00 83 fe ff ff  3f 00 00 00 d5 ea ff 00  |........?.......|
000001ce  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001fe

巧合的是,分区表条目长度和十六进制转储的默认行长度为 16 字节,因此每行都是一个完整的分区表条目。

最好让十六进制转储显示整个块,而不是压缩重复的行:

# hexdump -v -s 446 -n 64 diskdump.ddimg000001be  00 01 01 00 83 fe ff ff  3f 00 00 00 d5 ea ff 00  |........?.......|
000001ce  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001de  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001ee  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001fe

根据有关分区表条目格式的已发布信息手动解码并不是特别困难,但您可以让 hexdump 自己完成这项工作并充分了解 hexdump,以便将来能够将其应用于其他事情。为此,有必要深入研究格式语法。

格式字符串简介

格式字符串使用 -e 命令行选项指定。hexdump 手册页描述或引用了所有语法元素,并给出了一些示例,但需要一些工作来确定如何执行示例中没有的操作。这里有一些非常简单的东西,只是将整个表输出为十六进制的空格分隔的单字节值:

# hexdump -s 446 -n 64 -v**-e '1/1 “ %02X”'**diskdump.ddimg00 01 01 00 83 FE FF FF 3F 00 00 00 D5 EA FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00#

您将识别 -n、-s 和 -v 的用法,以便它在偏移量 446 处转储 64 个字节,而不会折叠重复项。其余的是格式表达式和要转储的文件。请注意,格式表达式括在单引号中:“。1/1 表示以一个字节为一组处理一个字节。n/m 语法特定于 hexdump,还有一些其他的 hexdump 细节,但以 % 开头的元素来自 C printf 函数系列,它在 hexdump 手册页中被引用,您可以使用 man 3 printf 更详细地查看语法。

在示例中,“%02X”表示输出一个空格,后跟单个字节的值,作为两个带有前导零的十六进制数字。% 表示在输出中的此时插入文件中的值,% 后面的内容描述了如何执行此操作。X 表示十六进制输出,2 表示输出为两位数字,零表示当数字长度小于两位时输出前导零。

使用大写字母 X 意味着十六进制数字中的 A 到 F 数字将大写。使用小写 x 会导致这些数字以小写形式输出为 a 到 f。

获取行输出

上面示例中输出末尾的 # 是下一个 shell 提示符,我在示例中没有包含任何行尾格式,所以我将首先修复它。\n 字符序列用于指定新行,也来自 C printf 系列。但是,如果我只添加一个\n,我将获得64行输出,每行都有一个字节值:

# hexdump -s 446 -n 64 -v -e '1/1 " %02X"**“\n”**' diskdump.ddimg0001010083FEFFFF3F00
etc

这是因为整个带引号的表达式被重复完整解析,直到满足 -n 开关的整个值。上面的表达式示例一次处理一个字节并添加换行符。这需要完成 64 次才能满足 -n 64 要求。

转储数据块 (m/n)

如果我们回到那个 1/1,我们可以通过指定 16/1 而不是 1/1 来告诉 hexdump 以 16 个为一组(一个分区表条目的大小)输出字节:

# hexdump -s 446 -n 64 -v -e '**16/1**" %02X" "\n"' diskdump.ddimg00 01 01 00 83 FE FF FF 3F 00 00 00 D5 EA FF 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

16/1 不可逆,第一个数字是组中要处理的元素数,第二个数字是元素长度。十六个单字节值是我正在寻找的。相反,1/16 将是一个 16 字节的值。这会导致“转换字符 X 的字节计数错误”错误。/ 后面的值是每个 %02X 要处理的字节数,/ 后面的值是重复该操作的次数。%02X 不会解码 128 位(16 字节)值,因此会出现错误。因此,16/1 是适当的语法。

拆分结构化数据的各个元素

现在我需要填写一些细节。分区表的第一个字节是一个状态值,指示分区是否可引导。我将通过隔离处理第一个字节和每个分区的剩余 15 个字节作为一个组来将其分离为自己的解码选项:

# hexdump -s 446 -n 64 -v -e '**1/1 “%02x” 15/1**" %02X" "\n"' diskdump.ddimg
00 01 01 00 83 FE FF FF 3F 00 00 00 D5 EA FF 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

当然,这看起来没有任何不同,但我才刚刚开始。分区表条目中的下一个是第一批具有硬盘的 PC 的遗物。有三个单独的字节值,用于指定分区的起始头、扇区和柱面(仅最低有效 8 位)位置。这是一组三个单字节值:

# hexdump -s 446 -n 64 -v -e '1/1 "%02x"**3/1 “ %02x” 12/1**" %02X" "\n"' diskdump.ddimg
00 01 01 00 83 FE FF FF 3F 00 00 00 D5 EA FF 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

十进制输出

这仍然没有什么不同,头部、扇区和圆柱体具有数字意义,因此我将以十进制显示它们以提高人类可读性:

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 "**%3d**" 12/1 " %02x" "\n"' diskdump.ddimg
00   1   1   0 83 fe ff ff 3f 00 00 00 d5 ea ff 00
00   0   0   0 00 00 00 00 00 00 00 00 00 00 00 00
00   0   0   0 00 00 00 00 00 00 00 00 00 00 00 00
00   0   0   0 00 00 00 00 00 00 00 00 00 00 00 00

新的格式元素是“%3d”,这意味着输出一个空格,后跟一个十进制的三个字符宽的字段。字节值可以介于 0 到 255 之间的十进制,因此不能超过三位数字。由于数值是相关的,我删除了零前缀而不删除长度前缀,并得到了一个填充空格的右对齐字段。您可以看到分区 1 的状态代码为零,从柱面 0、头 1、扇区 1 开始。这是磁盘上第一个分区的正常位置。

接下来是分区类型,另一个单字节:

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 " %3d"**1/1 “ %02x” 11/1**" %02x" "\n"' diskdump.ddimg
00   1   1   0 83 fe ff ff 3f 00 00 00 d5 ea ff 00
00   0   0   0 00 00 00 00 00 00 00 00 00 00 00 00
00   0   0   0 00 00 00 00 00 00 00 00 00 00 00 00
00   0   0   0 00 00 00 00 00 00 00 00 00 00 00 00

接下来是分区末端的头部、扇区和柱面(仅最低有效 8 位)位置。我将再次以十进制执行这些操作:

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 " %3d" 1/1 " %02x"**3/1 “ %3d” 8/1**" %02x" "\n"' diskdump.ddimg
00   1   1   0 83 254 255 255 3f 00 00 00 d5 ea ff 00
00   0   0   0 00   0   0   0 00 00 00 00 00 00 00 00
00   0   0   0 00   0   0   0 00 00 00 00 00 00 00 00
00   0   0   0 00   0   0   0 00 00 00 00 00 00 00 00

32位数据

现在结构开始显现。之后,我得到了在挂载文件系统时有用的值,以及单字节输出没有用的值。分区的起始扇区号(逻辑块地址或 LBA)和长度(以扇区为单位)以 32 位值表示:

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 " %3d" 1/1 " %02x" 3/1 " %3d"**2/4 “ x08x”**"\n"' diskdump.ddimg
00   1   1   0 83 254 255 255 0000003f 00ffead5
00   0   0   0 00   0   0   0 00000000 00000000
00   0   0   0 00   0   0   0 00000000 00000000
00   0   0   0 00   0   0   0 00000000 00000000

请注意,最新更改添加了 2/4 计数说明符,这意味着 4 字节(32 位)值的 2 个实例。%08x 格式表示将值输出为带有前导零的 8 位十六进制数字。由于 LBA 值具有数字意义,因此在十进制中和不使用零填充时会更有用

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 " %3d" 1/1 " %02x" 3/1 " %3d" 2/4 "**%9d”**"\n"' diskdump.ddimg
00   1   1   0 83 254 255 255        63  16771797
00   0   0   0 00   0   0   0         0         0
00   0   0   0 00   0   0   0         0         0
00   0   0   0 00   0   0   0         0         0

最长的 32 位十进制数有 9 位数字,而十六进制的相同数字有 8 位数字,所以我将“%08x”更改为“%9d”。

已签名/未签名

这就是我所需要的,但我有点粗心,尽管这次它不会影响输出。d 格式字符表示值是有符号的(可以是正数或负数)。分区不能具有负扇区、磁头、柱面或 LBA 值。在大多数平台上,整数值不能仅根据字节的值来识别为有符号或无符号。需要外部知识来说明是将值解释为有符号还是无符号。由于我知道这些值都是无符号的,那么在我到处都有 % 格式的 d,我应该用 u 替换它以指示该值是无符号的(仅限正值):

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1**“ %3u”**1/1 " %02x" 3/1**“ %3u”**2/4**“ %9u”**"\n"' diskdump.ddimg
00   1   1   0 83 254 255 255        63  16771797
00   0   0   0 00   0   0   0         0         0
00   0   0   0 00   0   0   0         0         0
00   0   0   0 00   0   0   0         0         0

向输出添加额外文本

您可以在十六进制转储的输出中包含您选择的文字文本。实际上,我们在使用的空格和\n值中已经有很多了。您可以添加其他内容并使输出更有意义:

# hexdump -s 446 -n 64 -v -e '1/1**“分区:| %02x”**3/1**" |%3u”**1/1 "**|%02x”**3/1 "**|%3u”**2/4 "**|%9u”**"\n"' /data/vms/vmware/vmdk/2raw/rawfromvmdk.img
Partition:| 00 |   1 |   1 |   0 | 83 | 254 | 255 | 255 |        63 |  16771797
Partition:| 00 |   0 |   0 |   0 | 00 |   0 |   0 |   0 |         0 |         0
Partition:| 00 |   0 |   0 |   0 | 00 |   0 |   0 |   0 |         0 |         0
Partition:| 00 |   0 |   0 |   0 | 00 |   0 |   0 |   0 |         0 |         0

这些更改为我们提供了列边框和每行的介绍性词。

把它放在带有一些布局的 shell 脚本中

所以,现在我有一个十六进制转储命令行,它将转储分区表。这是在添加了一些单元格边框的 shell 脚本中:

#!/bin/bash# Dump what would be the partition table of the command line specified# file if that file was partitioned mediaecho
echo 'PARTITION TABLE IN' $1
echo
echo '   -------Start-------    --------End--------'
echo 'St |  Hd | Cyl | Sec | Tp |  Hd | Cyl | Sec | LBA Start |   LBA End'
echo '---|-----|-----|-----|----|-----|-----|----------------------------'
hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 " | %3u" 1/1 " | %02x" 3/1 " | %3u" 2/4 " | %9u" "\n"' $1
echo '-------------------------------------------------------------------'
echo
echo将其保存为路径中某处的 partdump,将模式更改为可执行文件,您将拥有一个很酷的新工具:# partdump diskdump.ddimgPARTITION TABLE IN diskdump.ddimg-------Start-------    --------End--------
St |  Hd | Cyl | Sec | Tp |  Hd | Cyl | Sec | LBA Start |   LBA End
---|-----|-----|-----|----|-----|-----|----------------------------
00 |   1 |   1 |   0 | 83 | 254 | 255 | 255 |        63 |  16771797
00 |   0 |   0 |   0 | 00 |   0 |   0 |   0 |         0 |         0
00 |   0 |   0 |   0 | 00 |   0 |   0 |   0 |         0 |         0
00 |   0 |   0 |   0 | 00 |   0 |   0 |   0 |         0 |         0-------------------------------------------------------------------

这是一个 64 位的世界!

尽管这很好,但它几乎没有触及您可以使用 hexdump 做什么的表面。我们所做的只是处理十六进制和有符号/无符号十进制的 8 位和 32 位值,具有各种字段宽度和值布局,例如前导零或空格。这些是整数运算。如今,您必须能够处理长度超过 32 位的值。64 位值的 printf 语法在十六进制转储中不是必需的。您可以指定正在使用 64 位(8 字节)值以及所需的输出格式。例如,如果 LBA 值恰好是单个 64 位,您可以这样做以十六进制形式查看它们 – 请注意,这不再是对分区表的有效解释:

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 " %3u" 1/1 " %02x" 3/1 " %3u"**1/8 "**%x" "\n"' diskdump.ddimg
00   1   1   0 83 254 255 255 ffead50000003f
00   0   0   0 00   0   0   0 0
00   0   0   0 00   0   0   0 0
00   0   0   0 00   0   0   0 0

请注意,末尾的值 ffead50000003f 的格式为单个 8 字节值 (1/8),并以十六进制 %x 输出。您可以使用 %16x 来保留数字列对齐方式:

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 " %3u" 1/1 " %02x" 3/1 " %3u" 1/8 "**%16x”**"\n"' diskdump.ddimg
00   1   1   0 83 254 255 255   ffead50000003f
00   0   0   0 00   0   0   0                0
00   0   0   0 00   0   0   0                0
00   0   0   0 00   0   0   0                0

您可以包含前导零:

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 " %3u" 1/1 " %02x" 3/1 " %3u" 1/8 " %016x" "\n"' diskdump.ddimg00   1   1   0 83 254 255 255 00ffead50000003f
00   0   0   0 00   0   0   0 0000000000000000
00   0   0   0 00   0   0   0 0000000000000000
00   0   0   0 00   0   0   0 0000000000000000

您可以使用有符号十进制而不是十六进制:

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 " %3u" 1/1 " %02x" 3/1 " %3u" 1/8 "**%20d”**"\n"' diskdump.ddimg
00   1   1   0 83 254 255 255    72034319610150975
00   0   0   0 00   0   0   0                    0
00   0   0   0 00   0   0   0                    0
00   0   0   0 00   0   0   0                    0

您可以使用无符号十进制而不是十六进制:

# hexdump -s 446 -n 64 -v -e '1/1 "%02x" 3/1 " %3u" 1/1 " %02x" 3/1 " %3u" 1/8 "**%20u”**"\n"' diskdump.ddimg
00   1   1   0 83 254 255 255    72034319610150975
00   0   0   0 00   0   0   0                    0
00   0   0   0 00   0   0   0                    0
00   0   0   0 00   0   0   0                    0

文件中的文本

并非二进制文件中的所有值都是数字,该文件还可能包含文本。hexdump也提供了一种显示这一点的方法。与 C printf 系列函数不同,您必须提前知道文本有多长,并且有很多方法可以实现相同的目标。回到我开始使用的 ELF 可执行文件,您可以按原样转储签名:

# hexdump**-s 1 -n 3**-v -e**'1/3 “%s” “\n”'**mydata
ELF

您获得以下每一项相同的效果:

# hexdump -s 1 -n 3 -v -e '3/1**“%c”**"\n"'  mydata
ELF
# hexdump -s 1 -n 3 -v -e '3/1**“%_p”**"\n"'  mydata
ELF
# hexdump -s 1 -n 3 -v -e '3/1**“%_c”**"\n"'  mydata
ELF
# hexdump -s 1 -n 3 -v -e '3/1**“%_u”**"\n"'  mydata
ELF

还有许多其他示例将输出相同的三个字符,但换行符的排列方式不同。_p、_c 和 _u 示例特定于十六进制转储,而不是 printf 语法的一部分。该_p会导致没有标准字形的字符输出为 .– 我们可以看到,通过超出 ELF 签名中的 F 的几个字节:

# hexdump -s 1 -n 5 -v -e '5/1 "%c" "\n"'  mydataELF
# hexdump -s 1 -n 5 -v -e '5/1 "%_p" "\n"'  mydataELF..

请注意,第一个使用 printf 标准 %c 对 ELF 后面的两个字符不显示任何内容,但第二个在 ELF 之后的每个字符显示一个点。这有助于对齐,并指示数据何时从文本(打印字符)更改为非文本(非打印字符)。

_c示例以主机的默认字符集输出文本,并按八进制(以 8 为基数)的值显示非打印字符:

# hexdump -s 1 -n 5 -v -e '5/1 "%_c" "\n"' mydataELF002001

因此,ELF 后面的点是字节值 2 和 1(八进制在这里输出为三位数字)。

_u示例输出带有控制字符(十进制字节值小于 32 且值为 255 的控件字符)的 US ASCll 文本,这些字符由其三个字符的控件名称显示:

# hexdump -s 1 -n 5 -v -e '5/1 "%_u" "\n"' mydataELFstxsoh

这是 TeXt 控件字符的开始,后跟标题的开始控制字符。在此文件中,字节值不表示这些 ASCII 控制字符,但您可以看到如何根据需要使用 %s、%_p、%_c 和 %_u 来处理不同类型的输入文件。

包含文本和数字数据的文件

正如我们之前将 8、32 和 64 位 %x 输出以及 8、32 和 64 位十进制值组合在一起一样,我们可以在同一命令行上组合数字和文本值:

# hexdump -n 6 -v -e**'1/1 “%02x ” 3/1 “%_u” 2/1 “ %02x”**"\n"' mydata
7f ELF 02 01

我删除了 -s 1,以便您可以在一些其他类型的数据之间看到文本。

带小数部分的数字

在编程世界中,它们被称为浮点数。十六进制转储可以显示它们,但不能保证文件中的浮点值采用主机的本机浮点格式。不过。如果您知道该 ELF 文件中有一个浮点值 5213 字节(实际上没有):

# hexdump -s 5213**-n 8 -v -e '1/8 “%f ”**"\n"' mydata
311143424.000000

同样,您可以将其与其他格式选项结合使用,以查看具有不同格式的文件的顺序元素。%f 转换的默认长度为 8 字节,因此您实际上可以从示例中删除 1/8,因为没有其他格式值并且转储长度为 8。如果数字有太多数字,那么你可以以指数格式显示它:

hexdump -s 5295 -n 8 -v -e '1/8**“%g”**"\n"' mydata
1.07497e-255

如果您希望 e 大写,可以使用 %G:

hexdump -s 5295 -n 8 -v -e '1/8**“%G”**"\n"' mydata
1.07497E-255

所有这三种浮点格式也可以对 4 字节浮点类型进行操作:

hexdump -s 5255 -n 8 -v -e '1/4 "%G" "\n"' mydata
1.67511E-10
1.07861E-38

我不再知道我在文件中的位置

当您解码文件中的大量元素时,您最终可能会得到如此多的输出,以至于您无法分辨给定值在文件中的位置。在第一个示例中,您看到 hexdump 将偏移量输出到第一列中的文件中:

# hexdump mydata0000000 457f 464c 0101 0001 0000 0000 0000 0000
0000010 0002 0003 0001 0000 8430 0804 0034 0000

您可以将相同的内容添加到您自己的解码中,为任何整数数字格式类型添加_a前缀:x、d、o。如果示例文件有 8 个浮点值,则文件中有 4091 个字节,我想知道每个浮点值的位置:

# hexdump -s 4091 -n 64 -v -e '**“%_ax | ”**1/8 "%G" "\n"' mydata
ffb | 2.56934
1003 | 2.57715
100b | 2.58496
1013 | 2.59277
101b | 0
1023 | 0
102b | 1.29478E+16
1033 | 9.34901E+25

我包括了该栏,以便您可以在值溢出以添加新数字时看到问题。我们可以对 %_ax 和 %_ad 使用相同的前缀,就像对正常的 %x 和 %d 一样:

# hexdump -s 4091 -n 64 -v -e '**“%08_ax | ”**1/8 "%G" "\n"' mydata
00000ffb | 2.56934
00001003 | 2.57715
0000100b | 2.58496
00001013 | 2.59277
0000101b | 0
00001023 | 0
0000102b | 1.29478E+16
00001033 | 9.34901E+25

十进制可能对人类的可读性更有用:

# hexdump -s 4091 -n 64 -v -e '**“%9_ad | ”**1/8 "%G" "\n"' mydata4091 | 2.569344099 | 2.577154107 | 2.584964115 | 2.592774123 | 04131 | 04139 | 1.29478E+164147 | 9.34901E+25

有一个特殊版本的文件位置选项,它将显示文件中最后一个值输出之后下一个字节的位置,_A:

hexdump -s 4091 -n 64 -v -e '"%9_ad | " 1/8 "%G" "\n"**“%_Ad \n”**' mydata4091 | 2.569344099 | 2.577154107 | 2.584964115 | 2.592774123 | 04131 | 04139 | 1.29478E+164147 | 9.34901E+254155

4155 是 %_Ad 的效果,额外的缩进是故意的。当您从多个 hexdump 命令行构建输出并想知道要在下一个命令上使用什么 -s 值而不必计算它时,_A 选项非常有用。

自定义字段宽度

在 % 后指定字段宽度时,可以使用其他语法来描述输出值的总宽度以及要包含的输出位数。例如,以下内容是等效的:

# hexdump -s 5091 -n 32 -v -e '1/4 "%08x" "\n"' mydata2e697472
752f0053
732f7273
702f6372
616b6361
2f736567
4c495542
6c672f44# hexdump -s 5091 -n 32 -v -e '1/4**“%8.8x”**"\n"' mydata
2e697472
752f0053
732f7273
702f6372
616b6361
2f736567
4c495542
6c672f44

在第二个中,我将 %08x 替换为 %8.8x。在第二种语法中,点之前的数字是字段的总宽度,点后面的数字是要生成的输出位数。如果第一个数字大于第二个数字,则该值将右对齐输出。因此,我们可以在 10 个字符的字段中拥有 8 位十六进制值:

# hexdump -s 5299 -n 32 -v -e '1/4**“%10.8x”**"\n"' mydata0b002402030b3e0b0300000e0b0b002408030b3e240400003e0b0b000500000b

无论第一个或第二个数字的值如何,都会输出输入长度和格式字符所暗示的整个长度,因此您不能用 %0.0x 愚弄十六进制转储:

# hexdump -s 5299 -n 32 -v -e '1/4 "%0.0x" "\n"' mydatab002402
30b3e0b
300000e
b0b0024
8030b3e
24040000
3e0b0b00
500000b

这些值是完整输出的,并左对齐。

将一切整合在一起

下面是一个非常大的例子,它使用大量带有 shell 变量、算术和逻辑运算的十六进制命令将数据包从 tcpdump 文件转储到 tcp 标头 - 为其他一些 tcpdump 文件插入正确的偏移量留给读者作为练习:

#!/bin/bash# Decode a UDP packet raw binary dataDonelet offs=40
declare -i ws
declare -i end
declare -i rmndr
declare -i byteval
declare -i bitws
NetworkOrder16Bit() {ws=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`((offs+=1))((ws*=256))byteval=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`((offs+=1))((ws+=byteval))
}
NetworkOrder32Bit() {ws=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`((offs+=1))((ws*=256))byteval=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`((offs+=1))((ws+=byteval))((ws*=256))byteval=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`((offs+=1))((ws+=byteval))((ws*=256))byteval=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`((offs+=1))((ws+=byteval))
}
echo
echo
end=$offs+74
#Start with the destination MAC address
hexdump -s $offs -n 6 -e '"Ether Dest:        " 5/1 "%02x:" 1/1 "%02x\n"' test2.pkts
((offs+=6))# Then the sourcehexdump -s $offs -n 6 -e '"Ether Src:         " 5/1 "%02x:" 1/1 "%02x\n"' test2.pkts
((offs+=6))# Next is the packet type, but its in network byte order so we can't# use '1/2 "%04x"' - we may be run on a system with another byte order and you'll# get different outputhexdump -s $offs -n 2 -e '"Ether Type:        " 2/1 "%02x" "\n"' test2.pkts
((offs+=2))# IP header# Get the version/len value into a variable we can do arithmetic withbyteval=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`
((offs+=1))# The most significant 4 bits are the IP version((bitws=byteval&0xF0))
((bitws/=16))
echo "IP Version:        $bitws"# Least significant 4 bits are the header length((bitws=byteval&0x0F))
((bitws*=4))
echo "Header Length:     $bitws"# Type of servicebyteval=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`
((offs+=1))# Precedence((bitws=byteval&0xE0))
((bitws/=32))
echo "Precedence:        $bitws"# Delay((bitws=byteval&0x10))
if (( bitws ))
thenecho "Minimize delay"
fi# Throughput((bitws=byteval&0x08))
if (( bitws ))
thenecho "High throughput"
fi# Reliability((bitws=byteval&0x08))
if (( bitws ))
thenecho "High reliability"
fi# Cost((bitws=byteval&0x08))
if (( bitws ))
thenecho "Minimize cost"
fi# Message length - in network byte orderNetworkOrder16Bit
echo "Message Length:    $ws"# IDhexdump -s $offs -n 2 -e '"ID:                " 2/1 "%02x" "\n"' test2.pkts
((offs+=2))# Flagsbyteval=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`
((offs+=1))# DF((bitws=byteval&0x40))
if (( bitws != 0))
thenecho "Don't fragment"
else# More fragments((bitws=byteval&0x20))if (( bitws ))thenecho "More fragments"fi# Fragment number((ws=bitws&0x1F))((ws*=256))byteval=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`((ws+=byteval))echo "Fragment number:    $ws"
fi
((offs+=1))
hexdump -s $offs -n 1 -e '"TTL:               " 1/1 "%u\n"' test2.pkts
((offs+=1))
hexdump -s $offs -n 1 -e '"Protocol:          " 1/1 "%u\n"' test2.pkts
((offs+=1))
hexdump -s $offs -n 2 -e '"Header checksum:   " 2/1 "%02x" "\n"' test2.pkts
((offs+=2))
hexdump -s $offs -n 4 -e '"Source IP Addr:    " 3/1 "%02u." 1/1 "%02u\n"' test2.pkts
((offs+=4))
hexdump -s $offs -n 4 -e '"Dest IP Addr:      " 3/1 "%02u." 1/1 "%02u\n"' test2.pkts
((offs+=4))# TCP headerNetworkOrder16Bit
echo "Source Port:       $ws"
NetworkOrder16Bit
echo "Dest Port:         $ws"
hexdump -s $offs -n 4 -e '"Sequence Number:   " 4/1 "%02x" "\n"' test2.pkts
((offs+=4))
hexdump -s $offs -n 4 -e '"ACK Number:        " 4/1 "%02x" "\n"' test2.pkts
((offs+=4))
ws=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`
((offs+=1))
((ws/=16))
((ws*=4))
echo "Header Length:     $ws"
byteval=`hexdump -s $offs -n 1 -e '1/1 "%u"' test2.pkts`
((offs+=1))
((bitws=byteval&0x80))
echo -n "Flags:             "
if (( bitws ))
thenecho -n "CWR "
fi
((bitws=byteval&0x80))
if (( bitws ))
thenecho -n "CWR "
fi
((bitws=byteval&0x40))
if (( bitws ))
thenecho -n "ECN "
fi
((bitws=byteval&0x20))
if (( bitws ))
thenecho -n "URG "
fi
((bitws=byteval&0x10))
if (( bitws ))
thenecho -n "ACK "
fi
((bitws=byteval&0x08))
if (( bitws ))
thenecho -n "PSH "
fi
((bitws=byteval&0x04))
if (( bitws ))
thenecho -n "RES "
fi
((bitws=byteval&0x02))
if (( bitws ))
thenecho -n "SYN "
fi
((bitws=byteval&0x01))
if (( bitws ))
thenecho -n "FIN "
fi
echo
NetworkOrder16Bit
echo "Window Size:       $ws"
hexdump -s $offs -n 2 -e '"Checksum:          " 2/1 "%02x" "\n"' test2.pkts
((offs+=1))# Some more of the packetecho
echo
hexdump -s $offs -n 128 -C test2.pkts
echo
echo

这不应被视为可行的数据包分析器,但它显示了构建自定义工具来调查某些任意二进制数据所需的大多数元素。希望在将十六进制转储应用于您自己的二进制文件解码问题时,您会发现一些有用的东西。

理解 Hexdump相关推荐

  1. 深入理解JVM类文件格式

    我们知道Java最有名的宣传口号就是:"一次编写,到处运行(Write Once,Run Anywhere)",而其平台无关性则是依赖于JVM, 所有的java文件都被编译成字节码 ...

  2. linux elf 文件理解与分析

    https://linux-audit.com/elf-binaries-on-linux-understanding-and-analysis/ 我们理所当然的使用一些工具.其中一部分就是 linu ...

  3. 定制hexdump的打印输出格式

    背景 hexdump是Linux系统下一个以十六进制或ASCII形式输出文件内容的工具,通常-b -c -C -d等选项提供的打印格式就够用了,但有时板卡输出的数据格式比较特殊,想用文本比较工具(例如 ...

  4. MBR引导程序源码理解

    目录 MBR引导程序源码理解 序 参考链接 开机流程简述与MBR引导程序的关系 进入 BIOS 确认开机启动磁盘 获取引导磁盘第一扇区MBR数据 反汇编MBR.bin 源码解读 `00000000 E ...

  5. 【深入理解JVM】JAVA线上故障排查全套路

    线上故障主要会包括cpu.磁盘.内存以及网络问题,而大多数故障可能会包含不止一个层面的问题,所以进行排查时候尽量四个方面依次排查一遍.同时例如jstack.jmap等工具也是不囿于一个方面的问题的,基 ...

  6. 【从零开始】理解视频编解码技术

    [从零开始]理解视频编解码技术 auxten ​ CovenantSQL 联合创始人 ​关注他 1,263 人赞同了该文章 转载自: https://github.com/leandromoreira ...

  7. 通用解题法——回溯算法(理解+练习)

    积累算法经验,积累解题方法--回溯算法,你必须要掌握的解题方法! 什么是回溯算法呢? 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就&quo ...

  8. stream流对象的理解及使用

    我的理解:用stream流式处理数据,将数据用一个一个方法去 . (点,即调用) 得到新的数据结果,可以一步达成. 有多种方式生成 Stream Source: 从 Collection 和数组 Co ...

  9. Linux shell 学习笔记(11)— 理解输入和输出(标准输入、输出、错误以及临时重定向和永久重定向)

    1. 理解输入和输出 1.1 标准文件描述符 Linux 系统将每个对象当作文件处理.这包括输入和输出进程.Linux 用文件描述符(file descriptor)来标识每个文件对象.文件描述符是一 ...

最新文章

  1. 最佳SQL Server 2008入门教程
  2. Delphi中静态方法重载还是覆盖的讨论
  3. Python 3.6安装教程
  4. Python中的MySQL数据库编程
  5. jvm监控jstatd使用
  6. bootstrap验证 2021-04-21
  7. 使用百度EasyDL训练自己的图像识别模型
  8. 智齿科技获投B轮融资5000万人民币
  9. 技术小卡之Redis 管道技术
  10. TP-LINK三层网管交换机通过console接口完成复位操作
  11. Git 常用命令大全-转载
  12. Uncaught SyntaxError: The requested module ‘/node_modules/.vite/vue.js?v=bd1817bb‘ does not provide
  13. 2010-2021年上市公司专利数据
  14. 欧拉角和四元数相互转换
  15. 组件实战(一)——导航栏设计
  16. 深度linux任务栏在哪,更新Deepin 20后任务栏dde-dock消失不见的暂时解决
  17. Linux Miscellaneous Device
  18. 怎么绕过付费验证获取作文网站上的内容
  19. 原始设备制造商OEM简介
  20. 知识的经济学分析:一个文献综述——基于范式演进的视点

热门文章

  1. 初级会计实务--第七章第三节、产品成本的归集和分配
  2. 千兆网络变压器原理图及与PHY与网络变压器接线方式
  3. 《大话设计模式》php版本
  4. 《人类简史》一、智人觉醒——席卷全球的洪水
  5. 天空之门服务器维护通知,v6.3.0 新版本更新公告
  6. Android仿IOS滑动关机-自定义view系列(6)
  7. 如何学习Windows编程
  8. CAD进阶练习(四)
  9. PTGUI全景合成软件使用教程之镜头参数设置
  10. cmos逻辑门传输延迟时间_集基耦合双稳电路,集成化单稳电路,数字逻辑电路,门电路,触发器...