¿Cómo volcar un archivo binario como un literal de cadena C / C ++?

39

Tengo un archivo binario que me gustaría incluir en mi código fuente C (temporalmente, para fines de prueba), así que me gustaría obtener el contenido del archivo como una cadena C, algo así:

\x01\x02\x03\x04

¿Es esto posible, quizás utilizando las utilidades odo hexdump? Si bien no es necesario, si la cadena puede ajustarse a la siguiente línea cada 16 bytes de entrada e incluir comillas dobles al comienzo y al final de cada línea, ¡eso sería aún mejor!

Soy consciente de que la cadena tendrá nulos incrustados ( \x00), por lo que necesitaré especificar la longitud de la cadena en el código, para evitar que estos bytes terminen la cadena antes.

Malvinoso
fuente
stackoverflow.com/q/13856930/560648
Lightness compite con Monica el
Quiero un glifo similar para imprimir ascii, solo escapando 1-127, cita, barra invertida, nulo, etc.
把 友情 留 在 无 盐

Respuestas:

10

Casi puede hacer lo que quiera hexdump, pero no puedo entender cómo obtener comillas y barras diagonales simples en la cadena de formato. Así que hago un pequeño postprocesamiento con sed. Como beneficio adicional, también he sangrado cada línea por 4 espacios. :)

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

Editar

Como Cengiz Can señaló, la línea de comando anterior no se adapta bien a las líneas de datos cortas. Así que aquí hay una nueva versión mejorada:

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

Como Malvineous menciona en los comentarios, también tenemos que pasar la -vopción detallada hexdumppara evitar que abrevie longitudes largas de bytes idénticos a *.

hexdump -v -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/\\x  //g; s/.*/    "&"/'
PM 2Ring
fuente
Esto produce elementos redundantes e inválidos si la entrada es menor a 16 bytes.
Cengiz puede
@CengizCan:: ¡Uy :! ¿Eso está mejor?
PM 2Ring
1
Es necesario agregar la -vopción hexdump, de lo contrario, las ejecuciones largas del mismo byte de entrada causan líneas de salida que dicen "*".
Malvineous
@Malvineous Buen punto! He modificado mi respuesta. Gracias por el aviso (y gracias por aceptar mi respuesta).
PM 2Ring
66

xxdtiene un modo para esto. La opción -i/ --include:

salida en C incluye estilo de archivo. Se escribe una definición de matriz estática completa (llamada así por el archivo de entrada), a menos que xxd lea desde stdin.

Puede volcar eso en un archivo para ser #included, y luego simplemente acceder foocomo cualquier otra matriz de caracteres (o vincularlo). También incluye una declaración de la longitud de la matriz.

La salida se ajusta a 80 bytes y se parece esencialmente a lo que podría escribir a mano:

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

xxdes, algo extraño, parte de la vimdistribución, por lo que es probable que ya la tenga. Si no es así, ahí es donde lo obtienes: también puedes construir la herramienta por sí sola desde la vimfuente.

Michael Homer
fuente
¡Agradable! Ni siquiera sabía que tenía xxd. Ahora solo tengo que recordar que existe la próxima vez que lo necesite ... o probablemente solo replicaré la funcionalidad requerida en Python. :)
PM 2Ring
objcopysería mejor
Lightness compite con Monica el
@LightnessRacesinOrbit objcopypermitiría que OP vincule los datos binarios con el ejecutable como un archivo objeto, lo cual es útil pero no exactamente lo que se pregunta aquí.
Wander Nauta
1
@WanderNauta: Se podría acceder a él en más o menos la misma forma que es posible acceder a foo/ foo_lenaquí, y que no sería muy desperdiciar espacio de almacenamiento. Estoy convencido de que el OP estaría mejor objcopyy que se ajusta a sus necesidades.
Lightness compite con Monica el
2
objcopyestá bien cuando está cerca, pero no es portátil y la salida aún menos. Ciertamente puede ser parte de una buena solución permanente, pero esa no es la cuestión aquí.
Michael Homer
3

xxd es bueno pero el resultado es muy detallado y ocupa mucho espacio de almacenamiento.

Puedes lograr prácticamente lo mismo usando objcopy; p.ej

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

Luego enlace foo.oa su programa y simplemente use los siguientes símbolos:

00000550 D _binary_foo_end
00000550 A _binary_foo_size 
00000000 D _binary_foo_start

Este no es un literal de cadena, pero es esencialmente lo mismo en lo que se convierte un literal de cadena durante la compilación (considere esa cadena tenga en literales de en realidad no existen en tiempo de ejecución; de hecho, ninguna de las otras respuestas realmente le da un literal de cadena incluso en tiempo de compilación) y se puede acceder en gran medida de la misma manera:

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

La desventaja es que debe especificar su arquitectura de destino para que el archivo de objeto sea compatible, y esto puede no ser trivial en su sistema de compilación.

La ligereza corre con Mónica
fuente
2

Debe ser exactamente lo que pediste:

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

Esta es una breve utilidad que escribí que esencialmente hace lo mismo (originalmente publicado en 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;
}
Curtidor
fuente
1
Su respuesta sería más útil si también proporcionara los ejemplos de entrada y salida.
not2qubit
0

Si estás en Python, cárgalo en una variable "buff" y usa algo como esto:

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