Come scaricare un file binario come letterale stringa C / C ++?


39

Ho un file binario che vorrei includere nel mio codice sorgente C (temporaneamente, a scopo di test), quindi vorrei ottenere il contenuto del file come una stringa C, qualcosa del genere:

\x01\x02\x03\x04

Questo è possibile, forse utilizzando le utilità odo hexdump? Sebbene non sia necessario, se la stringa può passare alla riga successiva ogni 16 byte di input e includere virgolette all'inizio e alla fine di ogni riga, sarebbe ancora più bello!

Sono consapevole che la stringa includerà null ( \x00), quindi dovrò specificare la lunghezza della stringa nel codice, per evitare che questi byte terminino presto la stringa.



Voglio un glifo stampabile ASCII simile ma mantengo, scappando solo 1-127, citazione, barra rovesciata, null, ecc.
把 友情 留 在 无 盐

Risposte:


10

Puoi quasi fare quello che vuoi hexdump, ma non riesco a capire come ottenere virgolette e singole barre rovesciate nella stringa di formato. Quindi faccio un po 'di post-elaborazione con sed. Come bonus, ho anche indentato ogni riga di 4 spazi. :)

hexdump -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/.*/    "&"/'

modificare

Come ha sottolineato Cengiz Can, la riga di comando sopra non riesce a gestire bene le linee di dati brevi. Quindi ecco una nuova versione migliorata:

hexdump -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/\\x  //g; s/.*/    "&"/'

Come menziona Malvineous nei commenti, dobbiamo anche passare l' -vopzione dettagliata hexdumpper impedirgli di abbreviare lunghe serie di byte identici a *.

hexdump -v -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/\\x  //g; s/.*/    "&"/'

Ciò produce elementi ridondanti e non validi se l'input è inferiore a 16 byte.
Cengiz può il

@CengizCan:: oops :! Va meglio?
PM 2Ring

1
È necessario aggiungere l' -vopzione a hexdump, altrimenti l'esecuzione prolungata dello stesso byte di input causa righe di output che indicano "*".
Malvineous,

@Malvineous Ottimo punto! Ho modificato la mia risposta. Grazie per l'heads-up (e grazie per aver accettato la mia risposta).
PM 2Ring

66

xxdha una modalità per questo. L' opzione -i/ --includesarà:

l'output in C include lo stile del file. Viene scritta una definizione di array statico completa (che prende il nome dal file di input), a meno che xxd non legga da stdin.

Puoi scaricarlo in un file in modo da essere #included, quindi accedere semplicemente foocome qualsiasi altro array di caratteri (o collegarlo). Include anche una dichiarazione della lunghezza dell'array.

L'output è suddiviso in 80 byte e assomiglia essenzialmente a ciò che potresti scrivere a mano:

$ xxd --include foo
unsigned char foo[] = {
  0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
  0x21, 0x0a, 0x0a, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x76, 0x65,
  0x72, 0x79, 0x20, 0x63, 0x75, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x21, 0x20,
  0x57, 0x65, 0x6c, 0x6c, 0x20, 0x64, 0x6f, 0x6e, 0x65, 0x2e, 0x0a
};
unsigned int foo_len = 47;

xxdfa un po 'stranamente parte della vimdistribuzione, quindi probabilmente ce l'hai già. In caso contrario, è lì che lo ottieni: puoi anche creare lo strumento da solo fuori dalla vimfonte.


Bello! Non sapevo nemmeno di avere xxd. Ora devo solo ricordare che esiste la prossima volta che ne ho bisogno ... o probabilmente replicherò semplicemente la funzionalità richiesta in Python. :)
PM 2Ring

objcopysarebbe meglio
Lightness Races con Monica il

@LightnessRacesinOrbit objcopyconsentirebbe a OP di collegare i dati binari all'eseguibile come file oggetto, il che è utile ma non esattamente ciò che viene richiesto qui.
Wander Nauta,

1
@WanderNauta: accederesti praticamente nello stesso modo in cui accederai foo/ foo_lenqui, e non sprecheresti molto spazio. Sono convinto che il PO starebbe meglio objcopye che soddisfa le sue esigenze.
Lightness Races con Monica il

2
objcopyva bene quando è in giro, ma non è portatile e l'output lo è ancora di meno. Può certamente far parte di una buona soluzione permanente, ma non è questa la domanda.
Michael Homer,

3

xxd è buono ma il risultato è molto dettagliato e occupa molto spazio di archiviazione.

Puoi ottenere praticamente la stessa cosa usando objcopy; per esempio

objcopy --input binary \
    --output elf32-i386 \
    --binary-architecture i386 foo foo.o

Quindi collegare foo.oal programma e utilizzare semplicemente i seguenti simboli:

00000550 D _binary_foo_end
00000550 A _binary_foo_size 
00000000 D _binary_foo_start

Questa non è una stringa letterale, ma è essenzialmente la stessa cosa che una stringa letterale si trasforma durante la compilazione (considera che i letterali stringa non esistono in realtà in fase di esecuzione; in effetti, nessuna delle altre risposte ti dà effettivamente una stringa letterale anche in fase di compilazione) ed è possibile accedervi in ​​gran parte allo stesso modo:

unsigned char* ptr = _binary_foo_start;
int i;
for (i = 0; i < _binary_foo_size; i++, ptr++)
   putc(*ptr);

Il rovescio della medaglia è che è necessario specificare l'architettura di destinazione per rendere compatibile il file oggetto e questo potrebbe non essere banale nel sistema di compilazione.


2

Dovrebbe essere esattamente quello che hai chiesto:

hexdump -v -e '"\\" "x" 1/1 "%02X"' file.bin ; echo

0

Questa è una breve utility che ho scritto che essenzialmente fa la stessa cosa (originariamente pubblicata su StackTranslate.it ):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LENGTH 80

int main(void)
{
    FILE *fout = fopen("out.txt", "w");

    if(ferror(fout))
    {
        fprintf(stderr, "Error opening output file");
        return 1;
    }
    char init_line[]  = {"char hex_array[] = { "};
    const int offset_length = strlen(init_line);

    char offset_spc[offset_length];

    unsigned char buff[1024];
    char curr_out[64];

    int count, i;
    int line_length = 0;

    memset((void*)offset_spc, (char)32, sizeof(char) * offset_length - 1);
    offset_spc[offset_length - 1] = '\0';

    fprintf(fout, "%s", init_line);

    while(!feof(stdin))
    {
        count = fread(buff, sizeof(char), sizeof(buff) / sizeof(char), stdin);

        for(i = 0; i < count; i++)
        {
            line_length += sprintf(curr_out, "%#x, ", buff[i]);

            fprintf(fout, "%s", curr_out);
            if(line_length >= MAX_LENGTH - offset_length)
            {
                fprintf(fout, "\n%s", offset_spc);
                line_length = 0;
            }
        }
    }
    fseek(fout, -2, SEEK_CUR);
    fprintf(fout, " };");

    fclose(fout);

    return EXIT_SUCCESS;
}

1
La tua risposta sarebbe più utile se fornissi anche gli esempi di input e output.
not2qubit

0

Se ti piace Python, caricalo in una variabile "buff" e usa qualcosa del genere:

buff2 = buff.encode("hex")
print ("0x"+", 0x".join([buff2[i:i+2] for i in range(0,len(buff2),2)]))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.