1

我想复制并调用一个函数,但是下面的代码在调用缓冲区时会出现段错误。我必须改变什么?(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 ());
}

编辑:工作解决方案:

#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 ());
}
4

3 回答 3

9

哇,那个代码有很多问题。

  1. 你无法知道函数在内存中是按顺序排列的,它们之间没有填充
  2. 你不知道指向两个函数的指针是可减的
  3. 你不知道malloc()可以调用返回的内存

简而言之,不要这样做。

更新:

在 Linux 中,我认为您可以使用mprotect()来设置内存块的权限。我认为这需要root,但显然不需要(只要您在自己的进程内存中)。

于 2010-09-15T12:21:31.967 回答
3

您可能正在使用一个不授予数据段执行权限的操作系统。

一些环境将保护数据页面不被执行,以避免各种类型的安全问题(或针对它们的利用)。

考虑调用 mprotect() 以启用该页面的执行并报告发生的情况。

于 2010-09-15T12:22:27.910 回答
0

这是嵌入式系统人员中的一个常见问题。这种技术经常用于从只读存储器复制到随机存取存储器(具有读写能力)。使用标准 C 或 C++ 没有优雅或标准的解决方案。

一个更简单的解决方案是使用链接器来定义一些新的、非标准的段。使用非标准#pragma来指示编译器将函数放入新段中。使用非标准编译器指令访问该段的开始地址和结束地址。这将允许您获取函数的大小。

对目标更安全的方法是创建另一个具有可执行和写入属性的段。将函数段中的数据复制到这个可执行段中。设置一个函数指针指向该段的开始。通过指针执行函数。

另一种解决方案是用汇编语言执行此操作。通常,汇编程序给你更多的自由(让你的脚射门)来像这样在较低的级别上操作内存。

此外,请查看您的操作系统加载程序、内存属性和保护方案。某些操作系统可能会将这种行为限制为内核权限或更高权限。

于 2010-09-15T19:25:27.383 回答