先来看这样一个代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int *p = (int*)malloc(sizeof(int));
*p = 0;
while(1){
printf("[%d]addr: %p, value: %d\n", getpid(), p, *p);
*p = *p + 1;
sleep(1);
}
return 0;
}
代码很简单,在堆上分配一个int大小的内存空间,先将这个内存空间清零,然后在一个死循环中打印进程id,该内存空间的地址,以及该内存空间保存的数据,然后将其中的数字加1,睡眠1秒钟,如此循环往复。
我们将其编译成可执行文件,并命名为addr_randomize
gcc -o addr_randomize addr_randomize.c
如果我们在命令行同时运行这个程序的两个实例,看看会发生什么:
[-bash-4.2 $]./addr_randomize & ./addr_randomize &
[1] 2139
[2] 2140
[-bash-4.2 $]
[2139]addr: 0x912010, value: 0
[2140]addr: 0xda2010, value: 0
[2139]addr: 0x912010, value: 1
[2140]addr: 0xda2010, value: 1
[2139]addr: 0x912010, value: 2
[2140]addr: 0xda2010, value: 2
[2140]addr: 0xda2010, value: 3
[2139]addr: 0x912010, value: 3
[2140]addr: 0xda2010, value: 4
可以看到启动了两个进程,一个pid为2139,另一个为2140.这里我们关注这个指针p所指向的地址,2139进程,p指向0x912010,2140进程,p指向0xda2010。
同一个可执行文件,每次执行时分配给p的内存地址是随机的,这是Linux内核的一种叫做Linux内核地址空间布局随机化(ASLR)的特性。目的是增大黑客预测某一个函数或者某一个数据结构的难度。
Linux kernel's Address Space Layout Randomization (ASLR). ASLR is a security feature that randomizes the memory addresses used by system processes, making it harder for attackers to predict the locations of specific functions or data structures.
那如果想禁用这个特性,应该怎么做呢?
使用setarch命令
setarch $(uname -m) --addr-no-randomize your_program_name
其中uname -m 返回你的机器的架构,在我的机器上,它返回x86_64,addr-no-randomize选项可以禁用某一个可执行文件的ASLR。
[-bash-4.2 $]setarch $(uname -m) --addr-no-randomize ./addr_randomize & setarch $(uname -m) --addr-no-randomize ./addr_randomize &
[1] 3146
[2] 3147
[-bash-4.2 $]
[3146]addr: 0x602010, value: 0
[3147]addr: 0x602010, value: 0
[3146]addr: 0x602010, value: 1
[3147]addr: 0x602010, value: 1
[3146]addr: 0x602010, value: 2
[3147]addr: 0x602010, value: 2
[3146]addr: 0x602010, value: 3
[3147]addr: 0x602010, value: 3
可以看到,在禁用了ASLR之后,进程3146和进程3147中p指向的地址都是0x602010。
虽然这两个进程中p都指向0x602010, 并不意味着两个进程中的p真的指向同一个物理内存地址。因为0x602010是逻辑地址,还要经过地址单元的翻译,才会映射到最终的物理地址。
上述命令只对某一个进程起作用。如果要对所有进程都禁用ASLR,可以修改这个参数:kernel.randomize_va_space
sudo sysctl -w kernel.randomize_va_space=0
当系统重启后,这个参数会恢复默认值1
如果要保持重启后依然有效,需要将其写入配置文件/etc/sysctl.conf
kernel.randomize_va_space = 0
编辑完 /etc/sysctl.conf 后,执行下面的命令使更改生效:
sudo sysctl -p
需要注意的事,正如前面所说,修改这个参数会降低系统的安全系数,最好不要去改,除非有特殊的需要并且你很清楚这么做的后果。