递归搜索二进制文件目录中的十六进制序列?

4
我目前使用的命令来搜索一些十六进制值(例如0A 8b 02)包括: find . -type f -not -name "*.png" -exec xxd -p {} \; | grep "0a8b02" || xargs -0 -P 4 如果想要实现以下目标,有可能进行改进:
  • 递归地搜索文件
  • 显示偏移量和文件名
  • 排除带有某些扩展名的文件(上述示例不会搜索.png文件)
  • 速度:搜索需要处理直接总共约2GB大小约为50KB至1MB的200,000个文件。
我不确定xargs是否能够正确地工作于4个处理器。而且,当grep找到匹配项时,由于是从xxd中导出的,所以我很难打印出与之相应的文件名。你有什么建议吗?

我会编写一个用于搜索单个二进制文件的脚本(成功时打印文件名),并在 find | xargs 中使用该脚本。由于您使用的是 zsh,因此在子shell中定义函数很困难。如果您决定将所有内容放在一个脚本中,可以改用 bash,它允许您导出函数。 - 4ae1e1
那么,根据我目前所拥有的内容...甚至无法输出文件名吗? - Helen Che
如果搜索字节序列从不包括 0xa(即换行符),那么会有一个相当简单的解决方案 - 但听起来它们可以包括,对吗?此外,您是否使用 GNU 工具(Linux)? - mklement0
@mklement0 不,序列永远不会包括 0xa,不幸的是我正在 OS X 上运行。这会成为问题吗? - Helen Che
1
很不幸,可能是的。但是,也许安装GNU grep对您来说是一个选择。请查看我的答案,让我们在那里继续讨论。 - mklement0
1个回答

4

如果:

  • 您有GNUgrep
  • 并且您搜索的十六进制字节从不包含换行符(0xa[1]
    • 如果它们包含NUL(0x),则必须通过文件(-f)而不是直接参数提供grep搜索字符串。

以下命令将帮助您使用搜索0e 8b 02的示例:

LC_ALL=C find . -type f -not -name "*.png" -exec grep -FHoab $'\x{0e}\x{8b}\x{02}' {} + |
  LC_ALL=C cut -d: -f1-2
< p > grep 命令的输出行如下:

<filename>:<byte-offset>:<matched-bytes>

LC_ALL=C cut -d: -f1-2将其缩小为<filename>:<byte-offset>

该命令在使用BSDgrep时几乎可以正常工作,但是报告的字节偏移始终是匹配模式所在行的开头
换句话说:如果文件中有换行符在匹配之前,则字节偏移将不正确
此外,BSD grep不支持将NUL(0x0)字节作为搜索字符串的一部分进行指定,即使通过-f提供文件也不支持。

请注意,这里没有并行处理,只有一些基于使用find的-exec ... +的grep调用,类似于xargs,一次将尽可能多的文件名传递给grep。通过让grep直接搜索字节序列,无需使用xxd:
该序列被指定为ANSI C-quoted string,这意味着转义序列由shell扩展为文字,使Grep能够搜索结果字符串作为文字(通过-F),这样更快。
LC_ALL=C确保grep将每个字节视为自己的字符而不应用任何编码规则。
-F将搜索字符串视为文字(而不是正则表达式)
-H在每个输出行之前添加相关输入文件名;当给出多个文件名参数时,Grep会隐式执行此操作
-o仅报告匹配的字符串(字节序列),而不是整个行(在二进制文件中,行的概念没有意义)[2]
-a将二进制文件视为文本文件进行处理(如果没有此选项,则对于具有匹配项的二进制输入文件,Grep只会打印文本Binary file matches)
-b报告匹配项的字节偏移量 如果在给定的输入文件中找到至多1个匹配项足够了,那么请添加-m 1

[1] 由于Grep总是将搜索模式字符串中的换行符视为分隔多个搜索模式,因此不能使用换行符。另外,Grep是基于行的,所以您无法跨行匹配;GNU Grep的-null-data选项可以通过NUL字节拆分输入,但仅当您的搜索字节序列不包含NUL字节时才有效;您还必须在与-P结合使用的正则表达式中表示您的字节值为转义序列,因为您需要使用转义序列\n代替实际的换行符。

[2] 需要使用 -o 选项才能使 -b 报告匹配的字节偏移量,而不是报告行开头的偏移量(如上所述,BSD Grep 总是报告后者,不幸的是);此外,仅报告匹配本身在这里是有益的,因为尝试打印整个行将导致输出行长度不可预测,因为二进制文件中没有行的概念;无论如何,在终端中输出二进制文件的字节可能会导致奇怪的渲染行为。


您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Helen Che
@VeraWang:这是_shell_的一个限制,您无法将包含NUL(0x0)的值作为_参数_传递。使用_GNU_ Grep,您可以通过将字节序列保存到_文件_中,然后使用-f选项来解决此问题。不幸的是,这种技术在_BSD_ Grep上不起作用。 - mklement0
@VeraWang:关于显示n个字节的上下文:据我所知,上下文特征通常与“文本行”相关,而不是字节。 - mklement0
@VeraWang:这是一个生成单个NUL并将其重定向到文件的示例:dd if=/dev/zero bs=1 count=1 2>/dev/null - mklement0
1
@VeraWang:您可以使用“-regex”和“-iregex”主要功能,它们将整个路径与正则表达式匹配(在后一种情况下不区分大小写)。如果您将其与“-E”组合以启用扩展正则表达式,则可以使用交替(|),例如以下示例,该示例排除了.txt.bak文件:find -E . ! -iregex '.*\.(bak|txt)$' - mklement0
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接