2012-02-26 11 views
9

ho binario e desidera estrarre parte di esso, a partire da un know stringa di byte (cioè FF D8 FF D0) e termina con nota stringa di byte (AF FF D9)Come scaricare parte del file binario

Nel passato ho usato dd per tagliare parte del file binario dall'inizio/fine ma questo comando non sembra supportare quello che chiedo.

Quale strumento sul terminale può eseguire questa operazione?

risposta

3

In un unico 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 

L'idea è quella di utilizzare awk tra due xxd per selezionare la parte del file necessaria. Una volta trovato il primo modello, awk stampa i byte fino a quando viene trovato il secondo modello ed esce.

Il caso in cui viene trovato il primo schema ma il secondo non deve essere preso in considerazione. Viene eseguito nella parte END dello script awk, che restituisce uno stato di uscita diverso da zero. Questo è catturare da bash 's ${PIPESTATUS[1]} dove ho deciso di eliminare il nuovo file.

Si noti che un file vuoto significa anche che non è stato trovato nulla.

+0

Ancora un altro riassegnazione del marchio - la soluzione di lOranger fallisce se il 2 ° modello può essere trovato prima del 1 ° - dando $ len con segno negativo. Questa soluzione ricerca dopo la prima corrispondenza del modello, quindi non presenta tale problema né genera file di dimensioni triple intermedie. – theta

+0

Dopo aver testato di più, l'ho trovato senza problemi, ma è piuttosto lento su file di grandi dimensioni. Qualcuno vede un posto per qualche ottimizzazione, o questo è il migliore che si possa ottenere da xxd/awk? – theta

+0

Prova la nuova versione 'sed' che ho appena postato. Questo può essere ottimizzato sostituendo la concatenazione delle stringhe e l'estrazione con gli indici rotanti negli array, ma è meno leggibile; e non voglio farlo se non necessario ;-). – jfg956

7

Individuare la posizione iniziale/finale, quindi estrarre l'intervallo.

$ 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

Ho trovato "..count = $ ((0x9590-0xcb0 + 2)) skip = $ ((0xcb0 + 1)) ..." corrisponde esattamente a partire da "FFD8 .." e termina con "AFFF. . ". Grazie per la tua bella procedura. Cheers – theta

+1

Dopo un paio di estrazioni ho notato che questa è solo una soluzione approssimativa. +1, +2 dipendono tutti dal contenuto. Ad esempio, '007d820: 74290068656c6c6f2e6a706700ffd8ff' fornisce 007d820 sia per '74 29 00 68' che '00 ff d8 ff ', quindi qualcosa di leggermente diverso deve essere fatto – theta

+1

Questo * non funziona *. Se il pattern da abbinare è diviso su due righe di output 'xxd', non verrà mai trovato (per default' xxd -g0' linee di gruppo per 16 byte). Per un pattern di 4 byte la probabilità di avere una divisione è del 25%. Inoltre, 'grep | awk' stamperà l'indirizzo * all'inizio * della riga in cui si verifica il pattern, quindi può verificarsi un delta di dimensioni fino a una linea, si ottengono più dati di quelli che si desidera veramente. –

1

Vedi this link di un modo per fare grep binario. Una volta che hai l'offset iniziale e finale, dovresti essere in grado di ottenere dd per ottenere ciò di cui hai bisogno.

2

Questo dovrebbe funzionare con gli strumenti standard (xxd, tr, grep, awk, dd). Gestisce correttamente il problema del "pattern split across", inoltre cerca il pattern allineato solo allo sfasamento dei byte (non nibble).

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: Lo script precedente utilizzo di un file temporaneo per evitare di dover la conversione binaria> hex due volte. Un compromesso spazio/tempo consiste nel convogliare il risultato di xxd direttamente nei due grep. È anche possibile un rivestimento singolo, a scapito della chiarezza.

Uno potrebbe anche utilizzare tee e named pipe per evitare di dover memorizzare un file temporaneo e convertire l'output due volte, ma non sono sicuro che sarebbe più veloce (xxd è veloce) ed è sicuramente più complesso da scrivere.

+0

lOranger, ho usato -c64 per compensare un bit, e 'cut' e' sed' per calcolare l'indirizzo corretto, ma -c1 dovrebbe essere una soluzione reale. Segnerò la tua soluzione, ma quando riuscirò a farlo funzionare. Per prima cosa dovevo cambiare il pattern e il nome di 'grep' per fare in modo che grep funzionasse, ma a prescindere ricevessi' dd: numero errato' Immagino un problema nel calcolo di avvio/len/grammatica. Inoltre non possiamo escludere lo spazio vuoto e salvare 1/3 del file .hex di output che sarebbe il doppio della dimensione del file di input, invece tripla come lo è ora? – theta

+0

Spiacente, c'è stato un errore di battitura nello script: 'grep' dovrebbe essere * prima * il nome del file. Ho anche aggiunto un '| testa -1' per coprire il caso in cui il modello appare più volte nell'input, cosa che può accadere. Per quanto riguarda la tua domanda, lo spazio tra i byte esadecimali è necessario, altrimenti hai il problema "nibble" (il pattern non è allineato sui limiti dei byte). –

+0

Ho paura che non funzioni ancora. Ottengo il file di input come risultato. Ho usato il mio script -c64 e mi sono aspettato il dump, ma non volevo postarlo qui poiché era fragile sui limiti (meglio di quanto previsto, ma comunque ..) – theta

1

Una variazione sulla soluzione awk che presuppone che il file binario, una volta convertito in esadecimale con gli spazi, si inserisce in 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, è così dolce e sembra così facile. Non potrebbe essere migliore di questo. Lascerò il segno sulla risposta di IOranger poiché è corretto e ho risposto in precedenza, ma questo è di gran lunga il mio frammento preferito – theta

+0

Peccato che il più veloce ottenga il marchio, non il più breve ... Comunque, può ancora essere ottimizzato rimuovendo il ' tr', sostituendolo in 'sed' con' -e '1h' -e '2, $ H' -e '$ {x; s/\ n// g}' 'e modificando la sostituzione di cui sopra da eseguire solo sull'ultima riga. Nota che questa soluzione non funziona con un enorme file binario, dato che il file deve essere messo in memoria in 'sed'. Su file enormi, usa la soluzione 'awk'. – jfg956

+0

Grazie. L'ho provato su un laptop da 1 GB, ed era perfetto per 5MB, ma ha reso il mio sistema inaccessibile su un file da 50MB. C'è forse qualche regola generale per determinare la dimensione del file "limite" in base alla RAM disponibile, secondo lei? – theta

1

Un'altra soluzione in sed, ma utilizzando meno 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 

Il 1 ° sed stampa da ff d8 ff d0 fino alla fine del file. Si noti che è necessario il numero N in -e '1{N;N;N}' in quanto vi sono byte nel 1o modello meno uno.

Il 2 ° sed stampa dall'inizio del file su aa ff d9. Nota ancora che hai bisogno di più N in -e '1{N;N}' dato che ci sono byte nel tuo secondo modello meno uno.

Ancora una volta, è necessario un test per verificare se il 2 ° modello è stato trovato ed eliminare il file se non lo è.

Si noti che il comando Q è un'estensione GNU su sed. Se non ce l'hai, devi cestinare il resto del file una volta che il modello è stato trovato (in un ciclo come il 1o sed, ma non stampare il file), e controlla dopo la conversione esadecimale in binario che il nuovo file finisce con modello di wright.

+0

Ho questa estensione GNU per sed, ma non posso far funzionare questo script per qualche motivo – theta

+0

Qual è l'errore/problema/...? – jfg956

+0

Spiacente, errore di battitura nel secondo 'sed': dovrebbe funzionare se si sostituisce'/aa \ nff \ nd9/'con'/af \ nff \ nd9/'. – jfg956

Problemi correlati