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!

Leave a Comment

Your email address will not be published.