代码必须位于具有执行权限的页面中。默认情况下,出于安全原因,堆栈和读写静态数据(如非常量全局变量)位于未经 exec 权限映射的页面中。
最简单的方法是编译gcc -z execstack
,它链接您的程序,以便堆栈and全局变量(静态存储)被映射到可执行页面中,分配也是如此malloc
.
另一种无需制作的方法一切可执行文件就是将这个二进制机器代码复制到可执行缓冲区中。
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>
char code[] = {0x55,0x48,0x89,0xe5,0x89,0x7d,0xfc,0x48,
0x89,0x75,0xf0,0xb8,0x2a,0x00,0x00,0x00,0xc9,0xc3,0x00};
/*
00000000004004b4 <main> 55 push %rbp
00000000004004b5 <main+0x1> 48 89 e5 mov %rsp,%rbp
00000000004004b8 <main+0x4> 89 7d fc mov %edi,-0x4(%rbp)
00000000004004bb <main+0x7> 48 89 75 f0 mov %rsi,-0x10(%rbp)
'return 42;'
00000000004004bf <main+0xb> b8 2a 00 00 00 mov $0x2a,%eax
'}'
00000000004004c4 <main+0x10> c9 leaveq
00000000004004c5 <main+0x11> c3 retq
*/
int main(int argc, char **argv) {
void *buf;
/* copy code to executable buffer */
buf = mmap (0,sizeof(code),PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANON,-1,0);
memcpy (buf, code, sizeof(code));
__builtin___clear_cache(buf, buf+sizeof(code)-1); // on x86 this just stops memcpy from optimizing away as a dead store
/* run code */
int i = ((int (*) (void))buf)();
printf("get this done. returned: %d", i);
return 0;
}
output:
完成这件事。返回:42
运行成功(总时间:57ms)
Without __builtin___clear_cache
, 这可能会打破 https://stackoverflow.com/questions/35741814/how-does-builtin-clear-cache-work#comment85964322_35741869启用优化,因为 gcc 会认为memcpy
是一个死店并优化它。当编译 x86 时,__builtin___clear_cache
does not实际上清除所有缓存;额外指令为零;它只是将内存标记为“已使用”,因此对其进行的存储不会被视为“已死”。 (看海湾合作委员会手册 https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html.)
另一种选择是mprotect
页面包含char code[]
数组,给它PROT_READ|PROT_WRITE|PROT_EXEC
。无论它是本地数组(在堆栈上)还是全局数组,这都有效.data
.
或者如果是const char code[]
in the .rodata
部分,你可能只是给它PROT_READ|PROT_EXEC
.
(在 binutils 版本中ld
从2019年左右开始,.rodata
作为同一段的一部分链接为.text
,并且已经映射为可执行文件。但最近ld
给它一个单独的段,这样就可以在没有 exec 权限的情况下映射它const char code[]
不再为您提供可执行数组,但它曾经提供过,因此您可以在其他地方使用这个旧建议。)