在 Unix 上(现在,这意味着“除了 Windows 和一些你可能从未听说过的嵌入式和大型机东西之外的所有东西”),你可以通过分配全部页数来实现这一点mmap http://linux.die.net/man/2/mmap,将代码写入其中,然后使它们可执行mprotect http://linux.die.net/man/2/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 代码非常相似樱桃的回答 https://stackoverflow.com/a/37122333/388520。只是系统调用的名称和参数不同。
当使用这样的代码时,重要的是要知道许多现代操作系统不允许您拥有这样的 RAM 页:同时地可写和可执行。如果我写的是PROT_READ|PROT_WRITE|PROT_EXEC
在通话中mmap
or mprotect
,它会失败。这被称为W^X政策 https://en.wikipedia.org/wiki/W%5EX;该缩写代表 Write XOR eXecute。它起源于 OpenBSD http://www.openbsd.org/papers/ven05-deraadt/index.html,其想法是让缓冲区溢出漏洞更难将代码写入 RAM 然后执行。 (仍然是possible,该漏洞利用只需找到一种方法来进行适当的调用mprotect first http://cseweb.ucsd.edu/~hovav/talks/blackhat08.html.)