私が持っているファイルから特定のバイトシーケンスが発生する回数を数えたいです。たとえば、\0xdeadbeef
実行可能ファイルにこの数が何回表示されるかを知りたいと思います。今はgrepを使ってこれをやっています。
#/usr/bin/fish
grep -c \Xef\Xbe\Xad\Xde my_executable_file
(私のCPUはリトルエンディアンなので、バイトは逆順に書き込まれます)
しかし、私のアプローチには2つの問題があります。
- これらの
\Xnn
エスケープシーケンスは魚の殻でのみ機能します。 - grepは実際に私のマジックナンバーを含む行数を計算します。パターンが同じ行に 2 回表示されると、1 回だけカウントされます。
これらの問題を解決する方法はありますか? Bashシェルでこの行を実行し、ファイル内でこのパターンが発生する回数を正確に数えるにはどうすればよいですか?
ベストアンサー1
要求された1行のソリューションは次のとおりです(「プロセスの置き換え」を含む最近のシェルの場合)。
grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l
使用可能な「プロセスの交換」がない場合は、<(…)
grep をフィルターとして使用します。
hexdump -v -e '/1 "%02x "' infile.bin | grep -o "ef be ad de" | wc -l
以下は、ソリューションの各部分の詳細な説明です。
16進数のバイト値:
最初の問題は簡単に解決できます。
これらの \Xnn エスケープシーケンスは Fish シェルでのみ有効です。
X
上を下に変更x
し、printfを使用します(ほとんどのシェルについて):
$ printf -- '\xef\xbe\xad\xde'
または以下を使用してください。
$ /usr/bin/printf -- '\xef\xbe\xad\xde'
'\x' 表現を実装しないことを選択したシェルの場合。
もちろん、16進数を8進数に変換することは(ほぼ)すべてのシェルで機能します。
$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'
ここで、「$sh」は(合理的な)シェルです。しかし、これを正確に引用することは非常に困難です。
バイナリファイル。
0x0A
最も信頼できる解決策は、ファイルとバイトシーケンス(両方)を(new line)や(null byte)などの奇数文字値に問題のないいくつかのエンコーディングに変換することです0x00
。 「テキストファイル」を処理するように設計され、適用されたツールを使用して適切に管理することは非常に困難です。
base64のような変換は有効なように見えるかもしれませんが、各入力バイトが最初、最初の2バイト、または3バイトのmod 24(ビット)位置に応じて最大3つの出力表現を持つことができるという問題が発生します。
$ echo "abc" | base64
YWJjCg==
$ echo "-abc" | base64
LWFiYwo=
$ echo "--abc" | base64
LS1hYmMK
$ echo "---abc" | base64 # Note that YWJj repeats.
LS0tYWJjCg==
16進変換。
これが、最も強力な変換が単純な16進表現のように、すべてのバイト境界で始まる変換でなければならない理由です。
次のいずれかのツールを使用して、ファイルを16進数で表したファイルをインポートできます。
$ od -vAn -tx1 infile.bin | tr -d '\n' > infile.hex
$ hexdump -v -e '/1 "%02x "' infile.bin > infile.hex
$ xxd -c1 -p infile.bin | tr '\n' ' ' > infile.hex
この場合、検索するバイトシーケンスはすでに16進数です。
:
$ var="ef be ad de"
しかし、変形も可能です。往復16進数-2進数-16進数の例は次のとおりです。
$ echo "ef be ad de" | xxd -p -r | od -vAn -tx1
ef be ad de
検索文字列はバイナリ表現に従って設定できます。上記の3つのオプション、od、hexdump、またはxxdはすべて同じです。一致がバイト境界にあることを確認するには、スペースを含める必要があります(ニブルシフトは許可されていません)。
$ a="$(printf "\xef\xbe\xad\xde" | hexdump -v -e '/1 "%02x "')"
$ echo "$a"
ef be ad de
バイナリが次の場合:
$ cat infile.bin | xxd
00000000: 5468 6973 2069 7320 efbe adde 2061 2074 This is .... a t
00000010: 6573 7420 0aef bead de0a 6f66 2069 6e70 est ......of inp
00000020: 7574 200a dead beef 0a66 726f 6d20 6120 ut ......from a
00000030: 6269 0a6e 6172 7920 6669 6c65 2e0a 3131 bi.nary file..11
00000040: 3232 3131 3232 3131 3232 3131 3232 3131 2211221122112211
00000050: 3232 3131 3232 3131 3232 3131 3232 3131 2211221122112211
00000060: 3232 0a
これにより、単純なgrep検索で一致するシーケンスのリストが表示されます。
$ grep -o "$a" infile.hex | wc -l
2
行?
これはすべて1行で実行できます。
$ grep -o "ef be ad de" <(xxd -c 1 -p infile.bin | tr '\n' ' ') | wc -l
たとえば、11221122
同じファイル内で検索するには、次の2つの手順が必要です。
$ a="$(printf '11221122' | hexdump -v -e '/1 "%02x "')"
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ') | wc -l
4
一致を「表示」するには:
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
3131323231313232
3131323231313232
3131323231313232
3131323231313232
$ grep "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
… 0a3131323231313232313132323131323231313232313132323131323231313232313132320a
バッファー
grepがファイル全体をバッファリングしてファイルが大きい場合、コンピュータに大きな負荷を与える可能性があります。これには、バッファリングされていない sed ソリューションを使用できます。
a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin |
sed -ue 's/\('"$a"'\)/\n\1\n/g' |
sed -n '/^'"$a"'$/p' |
wc -l
最初のsedはバッファリングされず、(-u
)一致する各文字列に対してストリームに2つの改行を挿入します。 2番目は、sed
(短い)一致する行だけを印刷します。 wc -l は一致する行数を計算します。
これにより、短い行だけがバッファリングされます。 2 番目の sed の一致する文字列です。ここで使用されるリソースはかなり低くなければなりません。
あるいは、理解するのは少し複雑ですが、sedでも同じアイデアを持っています。
a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin |
sed -u '/\n/P;//!s/'"$a"'/\n&\n/;D' |
wc -l