2

I work on a linux platform and I use g++ with the above program that copies a function from the code area to the data area. How do I change protection of data segment in order to allow me to execute the copied function ?

The code is bellow:

#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define Return asm volatile("pop %rbp; retq; retq; retq; retq; retq;")
int64_t funcEnd=0xc35dc3c3c3c3c35d;
constexpr int maxCode=0x800;
int8_t code[maxCode];

void testCode(void){
    int a=8,b=7;
    a+=b*a;
    Return;
}

typedef void (*action)(void);

int main(int argc, char **argv)
{
    action a=&testCode;
    testCode();

    int8_t *p0=(int8_t*)a,*p=p0,*p1=p0+maxCode;
    for(;p!=p1;p++)
        if ( (*(int64_t*)p)==funcEnd ) break;

    if(p!=p1){
            p+=sizeof(int64_t);
            printf("found\n");
        memcpy(&code,(void*)a,p-(int8_t*)a);
        ((action)&code)();
    }

  printf("returning 0\n");
    return 0;

}
user3723779
  • 287
  • 1
  • 2
  • 12

1 Answers1

6

It depends if you are trying to do this statically (at build-time), or at dynamically (at run-time).

Build-time

You need to tell GCC to put your blob in a section that is executable. We use __attribute__((section)), and this trick to specify the attributes of the section when we create it.

Run-time

TL;DR: Jump to the end of my answer, where I use mmap.

Although others might be questioning why you'd want do allow something like this at run-time, keep in mind that this is exactly what a VM with a JIT compiler (e.g. Java VM, .NET CLR, etc.) do when emitting native code.

You need to change the memory protections of the memory where you're trying to execute. We do that with mprotect(addr, PROT_EXEC). Note that addr must be aligned to the page size of your platform. On x86, the page size is 4K. We use aligned_alloc to guarantee this alignment.

Example (of both):

#define _ISOC11_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>   /* mprotect() */

__attribute__((section(".my_executable_blob,\"awx\",@progbits#")))
static uint8_t code[] = {
    0xB8,0x2A,0x00,0x00,0x00,   /* mov  eax,0x2a    */
    0xC3,                       /* ret              */
};

int main(void)
{
    int (*func)(void);

    /* Execute a static blob of data */
    func = (void*)code;
    printf("(static) code returned %d\n", func());

    /* Execute a dynamically-allocated blob of data */
    void *p = aligned_alloc(0x1000, sizeof(code));
    if (!p) {
        fprintf(stderr, "aligned_alloc() failed\n");
        return 2;
    }
    memcpy(p, code, sizeof(code));
    if (mprotect(p, sizeof(code), PROT_EXEC) < 0) {
        perror("mprotect");
        return 2;
    }
    func = p;
    printf("(dynamic) code returned %d\n", func());

    return 0;
}

Output:

$ ./a.out 
(static) code returned 42
(dynamic) code returned 42

SELinux Impact

Note that this puts your executable code on the heap which might be a bit dangerous. SELinux on my CentOS 7 machine actually denied the mprotect call:

SELinux is preventing /home/jreinhart/so/a.out from using the execheap access on a process.

*****  Plugin allow_execheap (53.1 confidence) suggests   ********************

If you do not think /home/jreinhart/so/a.out should need to map heap memory that is both writable and executable.
Then you need to report a bug. This is a potentially dangerous access.

So I had to temporarily sudo setenforce 0 to get this to work.

I'm not sure why, however, because looking in /proc/[pid]/maps, the pages are clearly marked only as executable, not as "writable and executable" as SELinux indicated. If I move the memcpy after the mprotect, my process segfaults, because I'm trying to write to non-writable memory. So it seems SELinux is being a bit too over-zealous here.

Use mmap instead

Instead of mprotecting a region of the heap (allocated with aligned_alloc), it is more straightforward to use mmap. This also avoids any issues with SELinux, as we're not trying to execute on the heap.

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>   /* mmap() */

static uint8_t code[] = { 
    0xB8,0x2A,0x00,0x00,0x00,   /* mov  eax,0x2a    */
    0xC3,                       /* ret              */
};

int main(void)
{
    void *p = mmap(NULL, sizeof(code), PROT_READ|PROT_WRITE|PROT_EXEC,
            MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 
    if (p==MAP_FAILED) {
        fprintf(stderr, "mmap() failed\n");
        return 2;
    }   
    memcpy(p, code, sizeof(code));

    int (*func)(void) = p;
    printf("(dynamic) code returned %d\n", func());

    pause();
    return 0;
}

The final solution

The mmap solution is good, but it doesn't provide us any safety; our mmaped region of code is readable, writable, and executable. It would be better to only allow the memory to be writable while we're putting our code in place, then making it executable only. The following code does just that:

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>   /* mmap(), mprotect() */

static uint8_t code[] = {
    0xB8,0x2A,0x00,0x00,0x00,   /* mov  eax,0x2a    */
    0xC3,                       /* ret              */
};

int main(void)
{
    const size_t len = sizeof(code);

    /* mmap a region for our code */
    void *p = mmap(NULL, len, PROT_READ|PROT_WRITE,  /* No PROT_EXEC */
            MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 
    if (p==MAP_FAILED) {
        fprintf(stderr, "mmap() failed\n");
        return 2;
    }   

    /* Copy it in (still not executable) */
    memcpy(p, code, len);

    /* Now make it execute-only */
    if (mprotect(p, len, PROT_EXEC) < 0) {
        fprintf(stderr, "mprotect failed to mark exec-only\n");
        return 2;
    } 

    /* Go! */
    int (*func)(void) = p;
    printf("(dynamic) code returned %d\n", func());

    pause();
    return 0;
}
Community
  • 1
  • 1
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
  • I'd go with `mmap` since it's seems to me the least 'hacky' and portable of the above solutions. – tangrs Jan 19 '15 at 00:51
  • @tangrs I completely agree. Using `mmap` and `mprotect` to properly manage your page permissions is a smart thing to do. – Jonathon Reinhart Jan 19 '15 at 00:58
  • @JonathonReinhart I just read the very detailed comments. Thanks !. In the meanwhile I had already arrived to mmap and done some checks, but while being able to allocate memory and copy when I execute I get segment error. I will check your code and come back if necessary. – user3723779 Jan 19 '15 at 12:23
  • @JonathonReinhart It worked but without using "printf" in the code. If I use a call to "printf" the called addres gets's mangled and so something different has to be done. – user3723779 Jan 19 '15 at 12:49
  • @user3723779 Do note that the `call` instruction branches to a *relative* address. That is, the immediate value in the `call` instruction tells the CPU how many bytes (from the *next* instruction) to jump. `EIP <- EIP + imm + 5`, where `EIP` is the instruction pointer (which is currently pointing at the `call`, `imm` is the call displacement (*imm*ediate value), and 5 is the size of the `call` instruction. You'll either need to write position-independent code, or fix-up all `call` instructions which call library functions. – Jonathon Reinhart Feb 12 '15 at 07:27
  • @JonathonReinhart I have done pretty much the same thing, and I am having this problem. I have tried using position-independent code by using -fPIC in g++, but to no avail. I have also tried other options (-fno-jump-tables, combinations of them...) any suggestions? – Nuclear_Man_D Jan 04 '17 at 09:49