바이너리 파일을 C/C++ 문자열 리터럴로 덤프하는 방법은 무엇입니까?

바이너리 파일을 C/C++ 문자열 리터럴로 덤프하는 방법은 무엇입니까?

내 C 소스 코드에 (일시적으로 테스트 목적으로) 바이너리 파일을 포함하고 싶기 때문에 파일 내용을 다음과 같은 C 문자열로 가져오고 싶습니다.

\x01\x02\x03\x04

od또는 유틸리티를 사용하여 이것이 가능합니까 hexdump? 필수는 아니지만 문자열이 16개의 입력 바이트마다 다음 줄로 줄 바꿈되고 각 줄의 시작과 끝에 큰따옴표를 포함할 수 있다면 좋을 것입니다!

문자열에 null 값이 포함( \x00)된다는 것을 알고 있으므로 이러한 바이트로 인해 문자열이 조기에 종료되는 것을 방지하려면 코드에서 문자열 길이를 지정해야 합니다.

답변1

xxd이를 수행하는 패턴이 있습니다. -i/옵션은 --include다음을 수행합니다.

C의 출력에는 파일 스타일이 포함되어 있습니다. xxd가 stdin에서 읽지 않는 한 완전한 정적 배열 정의(입력 파일 이름을 따서 명명됨)를 작성합니다.

다른 문자 배열처럼 쓰기 위해 파일에 덤프한 #include다음 foo액세스(또는 링크)할 수 있습니다. 또한 배열 길이 선언도 포함됩니다.

출력은 80바이트로 압축되며 기본적으로 직접 작성한 것과 같습니다.

$ 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다소 이상하게도 이는 vim배포판의 일부이므로 이미 가지고 있을 것입니다. 그렇지 않은 경우 거기에서 얻을 수 있습니다. 소스에서 직접 도구를 빌드할 수도 있습니다 vim.

답변2

당신은 할 수거의원하는 대로 작동 hexdump하지만 형식 문자열에 따옴표와 단일 백슬래시를 넣는 방법을 모르겠습니다. 그래서 sed. 보너스로 각 줄을 4칸씩 들여쓰겠습니다. :)

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

편집하다

Cengiz Can이 지적했듯이 위의 명령줄은 짧은 데이터 줄을 잘 처리하지 못합니다. 새롭게 개선된 버전은 다음과 같습니다.

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

Malvineous가 주석에서 언급했듯이, 동일한 bytes의 장기 실행을 단축하는 것을 방지 -v하려면 verbose 옵션을 verbose에 전달 해야 합니다 .hexdump*

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

답변3

xxd괜찮지만 결과가 너무 장황하고 저장 공간을 많이 차지합니다.

다음을 사용하면 거의 동일한 결과를 얻을 수 있습니다.objcopy;예를 들어

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

foo.o그런 다음 프로그램에 연결 하고 다음 기호를 사용하십시오.

00000550 D _binary_foo_end
00000550 A _binary_foo_size 
00000000 D _binary_foo_start

이것은 문자열 리터럴은 아니지만 본질적으로 컴파일 중에 문자열 리터럴이 되는 것과 동일합니다(문자열을 고려하세요).단어실제로 런타임에는 존재하지 않습니다. 실제로 컴파일 타임에도 다른 답변 중 어느 것도 실제로 문자열 리터럴을 제공하지 않으며 거의 ​​동일한 방식으로 액세스할 수 있습니다.

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

단점은 대상 파일을 호환 가능하게 만들기 위해 대상 아키텍처를 지정해야 한다는 것인데, 이는 빌드 시스템에서는 중요하지 않을 수 있습니다.

답변4

다음은 본질적으로 동일한 작업을 수행하는 짧은 유틸리티입니다(원래 게시됨).스택 오버플로):

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

관련 정보