关于Linux下ASLR与PIE的一些理解

前言:技术总是处在不断发展的过程,各种技术的细节和涵义也在不断的变迁,因而我们很难像数学给每种理论下一个严格的定义一样去对待技术,这就是为什么我们对很多技术的定义甚是困惑,因为它是不严谨的,是在历史的长河中不断变迁的,在变迁的过程中又有可能是自相矛盾的。我们更应该从思想和动机去理解技术而不是定义,毕竟这是工程技术而不是严谨的科学理论。

以下是我对Linux中的ASLR和PIE的理解的两个阶段,对比来看很有意思,虽然很无聊 :) 

Level 1

——————————————————————————————————我是分界线:)———————————————————————————————————————————————————————

根据翻阅的资料,得出以下结论,记录以备忘:

  首先,ASLR的是操作系统的功能选项,作用于executable(ELF)装入内存运行时,因而只能随机化stack、heap、libraries的基址;而PIE(Position Independent Executables)是编译器(gcc,..)功能选项(-fPIE),作用于excutable编译过程,可将其理解为特殊的PIC(so专用,Position Independent Code),加了PIE选项编译出来的ELF用file命令查看会显示其为so,其随机化了ELF装载内存的基址(代码段、plt、got、data等共同的基址)。

  其次,ASLR早于PIE出现,所以有return-to-plt、got hijack、stack-pivot(bypass stack ransomize)等绕过ASLR的技术;而在ASLR+PIE之后,这些bypass技术就都失效了,只能借助其他的信息泄露漏洞泄露基址(常用libc基址)。

  最后,ASLR有0/1/2三种级别,其中0表示ASLR未开启,1表示随机化stack、libraries,2还会随机化heap。

 

总结: ASLR(on process)、PIE(on executable)

 

实例验证:

写一个简单的程序test.c

编译两个版本:

gcc -o test test.c

gcc -fpie -pie -o test-pie test.c

file查看:

root@ubuntu:/home/jiangxin/test# file test
test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=97e4e8460d8eb27a452c49ce052e27003f89f399, not stripped

root@ubuntu:/home/jiangxin/test# file test-pie
test-pie: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=08ef21b96636a1757134e4377638022ce050b2e2, not stripped

ASLR 1 & no PIE 下查看进程内存空间: 

ASLR 2 & PIE 下查看进程内存空间:

 

 Level 2

——————————————————————————————————我是分界线:)———————————————————————————————————————————————————————

上述理解有一个明显的缺陷,那就是PIE只是在编译的过程中赋予了ELF加载到内存时其加载基址随机化的功能,也就是说PIE编译出来的ELF如果在ASLR=0的情况下,ELF的加载基址也是不会变的。

为什么呢,因为一个是能力赋予,一个是真正使用能力。

这个是有历史原因的,ASLR刚开始设计的时候是作为操作系统功能提供的,只考虑了当时技术背景下executable加载后stack、heap、libraries的随机化功能,因而有多种绕过方式,这一时期的ASLR的定义也就成了“对stack、heap、libraries的随机化”。后来设计者就考虑executable的加载基址随机化以使得bypass失效,然而这一点在技术细节上需要编译器来实现,因此gcc开始支持PIE选项,使得编译出来的executable像是一个特殊的so,可以被操作系统加载到随机化的内存地址(只是可以),因而到这一阶段你会发现ASLR的定义变成了“对stack、heap、libraries、executable base的随机化”。然而你要将PIE出来的executable理解为特殊的so的话,原来的定义还是可以自圆其说的:)。

所以这是ASLR 的三个级别变成了 :0, 不开启任何随机化;1, 开启stack、libraries [、executable base(special libraries -^-) if PIE is enabled while compiling] 的随机化;2,开启heap随机化。

因而,我们会发现PIE编译出来的executable如果ASLR=0的话,基址也是不会变的(有能力但没使用),如果ASLR=1的话,即使按照ASLR定义这个级别似乎不会对heap基址随机化,但是由于executable的基址已经随机化了,所以heap的基址自然也就被随机化了:)。我们不妨多做几个实验验证一下:

 

 

ELF装载入内存后的进程布局参考:

http://blog.sina.com.cn/s/blog_4ed962ae01013vhr.html

gcc PIE选项使用:

http://blog.csdn.net/ivan240/article/details/5363395

posted @ 2017-10-10 17:50 rec0rd 阅读(...) 评论(...) 编辑 收藏