如何将二进制文件转储为 C/C++ 字符串文字?

如何将二进制文件转储为 C/C++ 字符串文字?

我想将一个二进制文件包含在我的 C 源代码中(暂时用于测试目的),因此我想以 C 字符串形式获取文件内容,如下所示:

\x01\x02\x03\x04

这可能吗,也许通过使用odhexdump实用程序?虽然不是必需的,但如果字符串可以每 16 个输入字节换行到下一行,并在每行的开头和结尾包含双引号,那就更好了!

我知道该字符串将嵌入空值 ( \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 在评论中提到的,我们还需要将-vverbose 选项传递给 ,以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;
}

相关内容