2012-02-26 26 views
9

tengo binario y quiero extraer parte de ella, a partir de conocimientos cadena de bytes (es decir, FF D8 FF D0) y terminando con la cadena de bytes conocida (FA FF D9)Cómo volcar parte del archivo binario

En el pasado, he usado dd para cortar parte del archivo binario desde el principio/final, pero este comando no parece ser compatible con lo que pido.

¿Qué herramienta en el terminal puede hacer esto?

Respuesta

3

En un solo tubo:

xxd -c1 -p file | 
    awk -v b="ffd8ffd0" -v e="aaffd9" ' 
    found == 1 { 
     print $0 
     str = str $0 
     if (str == e) {found = 0; exit} 
     if (length(str) == length(e)) str = substr(str, 3)} 
    found == 0 { 
     str = str $0 
     if (str == b) {found = 1; print str; str = ""} 
     if (length(str) == length(b)) str = substr(str, 3)} 
    END{ exit found }' | 
    xxd -r -p > new_file 
test ${PIPESTATUS[1]} -eq 0 || rm new_file 

La idea es utilizar awk entre dos xxd para seleccionar la parte del archivo que se necesita. Una vez que se encuentra el primer patrón, awk imprime los bytes hasta que se encuentre el 2do patrón y salga.

Se debe tener en cuenta el caso donde se encuentra el primer patrón pero el segundo no es. Se realiza en la parte END del script awk, que devuelve un estado de salida distinto de cero. Esta es captura por bash 's ${PIPESTATUS[1]} donde decidí eliminar el nuevo archivo.

Tenga en cuenta que en el archivo vacío también significa que no se ha encontrado nada.

+0

Sin embargo, otra reasignación de marca: la solución de 'Irager' falla si se puede encontrar el 2 ° patrón antes del 1 °, dando $ len con signo negativo. Esta solución busca después de la coincidencia del primer patrón, por lo que no tiene ese problema ni genera un archivo intermedio de tamaño triple. – theta

+0

Después de probar esto más, lo encontré sin problemas, pero es bastante lento en archivos más grandes. ¿Alguien ve un lugar para alguna optimización, o este es el mejor que se puede obtener de xxd/awk? – theta

+0

Pruebe la nueva versión 'sed' que acabo de publicar. Éste se puede optimizar reemplazando la concatenación y extracción de cadenas con índices rotatorios en matrices, pero es menos legible; y no quiero hacerlo si no es necesario ;-). – jfg956

7

Ubique la posición inicial/final, luego extraiga el rango.

$ xxd -g0 input.bin | grep -im1 FFD8FFD0 | awk -F: '{print $1}' 
0000cb0 
$ ^FFD8FFD0^AFFFD9^ 
0009590 
$ dd ibs=1 count=$((0x9590-0xcb0+1)) skip=$((0xcb0)) if=input.bin of=output.bin 
+0

Encontré "..count = $ ((0x9590-0xcb0 + 2)) skip = $ ((0xcb0 + 1)) ..." para coincidir exactamente desde "FFD8 .." y terminando en "AFFF". . ". Gracias por su buen procedimiento. Cheers – theta

+1

Después de un par de extracciones noté que esta es solo una solución aproximada. +1, +2 todos dependen del contenido. Por ejemplo '007d820: 74290068656c6c6f2e6a706700ffd8ff' da 007d820 para '74 29 00 68 'y '00 ff d8 ff', por lo que debe hacerse algo ligeramente diferente – theta

+1

Esto * no funciona *. Si el patrón a coincidir se divide en dos líneas de salida 'xxd', nunca se encontrará (por defecto' xxd -g0' líneas de grupo por 16 bytes). Para un patrón de 4 bytes de longitud, la probabilidad de tener una división es del 25%. Además, el 'grep | awk' imprimirá la dirección del * comienzo * de la línea donde se produce el patrón, por lo que puede ocurrir un delta de hasta el tamaño de línea, usted termina con más datos de los que realmente desea. –

1

Ver this link una manera de hacer grep binario. Una vez que tenga el desplazamiento inicial y final, debe poder obtener dd para obtener lo que necesita.

2

Esto debería funcionar con las herramientas estándar (xxd, tr, grep, awk, dd). Esto maneja correctamente el problema de "patrón dividido entre líneas", también busca el patrón alineado solo en el desplazamiento de bytes (no en mordisco).

file=<yourfile> 
outfile=<youroutputfile> 
startpattern="ff d8 ff d0" 
endpattern="af ff d9" 
xxd -g0 -c1 -ps ${file} | tr '\n' ' ' > ${file}.hex 
start=$((($(grep -bo "${startpattern}" ${file}.hex\ 
    | head -1 | awk -F: '{print $1}')-1)/3)) 
len=$((($(grep -bo "${endpattern}" ${file}.hex\ 
    | head -1 | awk -F: '{print $1}')-1)/3-${start})) 
dd ibs=1 count=${len} skip=${start} if=${file} of=${outfile} 

Nota: El script anterior uso de un archivo temporal para evitar tener la conversión binaria> hexagonal dos veces. Una compensación de espacio/tiempo es canalizar el resultado de xxd directamente en los dos grep. También es posible un trazador de líneas, a expensas de la claridad.

También se podría usar tee y named pipe para evitar tener que almacenar un archivo temporal y convertir el resultado dos veces, pero no estoy seguro de que sea más rápido (xxd es rápido) y ciertamente es más complejo de escribir.

+0

lOrange, utilicé -c64 para compensar un poco, y 'corte' y' sed' para calcular la dirección correcta, pero -c1 debería ser una solución real. Marcaré su solución, pero cuando logro hacerlo funcionar. Primero necesitaba cambiar el lugar del patrón y el nombre de archivo de 'grep' para hacer que grep funcionara, pero independientemente de que obtuviera' dd: número inválido' Imagino un problema en el inicio/cálculo de len/gramática. Además, ¿no podemos excluir el espacio vacío y guardar 1/3 del archivo .hex de salida que sería el doble del tamaño del archivo de entrada en lugar de triple como lo es ahora? – theta

+0

Disculpa, había un error tipográfico en el guión: el patrón 'grep' debería ser * antes * del nombre del archivo. También agregué un '| head -1' para cubrir el caso donde el patrón aparece varias veces en la entrada, lo que puede suceder. Con respecto a su pregunta, el espacio entre los bytes hexadecimales es necesario; de lo contrario, tiene el problema de "mordisquear" (el patrón no está alineado en los límites de los bytes). –

+0

Me temo que todavía no funciona. Obtengo el archivo de entrada como resultado. Utilicé mi script -c64 y obtuve el dump esperado, pero no estaba dispuesto a publicarlo aquí porque era frágil en los límites (mejor que el proporcionado, pero aún así ...) – theta

1

Una variante de la solución awk que asume que el archivo binario, una vez convertidos en hexadecimal con espacios, cabe en la memoria:

xxd -c1 -p file | 
    tr "\n" " " | 
    sed -n -e 's/.*\(ff d8 ff d0.*aa ff d9\).*/\1/p' | 
    xxd -r -p > new_file 
+0

WOW, esto es tan dulce y parece tan fácil. No podría ser mejor que esto. Dejaré la marca en la respuesta de IOranger ya que es correcta y respondí antes, pero este es, de lejos, mi fragmento favorito – theta

+0

Lástima que el más rápido obtenga la marca, no el más corto ... De todos modos, todavía puede optimizarse quitando el ' tr', reemplazándolo dentro de 'sed' por' -e '1h' -e '2, $ H' -e '$ {x; s/\ n// g}' 'y modificando la sustitución anterior para que se realice solo en la última línea. Tenga en cuenta que esta solución no funciona con grandes archivos binarios, ya que el archivo debe colocarse en la memoria en 'sed'. En archivos grandes, use la solución 'awk'. – jfg956

+0

Gracias. Probé esto en una computadora portátil de 1GB, y estaba bien para archivos de 5MB, pero hizo que mi sistema fuera inaccesible en un archivo de 50MB. ¿Existe alguna regla general para determinar el tamaño del archivo "límite" en función de la memoria RAM disponible, en su opinión? – theta

1

Otra solución en sed, pero utilizando menos memoria:

xxd -c1 -p file | 
    sed -n -e '1{N;N;N}' -e '/ff\nd8\nff\nd0/{:begin;p;s/.*//;n;bbegin}' -e 'N;D' | 
    sed -n -e '1{N;N}' -e '/aa\nff\nd9/{p;Q1}' -e 'P;N;D' | 
    xxd -r -p > new_file 
test ${PIPESTATUS[2]} -eq 1 || rm new_file 

La primera sed imprime desde ff d8 ff d0 hasta el final del archivo. Tenga en cuenta que necesita tantos N en -e '1{N;N;N}', ya que hay bytes en su primer patrón menos uno.

La 2da sed imprime desde el comienzo del archivo a aa ff d9. Tenga en cuenta nuevamente que necesita tantos N en -e '1{N;N}' como hay bytes en su segundo patrón menos uno.

De nuevo, se necesita una prueba para comprobar si se encuentra el 2º patrón, y eliminar el archivo si no lo está.

Tenga en cuenta que el comando Q es una extensión de GNU a sed. Si no lo tiene, debe trash el resto del archivo una vez que se encuentre el patrón (en un ciclo como el 1er sed, pero no imprimir el archivo), y verifique después de la conversión de hexadecimal a binario que el archivo_nuevo termine con el patrón de wright.

+0

Tengo esta extensión de GNU para sed, pero no puedo hacer que este script funcione por alguna razón – theta

+0

¿Cuál es el error/problema/...? – jfg956

+0

Lo sentimos, error en el 2do 'sed': debería funcionar si reemplaza'/aa \ nff \ nd9/'con'/af \ nff \ nd9/'. – jfg956

Cuestiones relacionadas