Скопируйте функцию в память и выполните ее

Я хотел бы знать, как в C можно скопировать содержимое функции в память и выполнить ее?

Я пытаюсь сделать что-то вроде этого:

typedef void(*FUN)(int *);
char * myNewFunc;

char *allocExecutablePages (int pages)
{
    template = (char *) valloc (getpagesize () * pages);
    if (mprotect (template, getpagesize (), 
          PROT_READ|PROT_EXEC|PROT_WRITE) == -1) {
        perror ("mprotect");
    } 
}

void f1 (int *v) {
    *v = 10;
}

// allocate enough spcae but how much ??
myNewFunc = allocExecutablePages(...)

/* Copy f1 somewere else
 * (how? assume that i know the size of f1 having done a (nm -S foo.o))
 */

((FUN)template)(&val);
printf("%i",val);

Спасибо за ваши ответы


person Elinghton    schedule 28.12.2010    source источник
comment
Вы уже задавали этот вопрос однажды здесь, stackoverflow.com/questions/4541419/ - почему вы спрашиваете снова?   -  person David Heffernan    schedule 28.12.2010
comment
Для получения дополнительной информации поищите в Интернете встроенную копию ОЗУ с функцией C.   -  person Thomas Matthews    schedule 28.12.2010
comment
что такого особенного, что все равно нуждается в запутывании?   -  person David Heffernan    schedule 28.12.2010


Ответы (5)


Вы, кажется, поняли часть о флагах защиты. Если вы знаете размер функции, теперь вы можете просто выполнить memcpy() и передать адрес f1 в качестве исходного адреса.

Одно большое предостережение заключается в том, что на многих платформах вы не сможете вызывать какие-либо другие функции из той, которую вы копируете (f1), потому что относительные адреса жестко закодированы в двоичном коде функции и перемещают ее в другой код. расположение в памяти может привести к тому, что эти относительные адреса станут плохими.

person Eugene Smith    schedule 28.12.2010
comment
Всегда можно выполнить перемещение из файла .o. Однако получайте удовольствие от написания собственного компоновщика. :-) - person R.. GitHub STOP HELPING ICE; 28.12.2010
comment
Начальный адрес функции легко получить, используя имя функции. Другое дело длина функции. - person Thomas Matthews; 28.12.2010

Это работает, потому что функция1 и функция2 имеют одинаковый размер в памяти. Нам нужна длина function2 для нашей memcopy, поэтому нужно сделать следующее: int diff = (&main - &function2);

Вы заметите, что можете редактировать функцию 2 по своему вкусу, и она продолжает работать нормально!

Кстати ловкий трюк. К сожалению, компилятор g++ выдает неверное преобразование из void* в int... Но на самом деле с gcc он отлично компилируется;)

Модифицированные источники:

//Hacky solution and simple proof of concept that works for me (and compiles without warning on Mac OS X/GCC 4.2.1):
//fixed the diff address to also work when function2 is variable size    
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <sys/mman.h>

int function1(int x){ 
   return x-5;
}   

int function2(int x){ 
  //printf("hello world");
  int k=32;
  int l=40;
  return x+5+k+l;
}   


int main(){
  int diff = (&main - &function2);
  printf("pagesize: %d, diff: %d\n",getpagesize(),diff);

  int (*fptr)(int);

  void *memfun = malloc(4096);

  if (mprotect(memfun, 4096, PROT_READ|PROT_EXEC|PROT_WRITE) == -1) {
      perror ("mprotect");
  }   

  memcpy(memfun, (const void*)&function2, diff);

  fptr = &function1;
  printf("native: %d\n",(*fptr)(6));
  fptr = memfun;
  printf("memory: %d\n",(*fptr)(6) );
  fptr = &function1;
  printf("native: %d\n",(*fptr)(6));

  free(memfun);
  return 0;
}   

Выход:

Walter-Schrepperss-MacBook-Pro:cppWork wschrep$ gcc memoryFun.c 
Walter-Schrepperss-MacBook-Pro:cppWork wschrep$ ./a.out 
pagesize: 4096, diff: 35
native: 1
memory: 83
native: 1

Еще одно замечание: вызов printf приведет к segfault, потому что printf, скорее всего, не будет найден из-за неправильного относительного адреса...

person Walter Schreppers    schedule 02.03.2012
comment
Хороший ответ! Незначительная проблема: malloc(4096) не гарантирует возврат памяти, выровненной по страницам размером 4096 байт (хотя я думаю, что это обычно происходит с большинством распределителей), но mprotect() обычно требует, чтобы его первый аргумент был выровнен по странице. Чтобы быть уверенным, вы можете использовать aligned_alloc() (доступно с C11) и использовать getpagesize() вместо константы 4096. en.cppreference.com/w/c/memory/aligned_alloc - person jcsahnwaldt Reinstate Monica; 28.12.2018

Хакерское решение и простое доказательство концепции, которое работает для меня (и компилируется без предупреждения в Mac OS X/GCC 4.2.1):

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <sys/mman.h>

int function1(int x){
   return x-5;
}

int function2(int x){
  return x+5;
}


int main(){
  int diff = (&function2 - &function1);
  printf("pagesize: %d, diff: %d\n",getpagesize(),diff);

  int (*fptr)(int);

  void *memfun = malloc(4096);

  if (mprotect(memfun, 4096, PROT_READ|PROT_EXEC|PROT_WRITE) == -1) {
      perror ("mprotect");
  }

  memcpy(memfun, (const void*)&function2, diff);

  fptr = &function1;
  printf("native: %d\n",(*fptr)(6));
  fptr = memfun;
  printf("memory: %d\n",(*fptr)(6) );
  fptr = &function1;
  printf("native: %d\n",(*fptr)(6));

  free(memfun);
  return 0;
}
person Brian    schedule 09.01.2012
comment
Хороший ответ! Незначительная проблема: malloc(4096) не гарантирует возврат памяти, выровненной по страницам размером 4096 байт (хотя я думаю, что это обычно происходит с большинством распределителей), но mprotect() обычно требует, чтобы его первый аргумент был выровнен по странице. Чтобы быть уверенным, вы можете использовать aligned_alloc() (доступно с C11) и использовать getpagesize() вместо константы 4096. en.cppreference.com/w/c/memory/aligned_alloc - person jcsahnwaldt Reinstate Monica; 28.12.2018

Я пробовал эту проблему много раз в C и пришел к выводу, что это невозможно решить, используя только язык C. Моя главная проблема заключалась в том, чтобы найти длину функции для копирования.

Стандартный язык C не предоставляет никаких методов для получения длины функции. Однако можно использовать язык ассемблера и «разделы», чтобы найти длину. Как только длина найдена, копирование и выполнение становятся простыми.

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

Существуют и другие методы, включающие настройку компоновщика, такие как предопределенные области или фиксированные местоположения и копирование этих местоположений.

Во встраиваемых системах большая часть кода, который копирует исполняемые файлы в оперативную память, написана на ассемблере.

person Thomas Matthews    schedule 28.12.2010
comment
Вам не нужен ассемблер, чтобы определить длину функции — вы можете сделать это с помощью скрипта компоновщика. - person Ponkadoodle; 03.08.2015

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

person gumby    schedule 12.09.2011