是否可以通过分配动态内存创建动态函数,为其编写一些汇编程序操作码(如 0x90 0xC2 NOP RET ),创建一个指向动态内存的函数指针,并像C程序中的常规函数一样执行它?
Target应该是常规的x86 Linux系统。
答案 0 :(得分:17)
一般来说是的,但你需要进入系统特定的事情。我猜这并不奇怪,因为你要使用二进制汇编指令的事实非常清楚。
您需要注意,您不能假设现代操作系统上的堆内存是可执行的,因此您可能需要跳过一些环节来实现它。您不能只调用malloc()
并假设返回的指针指向可以执行代码的内存。
在Linux中,您可以使用mmap()
要求内核为您映射一些内存,并通过在调用中指定PROT_EXEC
标志,您可以要求它使内存可执行。
答案 1 :(得分:3)
此内存没有堆内存(请参阅下面的注释)。而且,我认为你不能改变堆内存的执行权限。
在Linux上,您可以使用以下内容:
#include <sys/mman.h>
size_t size = 0x1000; // 1 page
// this will be mapped somewhere between /lib/x86_64-linux-gnu/ld-2.15.so
// and stack (see note and memory map below)
void *code = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS, -1, 0);
// use `code` to write your instructions
// then store location at which you want to jump to in `fp`
void *fp = ...;
mprotect(code, size, PROT_READ | PROT_EXEC);
// use some inline assembly to jump to fp
注意:在Linux上,用户映射的内存位于不同的区域(类似于从400000000000
到x86 Linux上的堆栈,在x64上可能是7f0000000000
一)。堆位于程序的ELF段之后和区域之前,可用于mmap
。可以使用brk
系统调用直接分配堆本身(现在已被malloc
取代)。看到这个例子(上了我的Ubuntu 12.10 x64):
➜ ~ ps
PID TTY TIME CMD
9429 pts/3 00:00:07 zsh
20069 pts/3 00:00:00 git-credential-
22626 pts/3 00:00:00 ps
➜ ~ cat /proc/9429/maps
00400000-004a2000 r-xp 00000000 08:01 6291468 /bin/zsh5
006a1000-006a2000 r--p 000a1000 08:01 6291468 /bin/zsh5
006a2000-006a8000 rw-p 000a2000 08:01 6291468 /bin/zsh5
006a8000-006bc000 rw-p 00000000 00:00 0
01a51000-01fd8000 rw-p 00000000 00:00 0 [heap]
...
7f6529d61000-7f6529d91000 rw-p 00000000 00:00 0
...
7f652d0d3000-7f652d0d5000 rw-p 00023000 08:01 44833271 /lib/x86_64-linux-gnu/ld-2.15.so
7fffd7c7f000-7fffd7cae000 rw-p 00000000 00:00 0 [stack]
7fffd7dff000-7fffd7e00000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
如您所见,heap
不可执行(并且是正确的),因此您无法使用malloc
来获取可执行内存。
答案 2 :(得分:2)
许多系统长期以来都在虚拟内存页面上有标志,告诉它们是否可以包含可执行代码。分配给最有可能的堆的内存不会设置此“可执行”标志。所以不,你不能直接这样做。
如果你想这样做,你必须使用操作系统特定的功能,并且可能必须以“管理员”或“root”运行程序才能够执行它,尽管似乎没有必要。< / p>