Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C: put x86 instructions into array and execute them [duplicate]

Is there any way to put processor instructions into array, make its memory segment executable and run it as a simple function:

int main()
{
    char myarr[13] = {0x90, 0xc3};
    (void (*)()) myfunc = (void (*)()) myarr;
    myfunc();
    return 0;
}
like image 330
Asking Brother Avatar asked Dec 05 '22 00:12

Asking Brother


1 Answers

On Unix (these days, that means "everything except Windows and some embedded and mainframe stuff you've probably never heard of") you do this by allocating a whole number of pages with mmap, writing the code into them, and then making them executable with mprotect.

void execute_generated_machine_code(const uint8_t *code, size_t codelen)
{
    // in order to manipulate memory protection, we must work with
    // whole pages allocated directly from the operating system.
    static size_t pagesize;
    if (!pagesize) {
        pagesize = sysconf(_SC_PAGESIZE);
        if (pagesize == (size_t)-1) fatal_perror("getpagesize");
    }

    // allocate at least enough space for the code + 1 byte
    // (so that there will be at least one INT3 - see below),
    // rounded up to a multiple of the system page size.
    size_t rounded_codesize = ((codelen + 1 + pagesize - 1)
                               / pagesize) * pagesize;

    void *executable_area = mmap(0, rounded_codesize,
                                 PROT_READ|PROT_WRITE,
                                 MAP_PRIVATE|MAP_ANONYMOUS,
                                 -1, 0);
    if (!executable_area) fatal_perror("mmap");

    // at this point, executable_area points to memory that is writable but
    // *not* executable.  load the code into it.
    memcpy(executable_area, code, codelen);

    // fill the space at the end with INT3 instructions, to guarantee
    // a prompt crash if the generated code runs off the end.
    // must change this if generating code for non-x86.
    memset(executable_area + codelen, 0xCC, rounded_codesize - codelen);

    // make executable_area actually executable (and unwritable)
    if (mprotect(executable_area, rounded_codesize, PROT_READ|PROT_EXEC))
        fatal_perror("mprotect");

    // now we can call it. passing arguments / receiving return values
    // is left as an exercise (consult libffi source code for clues).
    ((void (*)(void)) executable_area)();

    munmap(executable_area, rounded_codesize);
}

You can probably see that this code is very nearly the same as the Windows code shown in cherrydt's answer. Only the names and arguments of the system calls are different.

When working with code like this, it is important to know that many modern operating systems will not allow you to have a page of RAM that is simultaneously writable and executable. If I'd written PROT_READ|PROT_WRITE|PROT_EXEC in the call to mmap or mprotect, it would fail. This is called the W^X policy; the acronym stands for Write XOR eXecute. It originates with OpenBSD, and the idea is to make it harder for a buffer-overflow exploit to write code into RAM and then execute it. (It's still possible, the exploit just has to find a way to make an appropriate call to mprotect first.)

like image 65
zwol Avatar answered May 16 '23 08:05

zwol