上次是直接在可执行的栈中写入指令并转移至执行的,这次在当栈和堆都不可执行的情况下,分配空间、写入指令并执行。还是直接看代码吧。
#include
#include
#include mman.h>
int main(int argc, char *argv[])
{
unsigned int *insn = NULL;
insn = mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(MAP_FAILED != insn)
{
printf("%p\n", insn);
insn[0] = 0x1000ffff;
insn[1] = 0x00000000;
__asm__ volatile (
"jr %0 \n\t"
"nop \n\t"
::"r"(insn)
);
munmap(insn, 4096);
}
else
{
printf("%s\n", strerror(errno));
}
return 0;
}
在上面的代码中,使用系统调用 mmap 分配了一块 4096 字节的可读写、可执行的私有、匿名 VMA,并在此 VMA 中写入指令并转移至执行了。
我们来看一下此程序的运行时输出与 maps:
0x2ac88000
00400000-00404000 r-xp 00000000 08:04 262145 /home/heiher/tmp/test
00410000-00414000 rw-p 00000000 08:04 262145 /home/heiher/tmp/test
2ac64000-2ac88000 r-xp 00000000 08:02 917518 /lib/ld-2.14.so
2ac88000-2ac8c000 rwxp 00000000 00:00 0
2ac94000-2ac98000 rw-p 00020000 08:02 917518 /lib/ld-2.14.so
2ac98000-2aca8000 rw-p 00000000 00:00 0
2acb4000-2ae0c000 r-xp 00000000 08:02 918040 /lib/libc-2.14.so
2ae0c000-2ae18000 ---p 00158000 08:02 918040 /lib/libc-2.14.so
2ae18000-2ae1c000 r--p 00154000 08:02 918040 /lib/libc-2.14.so
2ae1c000-2ae20000 rw-p 00158000 08:02 918040 /lib/libc-2.14.so
2ae20000-2ae24000 rw-p 00000000 00:00 0
7fc04000-7fc28000 rwxp 00000000 00:00 0 [stack]
7fff4000-7fff8000 r-xp 00000000 00:00 0 [vdso]
我们看到确定多出了一个我们所需要的 VMA:
2ac88000-2ac8c000 rwxp 00000000 00:00 0
但其长度却不是 4096 字节,而是 16K(16384)。这是因为我们当前的操作系统页面长度是 16K(16384),分配一个VMA至少需要一个完整的页面,当小于它的时候也需要分配一个完整的页面。
如何让进程的栈和堆都不可执行呢?有没有办法可以修改代码段的数据?在上面分配了 4096 字节的内存,而实际则是 16K,那么其 4096 之后的空间是否可以使用呢?动态生成指令随后立即执行是否有问题呢?
Over!