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.



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/.*/    "&"/'

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.


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.


2

Deve ser exatamente o que você pediu:

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

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

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)]))
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.