Linux 关闭地址空间随机化 ASLR

在编写 Linux 简单的调试器代码时,调试发现每次 ptrace 获取的 IP 寄存器都不一样,地址总是在变化,于是联想到 Linux 内核的 ASLR 机制,关闭地址空间随机化后,IP 指针值就固定下来了。

开启 ASLR 后,每次程序运行时的时候,装载的可执行文件和共享库都会被映射到虚拟地址空间的不同地址处;而关掉 ASLR,则可以保证每次运行时都会被映射到虚拟地址空间的相同地址处。

地址空间随机化 ASLR

地址空间随机化 ASLR(Address Space Layout Randomization)配置通过内核参数 kernel.randomize_va_space 来实现,该参数取值范围为:0,1,2。系统默认开启 2 全随机模式。

  • 0:表示关闭进程地址空间随机化。
  • 1:表示部分随机化,将共享库、栈、mmap () 以及 VDSO 进行随机化。
  • 2:表示全部随机化,在 1 的基础上增加 heap 的随机化。

关闭随机化

查看:

1
cat /proc/sys/kernel/randomize_va_space

关闭(切换到 root 后再执行命令):

1
2
sudo su
echo 0 > /proc/sys/kernel/randomize_va_space

当使用 gdb 调试一个程序时,GDB 会自动关掉 ASLR。可以通过以下命令将它打开:

1
2
(gdb) set disable-randomization off
(gdb) info poc map # 查看进程地址映射表

测试 demo

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>

unsigned long get_sp(void) {
asm("mov %rsp, %rax\n");
}

int main(int argc, const char *argv[])
{
unsigned long sp = get_sp();
printf("SP ==> 0x%lx\n", sp);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@ubuntu:~# gcc test.c -o test
root@ubuntu:~# echo 2 > /proc/sys/kernel/randomize_va_space
root@ubuntu:~# ./test
SP ==> 0x7ffeccac2e60
root@ubuntu:~# ./test
SP ==> 0x7fffb5a29c70
root@ubuntu:~# ./test
SP ==> 0x7ffd1005e3b0
root@ubuntu:~# echo 0 > /proc/sys/kernel/randomize_va_space
root@ubuntu:~# ./test
SP ==> 0x7fffffffe500
root@ubuntu:~# ./test
SP ==> 0x7fffffffe500
root@ubuntu:~# ./test
SP ==> 0x7fffffffe500

get 小技巧

关掉 ASLR,可以保证在可执行程序和共享库不发生变更的情况下,每次执行时的进程地址空间映射表的一致。我们可通过运行时某动态符号(不知其名)的地址,减去其所在的共享库在地址映射表中起始地址,算出它相对于共享库数据段中的偏移,然后借助 objdump、readelf, nm 等工具查看对应 ELF 文件中全局符号的相对偏移,就可以反推出该符号的名字。这种调试手段对于那些被 strip 掉符号表的程序而言非常有效。