Linux ASLR的实现
ASLR大家都会听说过,但是Linux平台下应用程序的ASLR的情况是怎么样的呢?我在这里将ASLR分为几个小的部分来阐述,包括了栈的随机化,堆的随机化,mmap的随机化,以及pie程序运行时的主模块随机化。目的是为了知道随机化了哪些比特,随机了多少比特。
我在这里以Linux 4.11.4进行分析,以x64为代表,分析通过静态观察内核的源代码和动态调试内核源代码结合。在这里分析的是用户态程序的ASLR,由于用户态程序的ASLR是在程序加载执行过程中完成的,所以分析的起点应该是程序的加载执行。程序的加载执行从execve系统调用开始,到do_execve,到do_exeveat_common[1],因此就沿着这条主线一直走下去。
按照这个主线看下去,第一个见到的随机化是mmap的随机化[2],mmap有两种方式,早期是从低地址空间向高地址空间分配,如今是从高地址空间向低地址空间分配。mmap随机的位数由mmap_rnd_bits表示,在64位下是28比特,经过计算在64位平台下mmap的基地址是:page_align(0x7ffbf8000000-rand),而其中的rand在是28比特的数字左移12位。当mmap的基地址确定后,在各个系统中,程序运行起来时各个模块(不包括pie程序的主模块、但包括各个动态链接库)与mmap的基地址的偏移是固定的,因此这些模块加载地址的随机化也在28比特。
紧接着是栈的随机化,栈的随机化在3个地方有涉及。第一是[3],在这里将STACK_TOP进行了随机化。没随机化前的STACK_TOP的值是0x00007ffffffff000L,栈的随机化的位数由STACK_RND_MASK决定,STACK_RND_MASK在x64下是0x3fffff,也就是说栈的随机位数是22比特,经过计算,这里将STACK_TOP的值随机化为page_align(0x00007ffffffff000L-rand),而其中rand是22比特的随机数左移12位(即0到0x3fffff个页大小)。第二是[4],这里将STACK_TOP减去0或者1个页大小。第三个是[5],将栈内存高地址随机减去了0到8192,并进行了16字节对齐[5]。下图是一个程序运行中栈布局的图,1和2分别是两处随机化。
如果程序是pie的,那么程序主模块也会被随机化,见[8],这里主模块基地址被随机化为ELF_PAGESTART(0x555555554aaa+rand),rand由arch_mmap_rnd生成,和mmap随机化的位数是一样的,都是28比特。
下面是堆的随机化,堆空间的位置由start_brk和brk确定[6],程序运行最初start_brk和brk指向同一个位置,即data/bss段的下一个页面。堆的随机化的实现在[7],start_brk被随机化为[old_start_brk, old_start_brk+0x02000000)中页面对齐的值。
调试内核使用的是qemu,关于如何调试Linux内核网上有很多的文章。前期的准备就是一个编译好的Linux kernel、一个已经安装了Linux系统的硬盘镜像和一个gdb调试器。关于编译Linux kernel,尽可能少的编译优化对于调试会有不少帮助,但是Linux kernel并不能整体在-O0下编译,但是可以对单个源码文件或者单个函数以-O0形式进行编译,我们针对性地选择哪些不要优化[9][10]。关于准备安装了Linux系统的硬盘镜像,一种是自己手动创建硬盘并进行安装系统[12],例外一种就是使用网上已经存在的硬盘镜像,我用的是网上已经存在的Ubuntu Cloud Images[11]。关于gdb调试器,依赖于guest系统是什么系统,如果guest系统是ARM Linux,那么就需要一个ARM平台下的GDB。一种方法是手动的交叉编译,另外一种就是安装gdb-multiarch,它支持多种平台下的GDB。
附一下我用的命令行:qemu-system-x86_64 -s -S -hda xenial-server-cloudimg-amd64.img -initrd initrd.img-4.4.0-78-generic -kernel arch/x86/boot/bzImage -drive file=fat:rw:/media/sf_E_DRIVE/tools/mykey/ -redir tcp:5555::22 -append 'root=/dev/sda rw init=/usr/lib/cloud-init/uncloud-init ds=nocloud mem=1024M'
[1] http://elixir.free-electrons.com/linux/v4.11.4/source/fs/exec.c#L1660
[2] http://elixir.free-electrons.com/linux/v4.11.4/source/arch/x86/mm/mmap.c#L101
[3] http://elixir.free-electrons.com/linux/v4.11.4/source/fs/binfmt_elf.c#L875
[4] http://elixir.free-electrons.com/linux/v4.11.4/source/fs/exec.c#L696
[5] https://lwn.net/Articles/631631/
[6] https://sploitfun.wordpress.com/2015/02/11/syscalls-used-by-malloc/
[7] http://elixir.free-electrons.com/linux/v4.11.4/source/fs/binfmt_elf.c#L1078
[8] http://elixir.free-electrons.com/linux/v4.11.4/source/fs/binfmt_elf.c#L939
[9] http://www.alterawiki.com/wiki/Debug_Kernel