How do call _start of a binary from another executable?

Viewed 182

I have an elf64 executable, foo, that I want to load and start “by hand” and be able to call other functions from. How do I load it into memory and then set the instruction pointer to run with it.

foo is NOT a shared object library, it is an executable that has certain functions exported as if it were an SO.

So, a few questions:

  1. Where do I load the binary into memory so that is executable? Stack? Heap?
  2. How do I setup the instruction pointer to change from my program to the entry point of foo?

For example, I have the following, but it segfaults:

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <elf.h>

#define ELF_SIZE 10000

int main(int argc, char **argv)
{

    FILE *fp;
    void *   entry_point ;
    Elf64_Ehdr *elfHdr;
    uint8_t *buffer = malloc(ELF_SIZE);

    fp = fopen("./foo", "rb");

    int read_size = fread(buffer, 1, ELF_SIZE, fp);
    if (read_size == ELF_SIZE)
    {
        printf("loaded ELF onto heap
\n");
    } else
    {
        printf("read failed: %d\n", read_size);
        return 0;
    }
    printf("elf loaded at %x\n", buffer);

    elfHdr = (Elf64_Ehdr*) buffer;

    printf("entry point at %x\n", elfHdr->e_entry);

    entry_point = elfHdr->e_entry + buffer;

    printf("trying to jump to: %x\n", entry_point
            );
        int a;
        __asm__ ("jmp %1;"
                : "=r" (a)
                : "r" (entry_point));

    return 0;

} 

Using normal methods to start foo() like system(), or other standard OS tool isn't an option for various reasons. I need to be able to call _start to kick it off and and "foo_bar()" once it has started running. I have tried using dlopen/dlsym but it doesn't work since its an executable and not a shared library

1 Answers

printf("loaded ELF onto stack \n");

That is a lie: you loaded the executable into heap, not stack.

Where do I load the binary into memory so that is executable? Stack? Heap?

Neither heap nor stack are appropriate; use mmap to bring the binary into memory. Several mmap calls are likely required (one for each PT_LOAD segment in the target binary).

How do I setup the instruction pointer to change from my program to the entry point of foo?

Changing the instruction pointer is the least of your problems (the jmp you've already coded will work, or you can cast the address to a function pointer and then simply call it -- no need for inline assembly).


What you are trying to achieve is exceedingly non-trivial. Look at UPX sources to see what's involved.

If the binary is non-PIE executable, then you can't load it into arbitrary memory location -- it has been linked to be loaded at a specific address, and will not run correctly if loaded at any other address.

If the binary is a PIE executable, it can run at arbitrary address (though it will still have alignment requirements, which malloc is unlikely to satisfy).

In older versions of GLIBC it was possible to dlopen() PIE executables, but newer versions disallow that.

Before a PIE executable can run, it must be relocated, which is again highly non-trivial task.

TL;DR: you probably don't really want to do this (see also http://xyproblem.info), but if you really do, it would certainly take a lot more effort than merely reading the binary into a buffer and jumping to the _start symbol in it.

Related