web-dev-qa-db-fra.com

Comment vider un fichier binaire comme un littéral de chaîne C / C ++?

J'ai un fichier binaire que j'aimerais inclure dans mon code source C (temporairement, à des fins de test), donc je voudrais obtenir le contenu du fichier sous forme de chaîne C, quelque chose comme ceci:

\x01\x02\x03\x04

Est-ce possible, peut-être en utilisant les utilitaires od ou hexdump? Bien que cela ne soit pas nécessaire, si la chaîne peut passer à la ligne suivante tous les 16 octets d'entrée et inclure des guillemets doubles au début et à la fin de chaque ligne, ce serait encore plus agréable!

Je suis conscient que la chaîne contiendra des valeurs NULL incorporées (\x00) donc je devrai spécifier la longueur de la chaîne dans le code, pour empêcher ces octets de terminer la chaîne plus tôt.

41
Malvineous

Vous pouvez presque faire ce que vous voulez avec hexdump, mais je ne sais pas comment obtenir des guillemets et des barres obliques inverses au format chaîne. Je fais donc un petit post-traitement avec sed. En bonus, j'ai également indenté chaque ligne de 4 cases. :)

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

Modifier

Comme l'a souligné Cengiz Can, la ligne de commande ci-dessus ne gère pas bien les lignes de données courtes. Voici donc une nouvelle version améliorée:

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

Comme Malvineous le mentionne dans les commentaires, nous devons également passer le -v option verbeuse à hexdump pour l'empêcher d'abréger de longues séries d'octets identiques à *.

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

xxd a un mode pour cela. Le -i/--include l'option:

la sortie en C inclut le style de fichier. Une définition de tableau statique complète est écrite (nommée d'après le fichier d'entrée), à ​​moins que xxd ne lise depuis stdin.

Vous pouvez le vider dans un fichier pour être #included, puis accédez simplement à foo comme tout autre tableau de caractères (ou liez-le). Il comprend également une déclaration de la longueur du tableau.

La sortie est emballée à 80 octets et ressemble essentiellement à ce que vous pourriez écrire à la main:

$ 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 fait étrangement partie de la distribution vim, vous l'avez probablement déjà. Sinon, c'est là que vous l'obtenez - vous pouvez également créer l'outil seul à partir de la source vim.

72
Michael Homer

xxd est bon mais le résultat est très détaillé et prend beaucoup d'espace de stockage.

Vous pouvez obtenir pratiquement la même chose en utilisant objcopy ; par exemple.

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

Puis liez foo.o à votre programme et utilisez simplement les symboles suivants:

00000550 D _binary_foo_end
00000550 A _binary_foo_size 
00000000 D _binary_foo_start

Ce n'est pas un littéral de chaîne, mais c'est essentiellement la même chose que ce qu'un littéral de chaîne se transforme pendant la compilation (considérez que les littéraux de chaîne n'existent pas en fait au moment de l'exécution; en effet, aucune des autres réponses ne vous donne en fait un littéral de chaîne même au moment de la compilation) et est accessible en grande partie de la même manière:

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

L'inconvénient est que vous devez spécifier votre architecture cible pour rendre le fichier objet compatible, et cela peut ne pas être trivial dans votre système de génération.

3

Doit être exactement ce que vous avez demandé:

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

Ceci est un court utilitaire que j'ai écrit qui fait essentiellement la même chose (initialement publié sur 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;
}
0
Tanner

Si vous êtes en python, chargez-le dans une variable "buff" et utilisez quelque chose comme ceci:

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