我在 bash shell 中使用 grep 来查找文件中的一系列十六进制字节:
$ find . -type f -exec grep -ri "\x5B\x27\x21\x3D\xE9" {} \;
搜索工作正常,尽管我知道不使用仅返回结果的
-a
选项时匹配存在限制:
Binary file ./file_with_bytes matches
我想获取匹配结果的偏移量,可以吗?我愿意使用另一个类似的工具,我只是不确定它会是什么。
实际上 grep 中有一个选项可以使用
-b --byte-offset Print the 0-based byte offset within the input file
使用此选项的简单示例:
$ grep -obarUP "\x01\x02\x03" /bin
打印出目录内匹配模式的文件名和字节偏移量
/bin/bash:772067:
/bin/bash:772099:
/bin/bash:772133:
/bin/bash:772608:
/bin/date:56160:
注意,
find
实际上是不需要的,因为选项-r
已经处理了递归文件搜索
不在电脑前,但使用:
od -x yourFile
或
xxd yourFile
将其转储为十六进制,并在左侧进行偏移。
有时可能找不到您的搜索字符串,因为字符不是连续出现的而是分成两行。不过,您可以将文件传递两次,第二次截掉前 4 个字节,以确保您的字符串在一次传递或另一次传递中都完好无损。然后重新添加偏移量并对偏移量进行排序和统一。
在花费了比我想要的更多的时间尝试让macOS的
grep
(以及其他各种类似的工具,包括GNUgrep
)输出这样的二进制偏移量(和其他答案)之后,我偶然发现了radare2
的 rafind2
命令,它正是我所需要的:
– 文件中的高级命令行字节模式搜索rafind2
我们可以使用 Homebrew 在 macOS 上安装radare2:
brew install radare2
虽然
rafind2
不能用于递归搜索多个文件,但它非常强大,并且非常适合查找单个文件中的偏移量。
我们可以使用
-x
标志来搜索十六进制字符串:
搜索十六进制字符串(-x [hex]
)(可多次使用)909090
⇒ rafind2 -x "5B27213DE9" /path/to/the-bin
0xd0fdf
我们甚至可以通过在通过
.
传递的十六进制模式中使用 -x
来使用通配符占位符(完整字节或半字节):
⇒ rafind2 -x "5B..213.E9" /path/to/the-bin
0xd0fdf
我们知道
grep
可以在二进制文件中进行递归(-r
)匹配:
⇒ grep -r '\x5B\x27\x21\x3D\xE9' ./path/to/bins
Binary file ./path/to/bins/aaa matches
Binary file ./path/to/bins/bbb matches
Binary file ./path/to/bins/ccc matches
通过使用
-rl
,我们可以让grep
递归搜索,并输出匹配的文件名:
⇒ grep -rl '\x5B\x27\x21\x3D\xE9' ./path/to/bins
./path/to/bins/aaa
./path/to/bins/bbb
./path/to/bins/ccc
然后我们可以与
rafind2
结合来提取所有偏移量:
SEARCH_DIRECTORY="./path/to/bins"
GREP_PATTERN='\x5B\x27\x21\x3D\xE9'
# Remove all instances of '\x' from PATTERN for rafind2
# Eg. Becomes 5B27213DE9
PATTERN="${GREP_PATTERN//\\x/}"
grep -rl "$GREP_PATTERN" "$SEARCH_DIRECTORY" | while read -r file; do
echo "$file:"
rafind2 -x "$PATTERN" "$file"
done
这会导致输出如下:
./path/to/bins/aaa:
0x4b0060
./path/to/bins/bbb:
0x4b0060
./path/to/bins/ccc:
0x4bd1e0
我们也可以跳过
grep
,直接将 rafind2
与 find
/ fd
一起使用:
SEARCH_DIRECTORY="./path/to/bins"
PATTERN='5B27213DE9'
# Using find
find "$SEARCH_DIRECTORY" -type f -exec sh -c 'output=$(rafind2 -x "$1" "$2"); [ -n "$output" ] && echo "$2:" && echo "$output"' sh "$PATTERN" {} \;
# Using fd
fd --type f --exec sh -c 'output=$(rafind2 -x "$1" "$2"); [ -n "$output" ] && (echo "$2:"; echo "$output")' sh "$PATTERN" {} "$SEARCH_DIRECTORY"
两者都会输出如下:
./path/to/bins/aaa:
0x4b0060
./path/to/bins/bbb:
0x4b0060
./path/to/bins/ccc:
0x4bd1e0
要大致了解这些方法的性能,请使用
time
查看每个方法单次运行的以下输出:
⇒ time ./test-grep-and-rafind2
# ..snip..
./test-grep-and-rafind2 7.33s user 0.19s system 99% cpu 7.578 total
⇒ time ./test-find-and-rafind2
# ..snip..
./test-find-and-rafind2 3.24s user 0.72s system 98% cpu 4.041 total
⇒ time ./test-fd-and-rafind2
# ..snip..
./test-fd-and-rafind2 3.85s user 1.04s system 488% cpu 1.002 total