ASLR 全称 Address Space Layout Randomization,是一项 Linux 内核的安全措施,使应用程序每次加载到内存后,函数地址都不同。
试用一下
先来直观的感受下什么是 ASLR。目前大多数 linux 系统都默认开启了这个选项,可以用一下两个命令确认一下系统是否支持 ASLR。
$ cat /proc/sys/kernel/randomize_va_space
2
$ sysctl kernel.randomize_va_space
kernel.randomize_va_space = 2
其中 0 表示关闭,1 表示有约束的随机,2 表示完全随机化。
然后随便找一个可执行程序,用 ldd 命令显示它加载的动态链接库,可以看到两次运行 ldd 结果各个库的地址不一样。
$ ldd /bin/sleep
linux-vdso.so.1 (0x00007ffd49764000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f02783ae000)
/lib64/ld-linux-x86-64.so.2 (0x00007f02789a8000)
$ ldd /bin/sleep
linux-vdso.so.1 (0x00007ffc10996000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f12c3534000)
/lib64/ld-linux-x86-64.so.2 (0x00007f12c3b2e000)
应用程序如何使用 ASLR
在这篇文章中提到,除了 kernel 开启以外,应用程序在编译的时候也必须添加编译选项 gcc -fPIE -pie test.c
。
但是在我的实际测试中,似乎并不需要额外添加编译选项,看来 gcc 默认开启了 ASLR。
#include <stdlib.h>
#include <stdio.h>
void getAddr() {
printf("hello, world\n");
}
int main() {
printf("getAddr at: %p\n", getAddr);
return 0;
}
或者
#include <stdlib.h>
#include <stdio.h>
void* getAddr () {
return __builtin_return_address(0)-0x5;
};
int main(int argc, char** argv){
printf("Code located at: %p\n",getAddr());
return 0;
}
无需添加编译选项,上面两段代码都能实现地址的随机。
$ gcc va_random.c
$ ./a.out
getAddr at: 0x55b17f24268a
$ ./a.out
getAddr at: 0x5629e042c68a
$ ldd a.out
linux-vdso.so.1 (0x00007ffd5239a000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f666ee1f000)
/lib64/ld-linux-x86-64.so.2 (0x00007f666f412000)
$ ldd a.out
linux-vdso.so.1 (0x00007fff8c5c0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4b77a96000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4b78089000)
另外,关于 __builtin_return_address
gcc 内置函数,可参考资料1