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


39

J'ai un fichier binaire que je voudrais inclure dans mon code source C (temporairement, à des fins de test) afin que je souhaite 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 odou hexdump? Bien que cela ne soit pas nécessaire, si la chaîne peut passer à la ligne suivante tous les 16 octets en entrée et inclure des guillemets doubles au début et à la fin de chaque ligne, ce serait encore mieux!

Je suis conscient que la chaîne comportera nulls ( \x00). J'aurai donc besoin de spécifier la longueur de la chaîne dans le code pour empêcher ces octets de terminer la chaîne plus tôt.



Je veux similaire , mais conserve glyphe imprimable ascii, échappant seulement 1-127, citation, backslash, null, etc.
把友情留在无盐

Réponses:


10

Vous pouvez presque faire ce que vous voulez hexdump, mais je ne vois pas comment insérer des guillemets et des barres obliques inverses dans la chaîne de format. Je fais donc un peu de post-traitement avec sed. En prime, j'ai également mis en retrait chaque ligne de 4 espaces. :)

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

modifier

Comme Cengiz Can l'a fait remarquer, la ligne de commande ci-dessus ne convient pas aux lignes de données courtes. Alors, voici 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 l’ -voption verbose afin hexdumpde l’empêcher de raccourcir les longues séries d’octets identiques *.

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

Cela produit des éléments redondants et non valides si l'entrée est inférieure à 16 octets.
Cengiz peut

@CengizCan:: oops :! Est-ce mieux?
PM 2Ring

1
Nécessité d'ajouter l' -voption à hexdump, sinon les longs lancements du même octet d'entrée causent des lignes de sortie qui disent "*".
Malvineous

@ Bon point positif! J'ai modifié ma réponse. Merci pour le heads-up (et merci d'avoir accepté ma réponse).
PM 2Re

66

xxda un mode pour cela. L' option -i/ --includeva:

sortie en C type de fichier à inclure. Une définition complète de tableau statique est écrite (nommée d'après le fichier d'entrée), sauf si xxd lit à partir de stdin.

Vous pouvez sauvegarder cela dans un fichier pour qu'il soit #included, puis accéder foocomme n'importe quel autre tableau de caractères (ou le lier). Il comprend également une déclaration de la longueur du tableau.

La sortie est encapsulé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étrangement, fait partie de la vimdistribution, donc vous l’avez probablement déjà. Sinon, c'est là que vous l'obtenez - vous pouvez également créer l'outil à partir de la vimsource.


Agréable! Je ne savais même pas que j'avais xxd. Maintenant, je dois juste me rappeler que cela existe la prochaine fois que j'en ai besoin ... ou je vais probablement simplement reproduire les fonctionnalités requises en Python. :)
PM 2Ring

objcopyserait mieux
Courses de légèreté avec Monica

@LightnessRacesinOrbit objcopypermettrait à OP de lier les données binaires à l'exécutable sous forme de fichier objet, ce qui est utile mais ne correspond pas exactement à ce qui est demandé ici.
Wander Nauta

1
@WanderNauta: Vous y accéderiez pratiquement de la même manière que vous accéderiez foo/ foo_lenici, et vous ne gaspilleriez pas énormément d'espace de stockage. Je suis convaincu que le PO serait mieux avec objcopyet que cela répond à ses besoins.
Courses de légèreté avec Monica

2
objcopyest bien quand il est autour, mais ce n'est pas portable et la sortie encore moins. Cela peut certainement faire partie d'une bonne solution permanente, mais ce n'est pas la question.
Michael Homer

3

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

Vous pouvez réaliser pratiquement la même chose en utilisant objcopy; par exemple

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

Ensuite, créez un lien foo.overs 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 que devient un littéral de chaîne pendant la compilation (considérez que les littéraux de chaîne n'existent pas en réalité au moment de l'exécution; en fait, aucune des autres réponses ne vous donne réellement un littéral de chaîne. même au moment de la compilation) et peut être consulté 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, ce qui peut ne pas être trivial dans votre système de construction.


2

Devrait être exactement ce que vous avez demandé:

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

0

Voici un petit utilitaire que j'ai écrit qui fait essentiellement la même chose (posté à l'origine 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;
}

1
Votre réponse serait plus utile si vous fournissiez également des exemples d’entrée et de sortie.
not2qubit

0

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

buff2 = buff.encode("hex")
print ("0x"+", 0x".join([buff2[i:i+2] for i in range(0,len(buff2),2)]))
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.