Adding files to your binary
There are a lot of advantages to splitting up your files. If your program has a web interface for example, it’s easier to add the images and HTML files as separate files. But sometimes it’s nice if your program could wrap up itself and all the little files it uses into a monolithic binary.
Advantages:
- The files are complete and are the correct version
- Easy to distribute
- You can use an executable obfuscator, making your files a little more tamper-proof
- Be sure you can access your resources, no worries about permissions or paths
So, how do we do it? First off, let’s create some files. We’ll make some text files but you can include any kind of file:
echo -n "Hello" > hello.txt
echo -n "world!" > world.txt
Then we wrap all the files we want to include into an object file. Feel free to use wildcards where needed:
ld -r -b binary -o lib.o hello.txt world.txt
Let’s take a quick peek inside this object file to see how things are organized:
objdump -x lib.o
lib.o: file format elf64-x86-64 lib.o architecture: i386:x86-64, flags 0x00000010: HAS_SYMS start address 0x0000000000000000 Sections: Idx Name Size VMA LMA File off Algn 0 .data 0000000b 0000000000000000 0000000000000000 00000040 2**0 CONTENTS, ALLOC, LOAD, DATA SYMBOL TABLE: 0000000000000000 l d .data 0000000000000000 .data 0000000000000000 g .data 0000000000000000 _binary_hello_txt_start 000000000000000b g .data 0000000000000000 _binary_world_txt_end 0000000000000005 g .data 0000000000000000 _binary_world_txt_start 0000000000000005 g *ABS* 0000000000000000 _binary_hello_txt_size 0000000000000005 g .data 0000000000000000 _binary_hello_txt_end 0000000000000006 g *ABS* 0000000000000000 _binary_world_txt_size
As you can see, the files we added are mangled in there with start, end and size tags. It’s important to know that when our binary is running, an offset is added to these values to where they are actually loaded into memory. This means the start and end tags will point to where they should, however the size tag will not be usable since it too will have the offset added. In practice this won’t matter much since we’ll just calculate the size by subtracting the start from the end.
To bring it all together, we’ll create the program that uses the included resources. Copy the code below into a file called ‘print.c’:
#include <stdio.h>
#define DECL_START(x) extern const char _binary_##x##_start[];
#define DECL_END(x) extern const char _binary_##x##_end[];
#define DECL_ALL(x) DECL_START(x) DECL_END(x)
#define LIB_START(x) (unsigned char *) _binary_##x##_start
#define LIB_END(x) (unsigned char *) _binary_##x##_end
#define LIB_SIZE(x) (LIB_END(x) - LIB_START(x))
DECL_ALL(hello_txt)
DECL_ALL(world_txt)
/* You have to declare the variables in your program. The '_size' variable is
also present, but has a number added to it, so it's not usable. So instead
we just calculate the size by subtracting the start from the end */
int main()
{
// This prints: Hello world!
printf("%.*s %.*s\n", (int) (LIB_SIZE(hello_txt)), LIB_START(hello_txt),
(int) (LIB_SIZE(world_txt)), LIB_START(world_txt) );
return 0;
}
Compiling it is straightforward:
gcc -Wall -o print print.c lib.o
And finally the result:
# ./print
Hello world!