复制和通话功能

时间:2010-09-15 12:18:41

标签: c++ c linux assembly self-modifying

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

3 个答案:

答案 0 :(得分:9)

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

  1. 您无法知道函数是在内存中按顺序排列的,没有填充 他们之间
  2. 你不可能知道指向两个函数的指针是可以删除的
  3. 您无法知道malloc()返回的内存可以调入
  4. 简而言之,不要这样做。

    更新

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

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:0)

这是嵌入式系统民间的常见问题。这种技术通常用于从只读存储器复制到随机存取存储器(能够写和读)。使用标准C或C ++没有优雅的标准解决方案。

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

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

另一种解决方案是使用汇编语言执行此操作。通常,装配工给你更多的自由(射击你的脚)来操纵这样的记忆在较低的水平。

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