2010-09-15 5 views
1

Я хотел бы скопировать и вызвать функцию, но код ниже 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()); 
} 

EDIT: Рабочий раствор:

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

Вероятно, рекомендуется удалить «PROT_WRITE», чтобы предотвратить случайную модификацию после копирования кода.Кроме того, нет необходимости округлять адрес до границы страницы, и вы должны указать размер функции (если она больше одной страницы); вызов должен быть 'mprotect ((void *) f, s, PROT_READ | PROT_EXEC)' –

+0

'mprotect' не работает в моей системе, если вы не выравниваете адрес. – Thomas

ответ

3

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

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

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

+0

Работает сейчас, спасибо! – Thomas

9

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

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

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

Update:

В Linux, я думаю, что вы можете использовать mprotect() установить разрешения на блоке памяти. Я думал, что для этого нужен корень, но, по-видимому, нет (пока вы находитесь в своем собственном процессе).

+0

Я согласен, не делайте этого :) – MarkR

+0

Это только первый тест, в настоящее время, но позже мне нужно заполнить буфер своими инструкциями по сборке. g ++ устанавливает 's' в 16, поэтому компоновка функций в порядке. Как установить разрешения для вызова в память, выделенную malloc? – Thomas

+0

Они не знают * вообще *, но с некоторой дополнительной информацией вы можете знать все три. Первое известно, если вы контролируете компоновщик. Второе можно узнать, если вы знаете компилятор. Третий является узнаваемым, если вы знаете ОС и ее настройки. Кроме того, я не вижу причин думать, что root будет единственной учетной записью, которая может установить разрешение на выполнение в собственной памяти процесса. –

0

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

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

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

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

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

Смежные вопросы