15

是否可以通过分配动态内存来创建动态函数,向其写入一些汇编器操作码(如NOP RET0x90 0xC2),创建指向该动态内存的函数指针并像从 C 程序中的常规函数​​一样执行它?

目标应该是常规的 x86 Linux 系统。

4

3 回答 3

18

一般来说是的,但你需要进入特定于系统的事情才能做到这一点。我想这并不奇怪,因为您将使用二进制汇编指令这一事实已经很清楚了。

您需要注意,您不能假设现代操作系统上的堆内存是可执行的,因此您可能需要跳过一些环节才能做到这一点。您不能只调用malloc()并假设返回的指针指向您可以执行代码的内存。

在 Linux 中,您可以使用mmap()请求内核为您映射一些内存,并通过PROT_EXEC在调用中指定标志,您可以请求它也使内存可执行。

于 2013-10-28T12:19:52.740 回答
4

此内存不是堆内存(请参阅下面的注释)。此外,我认为您无法更改堆内存的执行权限。

在 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 上,用户映射的内存位于单独的区域(类似于400000000000x86 Linux 上的 from 和 up to stack,可能7f0000000000在 x64 上)。堆位于程序的 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来获取可执行内存。

于 2013-10-28T13:32:25.000 回答
2

很长一段时间以来,许多系统在虚拟内存页面上都有标志,告诉它们是否可以包含可执行代码。为堆分配的内存很可能不会设置此“可执行”标志。所以不,你不能直接这样做。

如果你想这样做,你必须使用操作系统特定的功能,并且可能必须以“管理员”或“root”身份运行程序才能做到这一点,尽管这似乎没有必要。

于 2013-10-28T12:19:37.470 回答