如何 grep 带通配符的十六进制模式?

如何 grep 带通配符的十六进制模式?

我正在尝试查找文件中十六进制模式的偏移量。这适用于一个特定值:

$ grep -obUaP -m1 "\x00\x50\x53\x46\x01\x01\x00\x00\x34\x01\x00\x00" file.bin
3088:PSF4

但是,此模式包含一些会更改的字节,因此我需要在 grep 中包含通配符。我不知道该怎么做。这是迄今为止我尝试过的所有内容:

  • \x.., \x., ..,我能想到的每一个类似的形式都不匹配
  • \x[0-9][0-9]不匹配
  • \x.*不匹配
  • 只是.*(即,\x00.*\x01匹配,但它是贪婪的并且比模式匹配更多

可能忽略了一些愚蠢的事情,但我在这里碰壁了。

如何指定十六进制通配符,或者至少在使用 grep 和 perl-regex 来搜索十六进制时?

答案1

grep -P '\xAB'不寻找十六进制字符。不存在这样的事情十六进制字符\xAB是PCRE句法匹配以十六进制表示的代码点值为 0xAB(十进制为 171)的字符。

代码点这里将是使用 UTF-8 的语言环境中的 Unicode 代码点和使用单字节字符集的语言环境中的字节值(GNUgrep -P不支持除 UTF-8 之外的多字节字符集)。

因此,\xAB将匹配 UTF-8 语言环境中的 U+00AB 字符 («)(该字符以 2 个字节进行编码:0xc2 和 0xab)以及单字节语言环境中的 0xAB 字节(例如,它Ћ表示使用 iso8859-5 字符集的区域设置)。

如果您想匹配字节值,您应该确保区域设置使用单字节字符集,C区域设置可能是您最好的选择。

LC_ALL=C grep -P '\xAB'

匹配 0xAB (171) 字节,无论它在任何字符集中代表什么字符(如果有)。

要匹配任何单个字节,您可以再次使用.(假设 C 语言环境或任何每个字符字符集为单个字节的本地语言)。

匹配某个范围内的字节值,正如 @Angle115 已经说过的:([\x01-\x45]此处适用于字节值 1 到 0x45 / 69)

但请记住,grep匹配内容文本的线¹,因此它永远不会找到作为行分隔符的换行符,并且无论区域设置如何,其值始终为 0x0A²(十进制为 10)。

因此LC_ALL=C grep -P '\x23.\xab'将匹配 3 个字节的序列,第一个字节的值为 0x23,第二个字节的值为任意值除了 0xA第三个值为 0xAB。

为了能够搜索具有任意值(包括 0xA)的字节,您需要将整个输入视为一个整体,而不是像以前那样一次处理一行或 nul 分隔的记录grep

为此,您可以将pcregrep-M(多行)选项与(?s)标志一起使用(换行符不被 特殊处理.)或perl与其 slurp-mode 一起使用:

LC_ALL=C pcregrep --file-offsets -Ma '(?s)\x23.\xab' < file

pcregrep没有-b选项,--file-offsets它打印偏移量和长度可能是最接近的)。

perl -l -0777 -ne 'print "$-[0]:$_" while /\x23.\xab/gs' < file

或者:

perl -l -0777 -ne 'print $-[0] if /\x23.\xab/s' < file

仅打印第一个匹配的字节偏移量。

perl将整个文件加载到内存中,pcregrep不会,但有内部限制,可能会阻止您处理 0xA 字节相距较远的文件。


--null¹ 或用/分隔的 NUL 记录-z

¹ 在基于 ASCII 的系统上。我什至不知道 libpcre 是否曾经被移植到 EBCDIC 系统,我怀疑现在很多人都不会遇到其中的一些。

答案2

您可以包含范围而不是使用通配符来匹配所有 ASCII 字符,如下所示:

grep -Pe '\x00\x50\x53\x46[\x00-\x7F]\x01\x00\x00\x34\x01\x00\x00'

相关内容