c++ mprotect for read, write and execute

2.3k Views Asked by At

As part of a project I'm working on, I want to allocate some memory, write opcodes into that memory and then call it. By then, the code will jump into my allocated memory, run the opcodes and return back to the previous location using the opcode ret.

My code run on Linux and doesn't need to support Windows.

I tried to use mprotect with the flags PROT_READ | PROT_WRITE | PROT_EXEC but I get "Permission Denied" error. I need both write and execute permission on this memory.

How can I achieve what I'm trying? Why do I get "Permission Denied"?

The allocated memory is allocated using aligned_alloc so it will be aligned to 4KB (page size).

2

There are 2 best solutions below

0
Ari0nhh On

Looks like PaX/GrSecurity work to me:

The goal of the PaX project is to research various defense mechanisms against the exploitation of software bugs that give an attacker arbitrary read/write access to the attacked task's address space. This class of bugs contains among others various forms of buffer overflow bugs (be they stack or heap based), user supplied format string bugs, etc.

Introducing code into a task's address space is possible by either creating an executable mapping or modifying an already existing writable/executable mapping. The second method can be prevented by not allowing the creation of writable/executable mappings at all.

Try to use paxctl utility to disable memory protection mechanism for your program binary.

0
MyNick On

I found a solution and it quite nice to understand what happen. Hope you would like it.

As you test it, you can see that if you allocate a "large" memory chunk, like 1MB then mprotect manage add execution permissions. Using binary search you can find that the sweet spot is at 128KB. Glibc's malloc works in an interesting way which you should read about but shortly, it allocates memory ahead and give you some when you ask for. If it ran out of memory, it allocates more and so on. But at some size, it doesn't give you memory from it's own pool. You asked for a buffer that large enough for him to allocate one especially for you. This threshold is at 128KB and defined by DEFAULT_MMAP_THRESHOLD in the source code.

The default memory is allocated by glibc.o which is in a different memory segment, and it is loaded during run time. Because of that, your library doesn't have the permission to change that memory protection and add PERM_EXEC. BUT when you ask for a large memory, and malloc allocate new memory for you, YOUR LIBRARY own this memory, thus you can change it's permissions as you want.

More details and how to solve it for small memory amounts:

So how does GCC allocate memory for you? using mmap - http://man7.org/linux/man-pages/man2/mmap.2.html

   #include <sys/mman.h>
   void *mmap(void *addr, size_t length, int prot, int flags,
              int fd, off_t offset);
   int munmap(void *addr, size_t length);

mmap allocate memory in a new memory segment. If addr is NULL, then the kernel chooses the (page-aligned) address at which to create the mapping. Thus, we can allocate memory using this function. length must be a multiplication of page size (4096) which can be pretty wasteful for small memory amounts. If fd is -1 then the memory is filled with zero's.

Using this method, we can allocate memory with protection as we want, and in this case, with execute permissions.