Como despejar um arquivo binário como uma literal de seqüência de caracteres C / C ++?

39

Eu tenho um arquivo binário que gostaria de incluir no meu código-fonte C (temporariamente, para fins de teste), então gostaria de obter o conteúdo do arquivo como uma string C, algo como isto:

\x01\x02\x03\x04

Isso é possível, talvez usando os utilitários odou hexdump? Embora não seja necessário, se a string puder quebrar a linha seguinte a cada 16 bytes de entrada e incluir aspas duplas no início e no final de cada linha, isso seria ainda melhor!

Estou ciente de que a string terá nulos incorporados ( \x00), portanto, precisarei especificar o comprimento da string no código, para impedir que esses bytes terminem a string com antecedência.

Malvineous
fonte
stackoverflow.com/q/13856930/560648
Lightness Races with Monica
Quero semelhante, mas mantêm ascii glifo de impressão, apenas a escapar 1-127, citações, barra invertida, null, etc.
把友情留在无盐

Respostas:

10

Você quase pode fazer o que quiser hexdump, mas não consigo descobrir como obter aspas e barras invertidas na string de formato. Então, eu faço um pouco de pós-processamento com sed. Como bônus, também recuei cada linha em 4 espaços. :)

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

Editar

Como Cengiz Can apontou, a linha de comando acima não lida bem com linhas de dados curtas. Então, aqui está uma nova versão aprimorada:

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

Como Malvineous menciona nos comentários, também precisamos passar a -vopção detalhada hexdumppara impedir que abrevie longas execuções de bytes idênticos a *.

hexdump -v -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/\\x  //g; s/.*/    "&"/'
PM 2Ring
fonte
Isso produz elementos redundantes e inválidos se a entrada for menor que 16 bytes.
Cengiz Pode 27/12
@CengizCan:: oops :! Isto é melhor?
PM 2Ring
11
É necessário adicionar a -vopção hexdump, caso contrário, longas execuções do mesmo byte de entrada causam as linhas de saída que dizem "*".
Malvineous
@Malvineous Bom ponto! Eu alterei minha resposta. Obrigado pela atenção (e obrigado por aceitar minha resposta).
usar o seguinte comando
66

xxdtem um modo para isso. A opção -i/ --include:

saída em C inclui estilo de arquivo. Uma definição de matriz estática completa é gravada (nomeada após o arquivo de entrada), a menos que o xxd leia de stdin.

Você pode despejar isso em um arquivo a ser #included e acessar apenas foocomo qualquer outra matriz de caracteres (ou vinculá-lo). Ele também inclui uma declaração do comprimento da matriz.

A saída é agrupada em 80 bytes e parece essencialmente com o que você pode escrever à mão:

$ 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;

xxdé, de certa forma, parte da vimdistribuição, então você provavelmente já a possui. Caso contrário, é onde você o obtém - você também pode criar a ferramenta por conta própria, fora da vimfonte.

Michael Homer
fonte
Agradável! Eu nem sabia que tinha xxd. Agora só tenho que lembrar que existe da próxima vez que precisar ... ou provavelmente replicarei a funcionalidade necessária no Python. :)
PM 2Ring
objcopyseria melhor
Lightness Races com Monica
O @LightnessRacesinOrbit objcopypermitiria ao OP vincular os dados binários ao executável como um arquivo de objeto, o que é útil, mas não é exatamente o que está sendo solicitado aqui.
Wander Nauta
11
@WanderNauta: Você poderia acessá-lo da mesma maneira que acessaria foo/ foo_lenaqui, e não estaria desperdiçando muito espaço de armazenamento. Estou convencido de que o OP estaria em melhor situação objcopye que atenda às suas exigências.
Lightness Races com Monica
2
objcopyé bom quando está por perto, mas não é portátil e a saída ainda menos. Certamente pode fazer parte de uma boa solução permanente, mas essa não é a questão aqui.
Michael Homer
3

xxd é bom, mas o resultado é altamente detalhado e ocupa muito espaço de armazenamento.

Você pode conseguir praticamente a mesma coisa usando objcopy; por exemplo

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

Em seguida, vincule foo.oao seu programa e simplesmente use os seguintes símbolos:

00000550 D _binary_foo_end
00000550 A _binary_foo_size 
00000000 D _binary_foo_start

Isso não é uma literal de cadeia de caracteres, mas é essencialmente a mesma coisa que um literal de cadeia de caracteres se transforma durante a compilação (considere que os literais de cadeia de fato não existem de fato no tempo de execução; na verdade, nenhuma das outras respostas realmente fornece uma cadeia de caracteres literal mesmo em tempo de compilação) e pode ser acessado basicamente da mesma maneira:

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

A desvantagem é que você precisa especificar sua arquitetura de destino para tornar o arquivo de objeto compatível, e isso pode não ser trivial em seu sistema de construção.

Corridas de leveza com Monica
fonte
2

Deve ser exatamente o que você pediu:

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

Este é um pequeno utilitário que escrevi que essencialmente faz a mesma coisa (originalmente publicado no Stack Overflow ):

#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;
}
Curtidor
fonte
11
Sua resposta seria mais útil se você também fornecesse os exemplos de entrada e saída.
not2qubit
0

Se você gosta de python, carregue-o em uma variável "buff" e use algo como isto:

buff2 = buff.encode("hex")
print ("0x"+", 0x".join([buff2[i:i+2] for i in range(0,len(buff2),2)]))
TimSC
fonte