Problem
I wish to inject an object file into an existing binary. As a concrete example, consider a source Hello.c
:
#inc
You must make room for the relocatable code to fit in the executable by extending the executables text segment, just like a virus infection. Then after writing the relocatable code into that space, update the symbol table by adding symbols for anything in that relocatable object, and then apply the necessary relocation computations. I've written code that does this pretty well with 32bit ELF's.
You cannot do this in any practical way. The intended solution is to make that object into a shared lib and then call dlopen on it.
The problem is that .o's are not fully linked yet, and most references are still symbolic. Binaries (shared libraries and executables) are one step closer to finally linked code.
Doing the linking step to a shared lib, doesn't mean you must load it via the dynamic lib loader. The suggestion is more that an own loader for a binary or shared lib might be simpler than for .o.
Another possibility would be to customize that linking process yourself and call the linker and link it to be loaded on some fixed address. You might also look at the preparation of e.g. bootloaders, which also involve a basic linking step to do exactly this (fixate a piece of code to a known loading address).
If you don't link to a fixed address, and want to relocate runtime you will have to write a basic linker that takes the object file, relocates it to the destination address by doing the appropriate fixups.
I assume you already have it, seeing it is your master thesis, but this book: http://www.iecc.com/linker/ is the standard introduction about this.
If it were me, I'd look to create Embed.c
into a shared object, libembed.so
, like so:
gcc -Wall -shared -fPIC -o libembed.so Embed.c
That should created a relocatable shared object from Embed.c
. With that, you can force your target binary to load this shared object by setting the environment variable LD_PRELOAD
when running it (see more information here):
LD_PRELOAD=/path/to/libembed.so Hello
The "trick" here will be to figure out how to do your instrumentation, especially considering it's a static executable. There, I can't help you, but this is one way to have code present in a process' memory space. You'll probably want to do some sort of initialization in a constructor, which you can do with an attribute (if you're using gcc
, at least):
void __attribute__ ((constructor)) my_init()
{
// put code here!
}
Assuming source code for first executable is available and is compiled with a linker script that allocates space for later object file(s), there is a relatively simpler solution. Since I am currently working on an ARM project examples below are compiled with the GNU ARM cross-compiler.
Primary source code file, hello.c
#include <stdio.h>
int main ()
{
return 0;
}
is built with a simple linker script allocating space for an object to be embedded later:
SECTIONS
{
.text :
{
KEEP (*(embed)) ;
*(.text .text*) ;
}
}
Like:
arm-none-eabi-gcc -nostartfiles -Ttest.ld -o hello hello.c
readelf -s hello
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 SECTION LOCAL DEFAULT 1
2: 00000000 0 SECTION LOCAL DEFAULT 2
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 FILE LOCAL DEFAULT ABS hello.c
5: 00000000 0 NOTYPE LOCAL DEFAULT 1 $a
6: 00000000 0 FILE LOCAL DEFAULT ABS
7: 00000000 28 FUNC GLOBAL DEFAULT 1 main
Now lets compile the object to be embedded whose source is in embed.c
void func1()
{
/* Something useful here */
}
Recompile with the same linker script this time inserting new symbols:
arm-none-eabi-gcc -c embed.c
arm-none-eabi-gcc -nostartfiles -Ttest.ld -o new_hello hello embed.o
See the results:
readelf -s new_hello
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 SECTION LOCAL DEFAULT 1
2: 00000000 0 SECTION LOCAL DEFAULT 2
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 FILE LOCAL DEFAULT ABS hello.c
5: 00000000 0 NOTYPE LOCAL DEFAULT 1 $a
6: 00000000 0 FILE LOCAL DEFAULT ABS
7: 00000000 0 FILE LOCAL DEFAULT ABS embed.c
8: 0000001c 0 NOTYPE LOCAL DEFAULT 1 $a
9: 00000000 0 FILE LOCAL DEFAULT ABS
10: 0000001c 20 FUNC GLOBAL DEFAULT 1 func1
11: 00000000 28 FUNC GLOBAL DEFAULT 1 main
Have you looked at the DyninstAPI? It appears support was recently added for linking a .o into a static executable.
From the release site:
Binary rewriter support for statically linked binaries on x86 and x86_64 platforms