如何临时禁用ASLR(地址空间布局随机化)?

我正在使用Ubuntu 12.04 32位系统,为了进行一些实验,我需要禁用ASLR;我应该如何做到这一点?在禁用ASLR之后,我应该做什么来重新启用它?

2仅供自己使用:http://stackoverflow.com/questions/11238457/disable-and-re-enable-address-space-layout-randomization-only-for-mysef || http://unix.stackexchange.com/questions/15881/disable-address-space-layout-randomization-aslr-for-my-processes - Ciro Santilli OurBigBook.com
5个回答

根据一篇文章Linux系统上ASLR的有效性有多大?,你可以使用/proc/sys/kernel/randomize_va_space接口在Linux中配置ASLR。
以下是支持的数值选项: - 0 - 无随机化。所有内容都是静态的。 - 1 - 保守随机化。共享库、堆栈、mmap()、VDSO和堆都会被随机化。 - 2 - 完全随机化。除了前一点列出的元素外,通过brk()管理的内存也会被随机化。
因此,要禁用它,请运行:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

并且要重新启用它,运行以下命令
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space

这个在重启后就不会生效了,所以你需要在sysctl中进行配置。添加一个文件/etc/sysctl.d/01-disable-aslr.conf,文件内容如下:
kernel.randomize_va_space = 0

应该永久禁用此功能。

1"全面随机化"到底是什么意思?这是否包括可执行文件本身?另外,brk()是什么? - Shuzheng
@Shuzheng brk()是在内存分配过程中的断点地址。详细信息请参考https://stackoverflow.com/questions/6988487/what-does-the-brk-system-call-do - Niklas Rosencrantz
@Niklas - 以上的任何级别中是否包含可执行的核心本身? - Shuzheng
@Shuzheng 我是这么认为的,也就是说,为了成功地引发缓冲区溢出,我需要将其关闭。如果启用了ASLR,即使攻击者能够注入和劫持程序流,他也无法轻易计算运行进程的内存地址。在级别1中,如果我理解正确的话,进程的绝对地址和相对地址都会被随机化,在级别2中,动态内存地址也会被随机化。你可以在虚拟机或容器中尝试禁用ASLR并尝试劫持进程的示例程序(搜索溢出)。 - Niklas Rosencrantz
如果你的意思是ASLR可以在Linux内核本身中禁用和启用,那我就不太清楚了...但肯定会在手册中有相关内容。 - Niklas Rosencrantz
@Niklas,目前还不清楚代码段的基地址是否会加载到随机地址,只知道库等会加载到随机地址。 - Shuzheng
只有可执行的PIE(Position Independent Executable)文件才能对其文本/数据/bss进行随机化。(这就是为什么Ubuntu和其他现代发行版默认配置GCC以构建PIEs,并以此方式构建二进制软件包)。传统的ELF(Executable and Linkable Format)可执行文件包含绝对地址,并且没有元数据可以在运行时修复它们,即使有人想尝试让内核这样做也不行。为什么非PIC(Position Independent Code)代码无法使用运行时修复而完全实现ASLR? - Peter Cordes
一个PIE可执行文件被视为一个共享库,并且应该在randomize_va_space = 1下对其文本/数据/BSS进行ASLR。它实际上是一个带有入口点的ELF共享对象;这在历史上是一个愚蠢的计算机技巧,但偶然间起作用;现在它是构建可执行文件的标准方式。 - Peter Cordes

/proc/sys/kernel/randomize_va_space 接口用于控制系统范围的ASLR。

如果您不想进行系统范围的更改,请使用 ADDR_NO_RANDOMIZE personality 标志来临时禁用ASLR。可以使用 setarch 和其 -R 选项来控制该标志,例如:

setarch `uname -m` -R /bin/bash

这将为您打开一个新的 Bash shell,其中 ASLR 被禁用,包括从此 shell 运行的所有子进程。完成后,只需exit即可退出 shell。


顺便说一下,在i386上,ulimit -s unlimited可以有效地“禁用”ASLR。
编辑(2016年4月):已修复ulimit -s unlimited并分配了CVE-2016-3672

1在util-linux的精神中有一个小细节:可以使用arch代替uname -m,因为这个二进制文件本质上也是做同样的事情。 - drumfire
1@drumfire arch 在 busybox 中不可用作 applet。 - youfu
1+1 为你两年后回来并添加关于CVE的信息。 - Multisync

如果你想构建一个程序,在运行时禁用 ASLR,你可以在 Linux 上使用 personality 系统调用。以下是一个示例:
#include <stdio.h>
#include <sys/personality.h>

int main(int argc, char **argv) {
    const int old_personality = personality(ADDR_NO_RANDOMIZE);
    if (!(old_personality & ADDR_NO_RANDOMIZE)) {
        const int new_personality = personality(ADDR_NO_RANDOMIZE);
        if (new_personality & ADDR_NO_RANDOMIZE) {
            execv(argv[0], argv);
        }
    }
    printf("&argc == %p\n", (void *) &argc);
}

如果你查看setarch的源代码,它以大致这种模式两次调用personality。主要区别在于setarch调用其他程序上的exec,而我的配方则执行自身的exec。重要的是使用& ADDR_NO_RANDOMIZE的非零性而不是相等性测试:否则,如果你使用-z execstack进行编译,可能会进入无限的exec循环。

另请参阅personality手册页


更永久地禁用ASLR的方法应该保留在虚拟机中,出于明显的原因。
为了测试覆盖堆栈帧返回地址等能力,您需要编译时禁用堆栈保护器-fno-stack-protector,而要允许在堆栈上执行代码,您需要使用-z execstack进行编译。
$ gcc -fno-stack-protector -z execstack -o <my_program> my_code.c

在主机上启用了ASLR时,在虚拟机中禁用ASLR并不那么容易。我发现临时禁用ASLR总是更好一些。 - undefined

您可以使用以下命令临时禁用ASLR。
sudo sysctl kernel.randomize_va_space=0