I am trying to execute a binary executable file inside C code without using system
since it has security and resource management issues.
The system used here is Debian Buster with kernel 5.4.0-2-amd64 and gcc 9.2.1.
I used the method in this question:
execute binary machine code from C
which is to convert executable into hexadecimal code with xxd -i
, but constantly get Segmentation fault
.
The procedures I used are:
First attempt
executable.c:
#include <stdio.h>
int main(void)
{
printf("Hello, World!n");
return 0;
}
after compiling it with gcc -o executable executable.c
xxd -i executable
will display binary file into hex
Then copy and paste the output to embedded.c
embedded.c:
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
const unsigned char[] executable = {
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01,
...
};
int main(void)
{
void *buf = mmap(
NULL, sizeof(executable), PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON, -1, 0);
memcpy(buf, sizeof(executable);
__builtin___clear_cache(buf, buf + sizeof(executable) - 1);
int i = ((int (*) (void)buf)();
return 0;
}
when compiled and run, the terminal displays Segmentation fault
.
Second attempt
Another method I tried is by using ld
which also displayed Segmentation fault
:
embedded.c:
extern const char _binary_executable_start[];
extern const char _binary_executable_end[];
// And same as the previous code.
The code was compiled using:
gcc -c -o embedded.o embedded.c
ld -r -b binary -o executable.o executable
gcc -o embedded embedded.o executable.o
And failed.
Is there anything I missed or is it impossible to embed binary file into C code and run it?
2
Answers
The executable content you’re providing is an ELF executable, not raw machine code. On Linux, ELF is the container format that is used to contain executables and shared libraries, and it wraps the actual executable code and associated data, like strings and arrays, with a format that allows it to be loaded easily.
As a result, the code you’re trying to execute isn’t actually machine code, it’s ELF header data. Even if you extract the executable code out of the ELF, it generally still needs to be relocated by the dynamic linker, so it won’t easily be able to be executed directly.
Instead, consider putting the additional code into a shared library and executing it there. If you need to load the code dynamically, you can use the
dlopen(3)
function to load a shared library anddlsym(3)
to find the function you want to execute, provided you link with-ldl
.If you want to execute directly an executable from your running program (
system(3)
library function spawns a shell to run it) you can use the same system calls that were used to run your binary (the one you are executing and whishes to execute a binary)In unix, you need first to create a second process, which is normally a twin (in the exact same state of execution) with the
fork(2)
system call. Thefork(2)
makes the difference between parent and child processes, by returning a different value to each (it returns the pid of the child to the parent, and 0 to the child) So, from that point on, you can follow different paths in your execution, based on the return value.Normally, the parent and child then arrange for input/output redirection, which means to substitute the
0
,1
and2
open file descriptors to do the redirection, and then the memory of the child process is overlaid with a new executable, that is loaded in place by the kernel, with theexecve(2)
family of system calls. There’s no more secure method than this. I don’t fully understand what do you mean with secure, but if you are not able to execute a program with this method, then nothing else can be done.Your method of loading yourself a binary in memory and try to run it yourself, is not only error prone, but also is not portable. Compiling a source file, as already commented in other answers, is not the solution, as that requires you to first understand the whole process of loading an executable in general unix systems. An executable has some text segment (normally it is read-only to make the executable code shareable between different processes running the same program), a read-write data segment (that grows on request by issuing a system call for it) and a stack segment (per thread) that usually grows automatically, when references to it are being made by the cpu.
So you have to deal with all of this, it is not as easy as on a simple processor, where all the memory is available for use. You have to ask the operating system for the memory you’ll be using to run your program. Think for example, that executable code in Intel processors don’t need even to be readable memory (only when loaded to the cpu as an instruction, the read memory operation succeeds, but if you try to read the data as data, you’ll generate a trap)
Conclussion
Read about the
fork(2)
andexec(2)
system calls in your linux manual, and a basic unix programming manual, to get a good introduction on how to execute programs in unix.