Ошибка загрузки 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)); Вместо маллока. - 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). Ваша программа может дать сбой, потому что она запускается с конца NOOP и либо в неинициализированную память, кто знает, что там скрывается, либо в конец исполняемой страницы. Я не могу сказать, не посмотрев, что находится в файле, который вы загружаете.

Кстати, strace очень полезен для таких программ. Он бы сказал вам, в чем была ошибка в mprotect.

person Bernd Jendrissek    schedule 13.03.2010

Использование всех разрешений PROT_EXEC | PROT_READ | PROT_WRIT тоже не нужен и довольно опасен. Обычно вам не нужен PROT_WRITE, достаточно просто выполнить и прочитать.

Некоторые защищенные ядра даже не позволяют PROT_EXEC | ПРОТ_ЗАПИСЬ.

person rurban    schedule 09.08.2010