web-dev-qa-db-ja.com

Linuxのバイナリgrep?

次のバイナリファイルを生成したとします。

_# generate file:
python -c 'import sys;[sys.stdout.write(chr(i)) for i in (0,0,0,0,2,4,6,8,0,1,3,0,5,20)]' > mydata.bin

# get file size in bytes
stat -c '%s' mydata.bin

# 14
_

そして、grepのような構文を使用して、すべてゼロ(_0x00_)の場所を見つけたいと言います。

私がこれまでにできる最善のことは:

_$ hexdump -v -e "1/1 \" %02x\n\"" mydata.bin | grep -n '00'

1: 00
2: 00
3: 00
4: 00
9: 00
12: 00
_

ただし、これにより、元のバイナリファイルの各バイトが暗黙的にマルチバイトに変換されますASCII表現、その上でgrepが機能します。最適化の主な例ではありません)

Linux用のバイナリgrepのようなものはありますか?おそらく、また、正規表現のような構文をサポートするものだけでなく、バ​​イトの「文字」もサポートします。つまり、「a(\x00*)b」のようなものを記述して、「0回以上」のバイトの出現に一致させることができますバイト 'a'(97)と 'b'(98)の間の0?

編集:コンテキストは、8ビットデータをキャプチャするドライバーに取り組んでいるということです。キロバイトからメガバイトまでのデータで何か問題が発生したため、特定の署名とその場所を確認したいと思います。 (これまでのところ、私はキロバイトのスニペットを使用しているので、最適化はそれほど重要ではありません-しかし、メガバイトの長いキャプチャでエラーが発生し始め、それらを分析する必要がある場合、私の推測はもっと最適化したいのですが:)。特に、1バイトを文字として「grep」できるものを希望します-hexdumpは、バイトごとに文字列を検索するように強制します

EDIT2:同じ質問、別のフォーラム:) バイナリファイルのバイトシーケンスを取得

EDIT3:@tchristの回答のおかげで、ここにも「grepping」と一致の例があり、結果(を表示していますが、OP):

_$ Perl -ln0777e 'print unpack("H*",$1), "\n", pos() while /(.....\0\0\0\xCC\0\0\0.....)/g' /path/to/myfile.bin

ca000000cb000000cc000000cd000000ce     # Matched data (hex)
66357                                  # Offset (dec)
_

一致したデータをそれぞれ1バイト(2つの16進文字)としてグループ化するには、一致した文字列のバイト数に応じて「H2 H2 H2 ...」を指定する必要があります。私のマッチ '_.....\0\0\0\xCC\0\0\0....._'は17バイトをカバーしているので、Perlで '_"H2"x17_'と書くことができます。これらの「H2」のそれぞれは、(リストのように)個別の変数を返すため、joinもそれらの間にスペースを追加するために使用する必要があります-最終的に:

_$ Perl -ln0777e 'print join(" ", unpack("H2 "x17,$1)), "\n", pos() while /(.....\0\0\0\xCC\0\0\0.....)/g' /path/to/myfile.bin

ca 00 00 00 cb 00 00 00 cc 00 00 00 cd 00 00 00 ce
66357
_

まあ..確かにPerlは非常に素晴らしい「バイナリgrepping」機能であり、私は認めなければなりません:)構文を適切に学ぶ限り:)

28
sdaau

ワンライナー入力

短いワンライナーバージョンは次のとおりです。

% Perl -ln0e 'print tell' < inputfile

そして、ここに少し長いワンライナーがあります:

% Perl -e '($/,$\) = ("\0","\n"); print tell while <STDIN>' < inputfile

これら2つのワンライナーを接続する方法は、最初のプログラムをアンコンパイルすることです。

% Perl -MO=Deparse,-p -ln0e 'print tell'
BEGIN { $/ = "\000"; $\ = "\n"; }
LINE: while (defined(($_ = <ARGV>))) {
    chomp($_);
    print(tell);
}

プログラムされた入力

コマンドラインから呼び出すのではなく、ファイルに入れたい場合は、より明確なバージョンを次に示します。

#!/usr/bin/env Perl

use English qw[ -no_match_vars ];

$RS  = "\0";    # input  separator for readline, chomp
$ORS = "\n";    # output separator for print

while (<STDIN>) {
    print tell();
}

そして、これは本当に長いバージョンです:

#!/usr/bin/env Perl

use strict;
use autodie;  # for Perl5.10 or better
use warnings qw[ FATAL all  ];

use IO::Handle;

IO::Handle->input_record_separator("\0");
IO::Handle->output_record_separator("\n");

binmode(STDIN);   # just in case

while (my $null_terminated = readline(STDIN)) {
    # this just *past* the null we just read:
    my $seek_offset = tell(STDIN);
    print STDOUT $seek_offset;  

}

close(STDIN);
close(STDOUT);

ワンライナー出力

ところで、テスト入力ファイルを作成するために、私はあなたの大きな長いPythonスクリプトを使用しませんでした。私はこの単純なPerlワンライナーを使用しました:

% Perl -e 'print 0.0.0.0.2.4.6.8.0.1.3.0.5.20' > inputfile

Perlは、同じ仕事をするのにPythonよりも2〜3倍短くなることがよくあります。そして、明快さについて妥協する必要はありません。 -ライナーは上?

プログラムされた出力

分かった分かった。まだ言語がわからない場合は、もっとわかりやすいかもしれません。

#!/usr/bin/env Perl
@values = (
    0,  0,  0,  0,  2,
    4,  6,  8,  0,  1,
    3,  0,  5, 20,
);
print pack("C*", @values);

これも機能しますが:

print chr for @values;

同様に

print map { chr } @values;

すべてを厳格で注意深く好きな人にとっては、これはあなたが見るものよりも多くなるかもしれません:

#!/usr/bin/env Perl

use strict;
use warnings qw[ FATAL all ];
use autodie;

binmode(STDOUT);

my @octet_list = (
    0,  0,  0,  0,  2,
    4,  6,  8,  0,  1,
    3,  0,  5, 20,
);

my $binary = pack("C*", @octet_list);
print STDOUT $binary;

close(STDOUT); 

TMTOWTDI

Perlは、最も快適な方法を選択できるように、複数の方法をサポートしています。これが学校や仕事のプロジェクトとしてチェックインする予定だった場合は、より長く、より注意深いバージョンを選択するか、少なくとも1行を使用している場合は、シェルスクリプトにコメントを付けます。

自分のシステムでPerlのドキュメントを見つけることができます。入力するだけ

% man Perl
% man perlrun
% man perlvar
% man perlfunc

シェルプロンプトなどで。かわりにWebでかなり風変わりなバージョンが必要な場合は、 Perlperlrunperlvar 、および perlfunchttp://perldoc.Perl.org から。

14
tchrist

これは私にとってはうまくいくようです:

grep --only-matching --byte-offset --binary --text --Perl-regexp "<\x-hex pattern>" <file>

ショートフォーム:

grep -obUaP "<\x-hex pattern>" <file>

例:

grep -obUaP "\x01\x02" /bin/grep

出力( Cygwin バイナリ):

153: <\x01\x02>
33210: <\x01\x02>
53453: <\x01\x02>

したがって、これを再度grepしてオフセットを抽出できます。ただし、再度バイナリモードを使用することを忘れないでください。

44
Fr0sT

他の誰かが同様にイライラしており、それを行うための独自のツール(または少なくとも同様の何か)を作成したようです: bgrep

19
David Dean

bbe プログラムは、バイナリファイル用の sed のようなエディタです。 ドキュメント を参照してください。

bbeの例:

bbe -b "/\x00\x00\xCC\x00\x00\x00/:17" -s -e "F d" -e "p h" -e "A \n" mydata.bin

11:x00 x00 xcc x00 x00 x00 xcd x00 x00 x00 xce

説明

-b search pattern between //. each 2 byte begin with \x (hexa notation).
   -b works like this /pattern/:length (in byte) after matched pattern
-s similar to 'grep -o' suppress unmatched output 
-e similar to 'sed -e' give commands
-e 'F d' display offsets before each result here: '11:'
-e 'p h' print results in hexadecimal notation
-e 'A \n' append end-of-line to each result

sedにパイプして、よりクリーンな出力にすることもできます。

bbe -b "/\x00\x00\xCC\x00\x00\x00/:17" -s -e "F d" -e "p h" -e "A \n" mydata.bin | sed -e 's/x//g'

11:00 00 cc 00 00 00 cd 00 00 00 ce

EDIT3からのPerlを使用した解決策では、大きなファイルで「メモリ不足」エラーが発生します。

bgrepでも同じ問題が発生します。

Bbeの唯一の欠点は、一致したパターンの前にあるコンテキストを印刷する方法がわからないことです。

10
hdorio

Grepのみを使用して当面の問題を解決する1つの方法は、単一のnullバイトを含むファイルを作成することです。その後、 grep -abo -f null_byte_file target_fileは次の出力を生成します。

 0:
 1:
 2:
 3:
 8:
 11:

もちろん、 "-b"で要求された各バイトオフセットの後に "-o"で要求されたnullバイトが続きます。

私は最初にPerlを提唱しますが、この場合、大家族を連れてくる必要はありません。

8
Omniwombat

grep -a?本当にバイナリファイルでどのように機能するかはわかりませんが、OSがバイナリであると考えるテキストファイルでうまく機能します。

1
Chance