2016-05-09 5 views
1

Есть ли способ поставить инструкций процессора в массив, сделать свой сегмент памяти исполняемый файл и запустить его в качестве простой функции:C: ставить х86 в массив и выполнить их

int main() 
{ 
    char myarr[13] = {0x90, 0xc3}; 
    (void (*)()) myfunc = (void (*)()) myarr; 
    myfunc(); 
    return 0; 
} 
+3

Это выглядит как [проблема XY] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Расскажите нам, какую проблему вы пытаетесь решить, прежде чем просить о помощи, чтобы решить ее определенным образом. –

+0

Я хотел бы сгенерировать x86-код в моей программе, а затем запустить его. –

+0

Вы имеете в виду что-то вроде [VirtualProtect] (https://msdn.microsoft.com/en-us/library/windows/desktop/aa366898 (v = vs.85) .aspx) в Windows? – Tomer

ответ

7

В Unix (в наши дни это означает «все, кроме Windows и некоторых встроенных и мейнфреймов, о которых вы, вероятно, никогда не слышали»), вы делаете это, выделяя целые числа страниц mmap, внося код в них, а затем сделать их исполняемыми с mprotect.

void execute_generated_machine_code(const uint8_t *code, size_t codelen) 
{ 
    // in order to manipulate memory protection, we must work with 
    // whole pages allocated directly from the operating system. 
    static size_t pagesize; 
    if (!pagesize) { 
     pagesize = sysconf(_SC_PAGESIZE); 
     if (pagesize == (size_t)-1) fatal_perror("getpagesize"); 
    } 

    // allocate at least enough space for the code + 1 byte 
    // (so that there will be at least one INT3 - see below), 
    // rounded up to a multiple of the system page size. 
    size_t rounded_codesize = ((codelen + 1 + pagesize - 1) 
          /pagesize) * pagesize; 

    void *executable_area = mmap(0, rounded_codesize, 
           PROT_READ|PROT_WRITE, 
           MAP_PRIVATE|MAP_ANONYMOUS, 
           -1, 0); 
    if (!executable_area) fatal_perror("mmap"); 

    // at this point, executable_area points to memory that is writable but 
    // *not* executable. load the code into it. 
    memcpy(executable_area, code, codelen); 

    // fill the space at the end with INT3 instructions, to guarantee 
    // a prompt crash if the generated code runs off the end. 
    // must change this if generating code for non-x86. 
    memset(executable_area + codelen, 0xCC, rounded_codesize - codelen); 

    // make executable_area actually executable (and unwritable) 
    if (mprotect(executable_area, rounded_codesize, PROT_READ|PROT_EXEC)) 
     fatal_perror("mprotect"); 

    // now we can call it. passing arguments/receiving return values 
    // is left as an exercise (consult libffi source code for clues). 
    ((void (*)(void)) executable_area)(); 

    munmap(executable_area, rounded_codesize); 
} 

Вы можете, вероятно, видеть, что этот код почти такой же, как код Windows, показанный в cherrydt's answer. Только имена и аргументы системных вызовов различны.

При работе с подобным кодом важно знать, что многие современные операционные системы не позволят вам иметь страницу ОЗУ одновременно записываемый и исполняемый файл. Если бы я написал PROT_READ|PROT_WRITE|PROT_EXEC в вызове mmap или mprotect, это не получится. Это называется W^X policy; аббревиатура означает Write XOR eXecute. Это originates with OpenBSD, и идея состоит в том, чтобы усложнить использование переполнения буфера для записи кода в ОЗУ и затем выполнить его. (Это по-прежнему возможно, эксплоит просто должен find a way to make an appropriate call to mprotect first.)

5

Зависит от платформы.

Для Windows, вы можете использовать этот код:

// Allocate some memory as readable+writable 
// TODO: Check return value for error 
LPVOID memPtr = VirtualAlloc(NULL, sizeof(myarr), MEM_COMMIT, PAGE_READWRITE); 

// Copy data 
memcpy(memPtr, myarr, sizeof(myarr); 

// Change memory protection to readable+executable 
// Again, TODO: Error checking 
DWORD oldProtection; // Not used but required for the function 
VirtualProtect(memPtr, sizeof(myarr), PAGE_EXECUTE_READ, &oldProtection);  

// Assign and call the function 
(void (*)()) myfunc = (void (*)()) memPtr; 
myfunc(); 

// Free the memory 
VirtualFree(memPtr, 0, MEM_RELEASE); 

Этот код предполагает myarr массив, как в коде вашего вопроса, и это предполагает, что sizeof будет работать на него, то есть он имеет непосредственно определенный размер и не только указатель переходил из другого места. Если это так, вам придется указывать размер по-другому.

Обратите внимание, что здесь есть два «упрощений» возможно, в случае, если вам интересно, но я бы не советовал им:

1) Вы могли бы назвать VirtualAlloc с PAGE_EXECUTE_READWRITE, но это в общем плохой практикой, потому что это было бы откройте вектор атаки для нежелательного вывода кода.

2) Вы могли бы назвать VirtualProtect на &myarr непосредственно, но это было бы просто сделать случайную страницу в вашей памяти, которая происходит, чтобы содержать свой исполняемый файл массива, который еще хуже # 1, потому что может быть и другие данные на этой странице, как хорошо, что теперь тоже неожиданно выполнимо.

Для Linux я нашел this в Google, но я мало знаю об этом.

+0

Для Linux: http://web.archive.org/web/20090203055327/http://people.redhat.com/drepper/selinux-mem.html – ninjalj

+0

@ninjalj Надеюсь, вы понимаете, что это страница о том, как * атаковать * SELinux. Нам не нужны люди, которые думают о SELinux как о препятствии. См. [Zwol's answer] (http://stackoverflow.com/a/37122499/6292850) за правильный способ его выполнения. –

+0

@ user6292850: неверно, это страница от предыдущего поддерживающего glibc, Ульрих Дреппер, о том, как выполнять динамически сгенерированный код. Он не может быть использован как атака, либо вы уже можете выполнить произвольный код, и вам не нужно это делать, иначе вы этого не сделаете, и вы не сможете его выполнить. – ninjalj

1

Очень зависит от ОС: не все операционные системы намеренно (чтение: без ошибок) позволяют выполнять код в сегменте данных. DOS, потому что он работает в режиме реального времени, Linux также может иметь соответствующие привилегии. Я не знаю о Windows.

Кастинг часто неопределен и имеет свои собственные оговорки, поэтому некоторые разработки по этой теме здесь. От C11 проект стандарта N1570, §J.5.7/1:

указатель на объект или на void может быть брошен в указатель на функцию, позволяя данные, которые будут служить функции (6.5. 4).

(добавлено форматирование.)

Таким образом, это совершенно нормально и должно работать, как ожидалось. Конечно, вам нужно будет соединить с конвенцией ABI.

+0

@ Downvoter Ум объяснить? – Downvoter

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

  • Нет связанных вопросов^_^