Jak zrzucić plik binarny jako literał ciągu C / C ++?


39

Mam plik binarny, który chciałbym zawrzeć w moim kodzie źródłowym C (tymczasowo, w celach testowych), więc chciałbym uzyskać zawartość pliku jako ciąg C, coś takiego:

\x01\x02\x03\x04

Czy jest to możliwe, być może przy użyciu narzędzia odlub hexdump? Chociaż nie jest to konieczne, jeśli ciąg znaków może zawijać się do następnego wiersza co 16 bajtów wejściowych i zawierać cudzysłowy na początku i na końcu każdego wiersza, byłoby jeszcze ładniej!

Wiem, że łańcuch będzie miał osadzone wartości null ( \x00), więc będę musiał określić długość łańcucha w kodzie, aby zapobiec przedwczesnemu zakończeniu łańcucha przez te bajty.



Chcę podobny, ale zachowaj glif do wydrukowania w ascii, tylko przed 1-127, cytat, ukośnik odwrotny, zerowy itp.
把 友情 留 在 在 无 盐

Odpowiedzi:


10

Możesz prawie robić, co chcesz hexdump, ale nie mogę dowiedzieć się, jak uzyskać cudzysłowy i pojedyncze odwrotne ukośniki w ciągu formatu. Więc robię trochę post-processingu z sed. Jako bonus, wciąłem również każdą linię o 4 spacje. :)

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

Edytować

Jak wskazał Cengiz Can, powyższa linia poleceń nie radzi sobie dobrze z krótkimi liniami danych. Oto nowa ulepszona wersja:

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

Jak Malvineous wspomina w komentarzach, musimy również przekazać -vpełną opcję, hexdumpaby zapobiec skracaniu długich ciągów identycznych bajtów *.

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

Powoduje to tworzenie zbędnych i niepoprawnych elementów, jeśli dane wejściowe są krótsze niż 16 bajtów.
Cengiz Can

@CengizCan:: oops :! Czy to jest lepsze?
PM 2, pierścień

1
Musisz dodać -vopcję hexdump, w przeciwnym razie długie przebiegi tego samego bajtu wejściowego powodują, że linie wyjściowe mówią "*".
Malvineous,

@Malvineous Dobra uwaga! Poprawiłem swoją odpowiedź. Dzięki za heads-up (i dziękuję za zaakceptowanie mojej odpowiedzi).
PM 2Ring

66

xxdma do tego tryb. Opcja -i/ --includebędzie:

dane wyjściowe w C obejmują styl pliku. Zostanie zapisana pełna definicja tablicy statycznej (nazwana od pliku wejściowego), chyba że xxd odczyta ze standardowego wejścia.

Możesz zrzucić to do pliku, który ma być #included, a następnie po prostu uzyskać dostęp, foojak każda inna tablica znaków (lub połączyć ją). Zawiera także deklarację długości tablicy.

Dane wyjściowe są zawinięte do 80 bajtów i wyglądają zasadniczo jak to, co można napisać ręcznie:

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

xxdjest nieco dziwnie częścią vimdystrybucji, więc prawdopodobnie już ją masz. Jeśli nie, to właśnie tam możesz go zdobyć - możesz również zbudować narzędzie samodzielnie ze vimźródła.


Miły! Nawet nie wiedziałem, że mam xxd. Teraz muszę tylko pamiętać, że istnieje ona następnym razem, gdy jej potrzebuję ... albo prawdopodobnie po prostu skopiuję wymaganą funkcjonalność w Pythonie. :)
PM 2Ring

objcopybyłoby lepiej
wyścigi lekkości z Monicą

@LightnessRacesinOrbit objcopypozwoliłby OP połączyć dane binarne z plikiem wykonywalnym jako plik obiektowy, co jest użyteczne, ale nie do końca o to, o co tutaj proszono.
Wander Nauta

1
@WanderNauta: Miałbyś do niego dostęp w prawie taki sam sposób, jak byś miał dostęp foo/ foo_lentutaj i nie marnowałbyś miejsca do przechowywania. Jestem przekonany, że lepiej byłoby PO objcopyi że odpowiada on jego wymaganiom.
Wyścigi lekkości z Monicą

2
objcopyjest w porządku, gdy jest w pobliżu, ale nie jest przenośny, a wyjście jeszcze mniej. Z pewnością może być częścią dobrego stałego rozwiązania, ale nie o to tutaj chodzi.
Michael Homer

3

xxd jest dobry, ale wynik jest bardzo szczegółowy i zajmuje dużo miejsca do przechowywania.

Możesz osiągnąć praktycznie to samo za pomocą objcopy; na przykład

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

Następnie połącz foo.osię z programem i użyj następujących symboli:

00000550 D _binary_foo_end
00000550 A _binary_foo_size 
00000000 D _binary_foo_start

To nie jest literał łańcuchowy, ale zasadniczo to samo, co zmienia się literał łańcuchowy podczas kompilacji (weź pod uwagę, że literały łańcuchowe w rzeczywistości nie istnieją w czasie wykonywania; w rzeczywistości żadna z pozostałych odpowiedzi nie daje ci literału łańcuchowego nawet w czasie kompilacji) i można uzyskać do niego dostęp w ten sam sposób:

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

Minusem jest to, że musisz określić architekturę docelową, aby plik obiektowy był kompatybilny, co może nie być trywialne w systemie kompilacji.


2

Powinno być dokładnie to, o co prosiłeś:

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

0

To krótkie narzędzie, które napisałem, które zasadniczo robi to samo (pierwotnie opublikowane na 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
Twoja odpowiedź byłaby bardziej przydatna, gdybyś podał również przykłady wejściowe i wyjściowe.
not2qubit

0

Jeśli jesteś w Pythonie, załaduj go do zmiennej „buff” i użyj czegoś takiego:

buff2 = buff.encode("hex")
print ("0x"+", 0x".join([buff2[i:i+2] for i in range(0,len(buff2),2)]))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.