有没有什么办法可以把处理器指令到阵列,使其内存段可执行文件并运行它作为一个简单的功能:C:把x86指令到数组,并执行它们
int main()
{
char myarr[13] = {0x90, 0xc3};
(void (*)()) myfunc = (void (*)()) myarr;
myfunc();
return 0;
}
有没有什么办法可以把处理器指令到阵列,使其内存段可执行文件并运行它作为一个简单的功能:C:把x86指令到数组,并执行它们
int main()
{
char myarr[13] = {0x90, 0xc3};
(void (*)()) myfunc = (void (*)()) myarr;
myfunc();
return 0;
}
在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);
}
你或许可以看到,这个代码是非常接近相同cherrydt's answer所示的Windows代码。只有系统调用的名称和参数不同。
当使用这样的代码时,知道很多现代操作系统不允许你有一页的RAM同时可写和可执行。如果我在mmap
或mprotect
的电话中写入PROT_READ|PROT_WRITE|PROT_EXEC
,则会失败。这叫做W^X policy;首字母缩写词表示写XOR执行。它的想法是让缓冲区溢出攻击难以将代码写入RAM然后执行它。 (它仍然是可能,该漏洞只需要find a way to make an appropriate call to mprotect
first。)
取决于平台。
对于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)你可以呼吁&myarr
直接VirtualProtect
,但这只会使你的记忆随机页面恰好包含您的阵列执行的,这是更糟然后#1,因为有可能是在这个页面,因为其他数据以及现在突然可执行的。
对于Linux,我在Google上发现了this,但我对此不太了解。
对于Linux:http://web.archive.org/web/20090203055327/http://people.redhat.com/drepper/selinux-mem.html – ninjalj
@ninjalj我希望你认识到这是一个关于如何攻击的页面* SELinux。我们不需要将SELinux视为障碍的人。请参阅[zwol的答案](http://stackoverflow.com/a/37122499/6292850)了解正确的方法。 –
@ user6292850:不正确,这是前一个glibc维护者Ulrich Drepper关于如何执行动态生成代码的页面。它不能被用作攻击,要么你已经可以执行任意代码,并且不需要这样做,否则你不能执行任何代码,并且你将无法执行该代码。 – ninjalj
非常依赖操作系统:并非所有的操作系统都会故意(读取:没有错误)允许您在数据段中执行代码。 DOS会因为它在真实模式下运行,Linux也可以具有适当的权限。我不知道Windows。
投射往往是不确定的,并有其自己的警告,所以在这里的一些细节在这个话题。从C11 N1570标准草案,§J.5.7/ 1:
一个指向对象或
void
可以 被转换为指针于函数时,允许数据被调用作为 函数(6.5。 4)。
(格式化所加)
所以,这是完全正常和预期应该工作。当然,你需要坚持ABI的调用习惯。
@Downvoter心灵解释? – Downvoter
这看起来像[XY问题](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem )。告诉我们在你寻求帮助解决某种特定方式之前,你想要解决什么问题。 –
我想在我的程序中生成x86代码,然后运行它。 –
你的意思是说Windows中的[VirtualProtect](https://msdn.microsoft.com/en-us/library/windows/desktop/aa366898(v = vs.85).aspx)? – Tomer