Зареждането на MachineCode от файл в паметта и изпълнението в C mprotect е неуспешно

Здравейте, опитвам се да заредя необработен машинен код в паметта и да го стартирам от C програма, точно сега, когато програмата се изпълнява, тя се прекъсва, когато се опитва да стартира mprotect в паметта, за да стане изпълнима. Също така не съм напълно сигурен, че ако паметта се настрои правилно, ще се изпълни. В момента изпълнявам това на Ubuntu Linux x86 (Може би проблемът е свръхзащитата на Ubuntu?)

Това, което имам в момента е следното:

#include <memory.h>
#include <sys/mman.h>
#include <stdio.h>

int main ( int argc, char **argv )
{
 FILE *fp;
 int sz = 0;
 char *membuf;
 int output = 0;

 fp = fopen(argv[1],"rb");

 if(fp == NULL)
 {
  printf("Failed to open file, aborting!\n");
  exit(1);
 }

 fseek(fp, 0L, SEEK_END);
 sz = ftell(fp);
 fseek(fp, 0L, SEEK_SET);


 membuf = (char *)malloc(sz*sizeof(char));
 if(membuf == NULL)
 {
  printf("Failed to allocate memory, aborting!\n");
  exit(1);
 }

  memset(membuf, 0x90, sz*sizeof(char));

 if( mprotect(membuf, sz*sizeof(char), PROT_EXEC | PROT_READ | PROT_WRITE) == -1)
 {
  perror("mprotect");
  printf("mprotect failed!!! aborting!\n");
  exit(1);
 }



 if(!(fread(membuf, sz*sizeof(char), 1, fp)))
 {
  perror("fread");
  printf("Read failed, aborting!\n");
  exit(1);
 }
 __asm__
 ( 
  "call %%eax;"
  : "=a" (output)
       : "a" (membuf)
 );
 printf("Output = %x\n", output);

 return 0;
}

Получавам предупреждението на компилатора:

/tmp/ccVnhHak.s: Assembler messages:
/tmp/ccVnhHak.s:107: Warning: indirect call without `*'

Все още не съм накарал програмата да достигне до този код, така че не мога да видя дали асемблерният ми код прави това, което трябва.


person ChartreuseKitsune    schedule 13.03.2010    source източник
comment
Съжаляваме, това е за x86 Linux, специално за Ubuntu. (Предполагам, че свръхзащитата на Ubuntu може да има нещо общо с това)   -  person ChartreuseKitsune    schedule 13.03.2010
comment
Какъв вид провал е това? Опитахте ли просто да го стартирате под gdb и да гледате какво ще се случи след това?   -  person Roman Dmitrienko    schedule 13.03.2010
comment
Грешката, която получава, е, че mprotect връща -1, което показва грешка. В момента не съм сигурен как да получа конкретния код за грешка от mprotect, за да мога да намеря грешката. Очевидно errno е зададено с него, но не знам как трябва да получа достъп до това.   -  person ChartreuseKitsune    schedule 13.03.2010
comment
Можете да използвате perror() като бърз и мръсен начин за получаване на описание на грешката   -  person Roman Dmitrienko    schedule 13.03.2010
comment
Добре, грешката, която получавам, е: mprotect: Невалиден аргумент, но не съм много сигурен кой аргумент е невалиден...   -  person ChartreuseKitsune    schedule 13.03.2010
comment
Може да е така, ако membuf е невалиден или не е подравнен към размера на системната страница, според човека...   -  person Roman Dmitrienko    schedule 13.03.2010
comment
Чудя се дали може би не съм разпределил паметта погрешно, като използвам malloc вместо някакъв друг метод?   -  person ChartreuseKitsune    schedule 13.03.2010
comment
Изглежда, че трябва да използвате posix_memalign()   -  person Roman Dmitrienko    schedule 13.03.2010
comment
Разглеждайки примера на mprotect в неговата страница с ръководство, изглежда, че те използват memalign за разпределяне на размера въз основа на файловете на страницата buffer = memalign(pagesize, 4 * pagesize); Когато размерът на страницата е зададен на системния размер на страницата, трябва ли аргументите на memalign да преместват страници в размери на страници?   -  person ChartreuseKitsune    schedule 13.03.2010


Отговори (3)


Добре, ето отговора, според нашата дискусия в коментарите :)

Областта на паметта трябва да бъде подравнена към размера на системната страница. posix_memalign() извикването е правилен начин за разпределяне на памет в такъв случай :)

person Roman Dmitrienko    schedule 13.03.2010
comment
Така че трябва да се разпредели в кратни на размера на страницата, който предполагам, а не от дължината на файла - person ChartreuseKitsune; 13.03.2010
comment
Е, ако съм разбрал правилно, само адресът е подравнен, не и размерът. Размерът е даден в байтове (според човека) - person Roman Dmitrienko; 13.03.2010
comment
Добре, изглежда, че сте преодоляли грешката на mprotect. Използване на: membuf = (char )memalign(pagesize, szsizeof(char)); Вместо malloc. - person ChartreuseKitsune; 13.03.2010
comment
Добре сега, след като mprotect вече не се проваля, има ли някакво предположение защо кодът създава segfault, gdb показва 0x0804b004 в ?? (). Предавам му файл с валиден байт код в него, а не ELF - person ChartreuseKitsune; 13.03.2010
comment
Предполагам, че програмата ви минава през последователност от NOP във вашия масив и тъй като няма инструкция за ret, тя отива направо в пустинята далеч отвъд определения регион - person Roman Dmitrienko; 13.03.2010
comment
Хм, това е странно, дали ще изпълни машинния код преди NOP, защото трябва да отпечата низ към терминала, преди да умре - person ChartreuseKitsune; 13.03.2010
comment
@Chartreuse: Ще трябва да направите стъпка si с една инструкция в GDB. По всичко личи, че сте подминали първата инструкция, защото адресът не завършва на 0. Вероятно нямате надежда да получите символи, така че ще бъде ?? (), но това всъщност не е толкова лошо. Отстранете грешки в обектния си код на друго място, преди да опитате да го заредите, разгледайте внимателно средата си, отворете нов въпрос, ако сте блокирали. - person Potatoswatter; 13.03.2010
comment
Добре, благодаря за цялата помощ, беше много по-бързо от очакваното. - person ChartreuseKitsune; 13.03.2010

Добавете 0xc3 (инструкция за връщане) след вашите 0x90 (noop) байта. Програмата ви може да се срива, защото се изпълнява от края на NOOPs и или в неинициализираната памет, кой знае какво се крие там, или в края на изпълнимата страница. Не мога да кажа наистина, без да погледна какво има във файла, който зареждате.

BTW strace е много полезен за този вид програми. Щеше да ви каже каква е грешката в mprotect.

person Bernd Jendrissek    schedule 13.03.2010

Използване на всички perms PROT_EXEC | PROT_READ | PROT_WRIT също не е необходим и доста опасен. Като цяло не се нуждаете от PROT_WRITE, достатъчно е само exec и read.

Някои защитени ядра дори не позволяват PROT_EXEC | PROT_WRIT.

person rurban    schedule 09.08.2010