функция копирования и вызова - PullRequest
1 голос
/ 15 сентября 2010

Я хотел бы скопировать и вызвать функцию, но код ниже segfaults при вызове буфера. Что я должен изменить? (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 ]

9 голосов
/ 15 сентября 2010

Ого, у этого кода так много проблем.

  1. Вы не можете знать, что функции последовательно располагаются в памяти без заполнения между ними
  2. Вы можете 'Не знаю, что указатели на две функции вычитаются
  3. Вы не можете знать, что память, возвращаемую malloc(), может быть вызвана в

Короче, не делайте этого.

Обновление:

В Linux, я думаю, вы можете использовать <a href="http://linux.die.net/man/2/mprotect" rel="nofollow noreferrer">mprotect()</a>, чтобы установить разрешения для блока памяти.Я думал, что для этого нужен root, но, очевидно, нет (если вы находитесь в памяти вашего собственного процесса).

3 голосов
/ 15 сентября 2010

Возможно, вы используете ОС, которая не предоставляет разрешения на выполнение для сегментов данных.

Некоторые среды защищают страницы данных от выполнения, чтобы избежать различных типов проблем безопасности (или эксплойтов для них).

Рассмотрите возможность вызова mprotect (), чтобы включить execute для этой страницы и сообщить, что происходит.

0 голосов
/ 15 сентября 2010

Это распространенная проблема среди встраиваемых систем людей. Этот метод часто используется для копирования из постоянной памяти в оперативную память (с возможностью записи и чтения). Не существует ни элегантного, ни стандартного решения, использующего стандартные C или C ++.

Более простое решение - использовать компоновщик для определения новых нестандартных сегментов. Используйте нестандартный #pragma, чтобы указать компилятору поместить функцию в новый сегмент. Используйте нестандартную директиву компилятора для доступа к начальному и конечному адресам этого сегмента. Это позволит вам получить размер функции.

Более безопасный метод для места назначения - создать еще один сегмент с атрибутами исполняемого файла и записи. Скопируйте данные из сегмента функции в этот исполняемый сегмент. Установите указатель на функцию, указывающую на начало этого сегмента. Выполнить функцию через указатель.

Другое решение - выполнить это на ассемблере. Часто ассемблеры дают вам больше свободы (стрелять ногой), чтобы манипулировать памятью на более низком уровне.

Кроме того, просмотрите загрузчик операционной системы, атрибуты памяти и схемы защиты. Некоторые операционные системы могут ограничивать такое поведение привилегиями ядра или выше.

...