¿Grep binario en Linux?
binary (6)
Digamos que he generado el siguiente archivo binario:
# 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
Y digamos, quiero encontrar las ubicaciones de todos los ceros ( 0x00
), usando una sintaxis grepiforme.
Lo mejor que puedo hacer hasta ahora es:
$ hexdump -v -e "1/1 /" %02x/n/"" mydata.bin | grep -n ''00''
1: 00
2: 00
3: 00
4: 00
9: 00
12: 00
Sin embargo, esto convierte implícitamente cada byte en el archivo binario original en una representación ASCII de varios bytes, en la que grep
opera; no es exactamente el mejor ejemplo de optimización :)
¿Hay algo así como un grep
binario para Linux? Posiblemente, también, algo que admitiría una sintaxis similar a una expresión regular, pero también para "caracteres" de byte, es decir, podría escribir algo como '' a(/x00*)b
'' y hacer coincidir ''cero o más'' apariciones de byte 0 entre los bytes ''a'' (97) y ''b'' (98)?
EDITAR: El contexto es que estoy trabajando en un controlador, donde capturo datos de 8 bits; algo va mal en los datos, que pueden ser kilobytes hasta megabytes, y me gustaría verificar firmas particulares y dónde ocurren. ( Hasta ahora, estoy trabajando con fragmentos de kilobytes, por lo que la optimización no es tan importante, pero si empiezo a obtener algunos errores en megabytes de capturas largas, y tengo que analizarlos, supongo que me gustaría algo más optimizado :). Y especialmente, me gustaría algo donde pueda "grep" para un byte como personaje - hexdump
me obliga a buscar cadenas por byte )
EDIT2: misma pregunta, foro diferente :) grepping a través de un archivo binario para una secuencia de bytes
EDIT3: Gracias a la respuesta de @tchrist, aquí también hay un ejemplo con ''grepping'' y matching, y visualización de resultados ( aunque no es exactamente la misma pregunta que 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)
Para que los datos coincidentes se agrupen como un byte (dos caracteres hexadecimales) cada uno, entonces se debe especificar "H2 H2 H2 ..." para tantos bytes hay en la cadena coincidente; como mi coincidencia '' ...../0/0/0/xCC/0/0/0.....
'' cubre 17 bytes, puedo escribir '' "H2"x17
'' "H2"x17
''en Perl. Cada uno de estos "H2" devolverá una variable separada (como en una lista), por lo que también se debe usar para agregar espacios entre ellos, eventualmente:
$ 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
Bueno ... de hecho Perl es una muy buena instalación de "binary grepping", debo admitirlo :) Siempre y cuando uno aprenda correctamente la sintaxis :)
Entrada de una sola línea
Aquí está la versión más corta de una línea:
% perl -ln0e ''print tell'' < inputfile
Y aquí hay un trazador de líneas un poco más largo:
% perl -e ''($/,$/) = ("/0","/n"); print tell while <STDIN>'' < inputfile
La forma de conectar esos dos lineamientos es mediante la compilación del programa del primero:
% perl -MO=Deparse,-p -ln0e ''print tell''
BEGIN { $/ = "/000"; $/ = "/n"; }
LINE: while (defined(($_ = <ARGV>))) {
chomp($_);
print(tell);
}
Entrada programada
Si desea poner eso en un archivo en lugar de llamarlo desde la línea de comando, aquí hay una versión algo más explícita:
#!/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();
}
Y aquí está la versión realmente larga:
#!/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);
Salida de una sola línea
Por cierto, para crear el archivo de entrada de prueba, no usé su secuencia de comandos de Python grande y larga; Acabo de usar este simple Perl de una sola línea:
% perl -e ''print 0.0.0.0.2.4.6.8.0.1.3.0.5.20'' > inputfile
Descubrirá que Perl a menudo termina siendo 2-3 veces más corto que Python para hacer el mismo trabajo. Y no tienes que comprometer la claridad; ¿Qué podría ser más simple que el delineador de arriba?
Salida programada
Sé que sé. Si aún no conoce el idioma, esto podría ser más claro:
#!/usr/bin/env perl
@values = (
0, 0, 0, 0, 2,
4, 6, 8, 0, 1,
3, 0, 5, 20,
);
print pack("C*", @values);
aunque esto también funciona:
print chr for @values;
al igual que
print map { chr } @values;
Aunque para aquellos a los que les gusta todo todo riguroso y cuidadoso y todo, esto podría ser más de lo que verías:
#!/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 admite más de una forma de hacer las cosas para que puedas elegir la que te resulte más cómoda. Si esto fuera algo que planeara registrar como proyecto de escuela o trabajo, ciertamente seleccionaría las versiones más largas y más cuidadosas, o al menos incluiría un comentario en el guión de shell si estuviera usando las frases únicas.
Puede encontrar documentación para Perl en su propio sistema. Sólo tipo
% man perl
% man perlrun
% man perlvar
% man perlfunc
etc. en su intérprete de comandos de shell. Si desea versiones más bonitas en la web, obtenga las páginas de manual de perl , perlrun , perlvar y perlfunc de http://perldoc.perl.org .
¿Qué pasa con grep -a
? No estoy seguro de cómo funciona en los archivos verdaderamente binarios, pero funciona bien en los archivos de texto que el sistema operativo cree que es binario.
Una forma de resolver su problema inmediato usando grep es crear un archivo que contenga un único byte nulo. Después de eso, grep -abo -f null_byte_file target_file
producirá el siguiente resultado.
0: 1: 2: 3: 8: 11:
Eso es, por supuesto, cada compensación de bytes según lo solicitado por "-b" seguido de un byte nulo según lo solicitado por "-o"
Yo sería el primero en abogar por Perl, pero en este caso no hay necesidad de traer a la familia extendida.
Esto parece funcionar para mí:
grep --only-matching --byte-offset --binary --text --perl-regexp "</x-hex pattern>" <file>
Forma corta:
grep -obUaP "</x-hex pattern>" <file>
Ejemplo:
grep -obUaP "/x01/x02" /bin/grep
Salida ( Cygwin binary):
153: </x01/x02>
33210: </x01/x02>
53453: </x01/x02>
Entonces puede grep esto otra vez para extraer compensaciones. Pero no olvides usar el modo binario nuevamente.
Alguien más parece haber sido frustrado de manera similar y escribió su propia herramienta para hacerlo (o al menos algo similar): bgrep .
El programa bbe es un editor sed -like para archivos binarios. Ver documentación .
Ejemplo con 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
Explicación
-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
También puede canalizarlo a sed para tener una salida más limpia:
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
Su solución con Perl de su EDIT3 me da un error de ''Falta de memoria'' con archivos de gran tamaño.
El mismo problema va con bgrep .
La única desventaja de bbe es que no sé cómo imprimir el contexto que precede a un patrón coincidente.