pwn学习

ASLR和PIE

ASLR

在Linux上,ASLR的全局配置/proc/sys/kernel/randominze_va_space有三种情况:0表示关闭ASLR;1表示部分开启(将mmap的基址,stack和vdso页面随机化);2表示完全开启(在1部分开启的基础上增加heap的随机化)。

PIE

背景:ASLR用于操作系统层面,不能应用于二进制文件。

PIE在应用层的编译器上实现,通过将程序编译为位置无关代码,使程序可以被加载到任意位置。在PIE和ASLR同时开启的情况下,攻击者对程序的内存布局一无所知,大大增加了利用难度。

绕过思路:

无论是ASLR还是PIE,被随机化的都只是某个对象的起始地址,而在该对象的内部依然保持原来的结构,也就是说相对偏移是不会变的。所以绕过时首先要计算出相对偏移,然后通过泄露实时的地址结合相对偏移得到想要的其他地址。

周末填坑:http://www.360doc.com/content/13/0817/08/7377734_307736719.shtml

动态链接

定义:程序在运行或加载时使用用于动态链接的共享库,而不是每个程序对应一个对应的系统库。

动态链接

如图,当运行func1.ELF时,系统将func1.o和依赖的testLib.o装载入内存,然后进行动态链接。完成后系统将控制权交给程序入口点,程序开始执行。接下来,当func2.ELF想要执行时,由于内存中已经有testLib.o,因此不再重复加载,直接进行链接即可。

位置无关代码

可以加载而无需重定位的代码称为位置无关代码,它是共享库必须具有的属性。

由于一个程序(或者共享库)的数据段和代码段的相对距离总是保持不变的,因此指令和变量之间的距离是一个运行时常量,与绝对内存地址无关。也就是说,指令与变量的虚拟地址并不是固定的。于是就有了全局偏移量表(GOT),它位于数据段的开头,用于保存全局变量和库函数的引用,每个条目占8个字节,在加载时会进行重定向并填入符号的绝对地址。

延迟绑定机制

PLT表和GOT表

延迟绑定的基本思想时当函数第一次被调用时,动态链接器才进行符号查找、重定位等操作。延迟绑定需要用到PLT表个GOT表,每个被调用的库函数都有一组对应的PLT和GOT。

位于代码段.plt节的PLT是一个数组。其中PLT[0]用于跳转到动态链接器,PLT[1]用于调用系统启动函数__libc_start_main(),从PLT[2]开始就是被调用的各个函数条目。

位于数据段.got.plt节的GOT也是一个数组。其中GOT[0]和GOT[1]包含动态链接器在解析函数地址时所需要的两个地址(.dynamic和relor条目),GOT[2]时动态链接器ld-linux.so的入口点,从GOT[3]开始就是被调用的各个函数条目,这些条目默认指向对应PLT条目的第二条指令,完成绑定后才会被修改为函数的实际地址。

函数具体调用步骤:

初次调用库函数时,执行call指令会进入plt表,其后plt表找到对应的GOT条目,这时GOT条目为执行函数的下一条指令(如push),然后进入PLT[0],也就是跳转到动态链接器。之后动态链接器结合GOT[1]、GOT[2]完成符号解析和重定位工作,并将函数的真实地址填入got.plt,最后将控制权交给函数。延迟绑定完成后,如果再次调用函数,就可以由plt表直接跳转到got.plt,将控制权交给函数自身。

所以之后函数地址的获取便是先找到plt表对应的位置,在从plt表跳转到GOT表,GOT表中存放的是函数的真实地址。(plt与GOT一一对应)。

RELRO

前提:在延迟绑定的情况下,.got.plt必须是可写的,这就给了攻击者篡改地址劫持程序执行的可能。

RELRO机制:为了解决延迟绑定的安全问题,也就是避免GOT上的地址被篡改。为了引入RELRO机制,GOT被拆分为.got节和.got.plt节两个部分,.got节为开启Full RELRO的情况。

Partial RELRO:

一些段(包括.dynamic、.got等)在初始化后将会被标记为只读。

Full RELRO:

延迟绑定将被禁止,所有的导入符号将在开始时被解析,.got.plt段会被完全初始化为目标函数的最终地址,并且mprotect标记为只读。