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

  1. https://learning-kernel.readthedocs.io/en/latest/c_skills.html