Как уже говорилось, это бесполезно, но это не так уж далеко от того, что компиляторы работают точно вовремя, или от исполняемого загрузчика ОС.
- Как Карлговорит, вам нужно поместить машинные инструкции в буфер, а не в C или сборку.
- Это означает, что вы выполняете всю работу по соблюдению ABI самостоятельно.
- Это также означает, что вы получаете никакой переносимости .Код, который делает подобные вещи, должен быть написан N раз - один раз для каждой комбинации CPU + OS, которая вас интересует.Компиляторы «точно в срок» обычно используют резервный интерпретатор байт-кода для еще не поддерживаемых платформ.
- По соображениям безопасности вы не можете просто выгрузить машинный код вбуферизуйтесь и переходите к нему;Вы должны сообщить ОС, что вы делаете.
Как это случилось, у меня есть пример, лежащий рядом.Это проверено только для работы на (x86 или x86-64) / Linux.Если вы хотите, чтобы это делало что-то более интересное, вам нужно заменить memset
кодом, который заполнил буфер, машинным кодом для более интересной операции.
Это не будет работает на любом другом процессоре, поскольку он жестко связывает машинную кодировку x86 с инструкцией возврата.Он , вероятно, не будет работать на любой другой ОС x86, потому что он игнорирует минное поле переносимости, окружающее mmap
и mprotect
: - (
#define _GNU_SOURCE 1
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void __attribute__((noreturn))
perror_exit(const char *msg)
{
perror(msg);
exit(1);
}
int main(void)
{
/* allocate one writable page */
size_t pagesize = getpagesize();
void *rgn = mmap(0, pagesize, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANON, -1, 0);
if (rgn == MAP_FAILED)
perror_exit("mmap");
/* fill it with return instructions */
memset(rgn, 0xC3, pagesize);
/* now switch the page from writable to executable */
if (mprotect((caddr_t)rgn, pagesize, PROT_READ|PROT_EXEC))
perror_exit("mprotect");
/* now we can call it */
((void (*)(void))rgn)();
return 0;
}