Revista Tecnología

Cómo empotrar datos dentro de un ejecutable hecho en C

Publicado el 12 septiembre 2016 por Gaspar Fernández Moreno @gaspar_fm

Cómo empotrar datos dentro ejecutable hechoCómo empotrar datos dentro de un ejecutable hecho en C

En nuestras andanzas como programadores, seguro que nos hemos encontrado alguna vez en esta situación. Tenemos un programa que vamos a distribuir, pero que tiene ciertos archivos asociados (imágenes, texto, scripts, etc) que deben ir junto con el programa.
En un primer momento podemos distribuir los archivos junto con el programa, y es una buena solución hasta que a alguien le da por cambiar esos archivos y consiguen que nuestro programa haga cosas diferentes a aquellas para las que ha sido pensado originalmente.

Esto se ha hecho durante años para almacenar este tipo de recursos, incluso algunos IDEs y compiladores lo hacen sin que nosotros hagamos nada. Pero somos valientes, y lo haremos con GCC.

Tal vez para imágenes no sea demasiado crítico, pero sí para código. Es decir, muchas veces, aunque hagamos un programa en C, éste a su vez tendrá fragmentos programados en LUA, Python, AngelScript, o incluso SQL. Y claro, si incluimos estos scripts en el código estamos forzando a recompilar el programa cuando hay un cambio en estos scripts, cosa que puede tardar mucho, y no es necesaria (lo mismo pasa con iconos, imágenes, etc).
Con recompilar el programa, me refiero a compilar un archivo .c o lo que es peor, realizar un cambio en un .h que se incluya en varias partes. Si habéis trabajado con algún programa grande, sabréis a lo que me refiero, un pequeño cambio puede suponer tener parado el ordenador varios minutos.

Lo que podemos hacer, es que en modo depuración, los recursos se lean desde los archivos externos, pero cuando el programa sea definitivo se lea de lo que tenemos almacenado en el ejecutable.

Vamos a empezar empotrando un pequeño texto dentro de un archivo ejecutable. Para ello, nos serviremos tanto de GCC como de objcopy. Es importante saber que este método sólo funcionará con GCC/G++, con otro compilador, debemos hacer las cosas de otra manera.
Lo primero será hacerlo con archivos de texto, para ello, crearemos un archivo llamado (pruebas.txt) y escribiremos dentro un texto.
Luego haremos este programa en C:

Ahora vamos a compilar el archivo pruebas.txt y el ejecutable:

gcc -o main main.o pruebas.o


Y listo, cuando lancemos ./main nos escribirá en pantalla el mensaje que tenemos configurado.

Eso sí, tenemos que tener especial cuidado con la arquitectura. Este ejemplo es para x86-64, si quisiéramos una salida para x86 en 32bit pondríamos elf32-i386.

Otro pequeño ejemplo con idea

Contamos con que no manipulen nuestro archivo ejecutable cambiando los datos que contiene, aunque podríamos introducir mecanismos como hacer que los primeros bytes sean un hash y cuando arranque nuestro programa comprobar que todo está bien. Pero este tipo de cosas puede servirnos para rellenar información en un struct, por ejemplo:

Eso sí, ahora tenemos que hacer un archivo binario en el que incluyamos:

  • 4 bytes (ID, un número, que tal vez en texto sea ilegible).
  • 100 bytes de texto (url, hay que tener cuidado y meter un terminador, 0x00 cuando termine el texto).
  • 100 bytes de texto (nombre, incluyendo un terminador como antes).

Podemos generar el fichero de datos de varias maneras: una de ellas sería creando otro programa en C que escriba los datos en un archivo binario, otra forma puede ser utilizar un editor hexadecimal, como el pantallazo que muestro a continuación (Sí, es Emacs como editor hexadecimal, que Emacs vale para todo):

Cómo empotrar datos dentro ejecutable hecho
Cómo empotrar datos dentro de un ejecutable hecho en C
.
Luego llamamos a este archivo struct.data, creamos el objeto con objcopy y compilamos el programa enlazando el objeto. El resultado será algo como:

gcc -o main main.o struct.o

URL: http://totaki.com/poesiabinaria

Alternativa para depuración

Si queremos hacer, como dije al principio que el dato se coja de un archivo sólo si estamos en modo depuración y si no, se coja del ejecutable, debemos hacer algo como esto (es una idea, nada más, no está optimizado ni nada):

Y el fichero texto.data podrá contener lo que queráis. En este caso veremos que cuando compilamos y DEBUG es 0, SIEMPRE cogeremos la información empotrada en el ejecutable, pero cuando DEBUG vale 1 y además el fichero texto.data existe, leeremos el fichero y cogeremos de ahí la información. Si cuando dejamos de utilizar el recurso llamamos a FREE_RESOURCE, liberaremos el puntero que se reserva en modo depuración, pero en real no hace nada.

Nota de la foto: ¿Por qué frutas? Considero trozos de datos o información a las frutas, y cada una de ellas debe ser empotrada en un ejecutable sin perder sus propiedades.

Foto: Roman Davayposmotrim


Volver a la Portada de Logo Paperblog