Функция копирования и вызова
Я хотел бы скопировать и вызвать функцию, но код ниже 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 ответа
Возможно, вы используете ОС, которая не предоставляет разрешения на выполнение для сегментов данных.
Некоторые среды защищают страницы данных от выполнения, чтобы избежать различных типов проблем безопасности (или их использования).
Рассмотрите возможность вызова mprotect(), чтобы включить execute для этой страницы и сообщить, что происходит.
У этого кода столько проблем.
- Вы не можете знать, что функции расположены в памяти последовательно, без заполнения между ними
- Вы не можете знать, что указатели на две функции вычитаются
- Вы не можете знать, что память вернулась
malloc()
может быть вызван в
Короче, не делай этого.
Обновить:
В Linux, я думаю, вы можете использовать mprotect()
установить разрешения для блока памяти. Я думал, что для этого нужен root, но, видимо, нет (пока вы находитесь в памяти вашего собственного процесса).
Это распространенная проблема среди встраиваемых систем людей. Этот метод часто используется для копирования из постоянной памяти в оперативную память (с возможностью записи и чтения). Не существует ни элегантного, ни стандартного решения, использующего стандартные C или C++.
Более простое решение - использовать компоновщик для определения некоторых новых нестандартных сегментов. Используйте нестандартный #pragma
поручить компилятору поместить функцию в новый сегмент. Используйте нестандартную директиву компилятора для доступа к начальному и конечному адресам этого сегмента. Это позволит вам получить размер функции.
Более безопасный метод для назначения - создать еще один сегмент с исполняемыми и записывающими атрибутами. Скопируйте данные из сегмента функции в этот исполняемый сегмент. Установите указатель на функцию, указывающую на начало этого сегмента. Выполнить функцию через указатель.
Другое решение - выполнить это на ассемблере. Часто ассемблеры дают вам больше свободы (стрелять ногой), чтобы манипулировать памятью на более низком уровне.
Также просмотрите загрузчик операционной системы, атрибуты памяти и схемы защиты. Некоторые операционные системы могут ограничивать такое поведение привилегиями ядра или выше.