copy and call function

2019-06-03 08:52发布

I'd like to copy and call a function, but the code below segfaults when calling the buffer. What do I have to change? (Linux, x86)

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

int foo () { return 12; }
void foo_end () {}

int main () {
  int s = (unsigned long long) foo_end - (unsigned long long) foo;
  int (*f) () = (int (*)()) malloc (s);
  memcpy ((void*) f, (const void*) foo, s);
  printf ("%d %d\n", f (), foo ());
}

EDIT: Working solution:

#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>

int foo () { return 12; }
void foo_end () {}

int main () {
  int s = (unsigned long long) foo_end - (unsigned long long) foo;
  int (*f) () = (int (*)()) malloc (s);
  memcpy ((void*) f, (const void*) foo, s);
  long ps = sysconf (_SC_PAGESIZE);
  void *fp = (void*) ((unsigned long long) f & ~((unsigned long long) (ps-1)));
  if (mprotect ((void*) fp, ps, PROT_READ | PROT_WRITE | PROT_EXEC)) return -1;
  printf ("%d %d\n", f (), foo ());
}

3条回答
等我变得足够好
2楼-- · 2019-06-03 09:05

This is a common issue among embedded systems folk. This technique is often times used for copying from Read-Only Memory into Random-Access Memory (write and read capable). There is no elegant nor standard solution using standard C or C++.

A simpler solution is to use the Linker to define some new, non-standard, segments. Use non-standard #pragma to instruct the compiler to place the function into a new segment. Use non-standard compiler directive to access the beginning address and ending address of this segment. This will allow you to get the size of the function.

A safer method for the destination is to create another segment with executable and write attributes. Copy the data in the function segment into this executable segment. Set up a function pointer to point to the start of this segment. Execute the function via the pointer.

Another solution is to perform this in assembly language. Often, assemblers give you more freedom (to shoot your foot) to manipulate memory like this in a lower level.

Also, review your operating system loader, memory attributes and protection schemes. Some OSes may restrict this kind of behavior to Kernel privilege or higher.

查看更多
霸刀☆藐视天下
3楼-- · 2019-06-03 09:10

Whoa, that code has so many issues.

  1. You can't know that the functions are laid out sequentially in memory, with no padding between them
  2. You can't know that the pointers to two functions are subtractable
  3. You can't know that memory returned by malloc() can be called into

In short, don't do this.

Update:

In Linux, I think you can use mprotect() to set the permissions on a block of memory. I thought this needed root, but apparently not (as long as you're in your own process' memory).

查看更多
淡お忘
4楼-- · 2019-06-03 09:17

Potentially you're using an OS which does not grant execute permission to data segments.

Some environments will protect data pages against execution, to avoid various types of security problems (or exploits for them).

Consider calling mprotect() to enable execute for that page and report what happens.

查看更多
登录 后发表回答