不同选项下的虚拟内存分布
Linux系统下,ELF格式的可执行文件的各个段都会被分配到不同的虚拟内存空间中。在操作系统实现地址随机化机制(Address Space Layout Randomization)之前,程序在任意一次执行下,所使用的虚拟空间的地址往往是相同的。这就给恶意攻击者的攻击行为提供了很大的便利(见Stack Smashing for Fun and Profit)。以下述程序为例。
#include
int local_global_var=0x10;
int local_global_func(void)
{
return 0x40;
}
int main(void)
{
int x = local_global_func();
local_global_var= 0x20;
while(getchar()!= 'q')
continue;
return0;
}
图1示例程序exam1.c
程序包括一个全局函数,一个全局变量和一个局部变量。程序中的while语句等待用户的输入,当输入字符为'q'时程序终止,这是为了方便在程序运行时观察不同的段所对应的不同内存。通过使用不同的编译选项,来观察它们对程序的虚拟内存的影响。选项分为3种情况:1)不使用任何特殊选项;2)使用-fpic选项。该选项通常被用来生成与位置无关动态库文件;3)-fpie选项,用于生成位置无关的代码段。
不使用任何特殊选项
不使用任何选项的情况下,在程序运行过程中,通过Linux命令cat /proc/pidnum/maps来输出程序的虚拟内存分布状况。其中pidnum是程序的进程号,通过ps命令获得。程序的两次不同执行下,其虚拟内存的分布如图2所示。
图中显示,程序exam1的代码段和数据段分别对应的虚拟地址为00400000-00401000和00600000-00601000,而且在两次不同的执行中,地址不变。堆栈段(stack)在两次执行中则有了变化,第一次为7fff783b9000-7fff783da000,第二次为7fff7f27a000-7fff7f29b000。这是因为操作系统的地址随机化机制,把堆栈段的地址随机化了,每次不同的执行都对应不同的堆栈段。
图2不使用任何选项所生成程序的两次不同执行
使用选项-fpic
使用-fpic所编译生成的程序不是一个可执行程序,因此不能观察其执行时的虚拟内存分布状况,将在下一节讨论该选项的作用。
使用选项-fpie
使用选项-fpie(Position Independent Executable)生成的文件是可执行文件,其两次执行效果如图3所示。从图中我们可以看到,第一次执行时,程序的代码段和数据段所对应的虚拟地址分别为7fb75116a000-7fb75116b000和7fb75136a000-7fb75136b000。第二次执行时,对应的虚拟地址分别为7f02313ac000-7f02313ad000和7f02315ac000-7f02315ad000。与不加-fpie选项的情况不同,这两次执行,elf文件中的代码段被映射到了不同的虚拟内存地址上,这是因为编译器在该选项下可以生成位置无关的代码,从而在执行时,可以被装载到不同的地址空间。当代码、数据、stack和heap在每次执行时,都被随机化分配到不同的空间的时候,攻击者的难度无疑大大增加了。
图3使用-fpie选项所生成程序的两次不同执行
不同选项的重定位信息
使用选项gcc -c exam1.c,生成exam1.o,然后使用命令objdump -d -r exam1.o输出其反汇编的代码,并附带有重定位信息。输出代码如下。
000000000000000b :
b: 55 push %rbp
c: 48 89 e5 mov %rsp,%rbp
f: 48 83 ec 10 sub $0x10,%rsp
13: e8 00 00 00 00 callq 18
14: R_X86_64_PC32 local_global_func-0x4
18: 89 45 fc mov %eax,-0x4(%rbp)
1b: c7 05 00 00 00 00 20 movl $0x20,0x0(%rip) # 25
22: 00 00 00
1d: R_X86_64_PC32 local_global_var-0x8
使用选项gcc -c -fpic exam1.c,然后对生成的二进制文件反汇编,输出代码如下。
000000000000000b :
b: 55 push %rbp
c: 48 89 e5 mov %rsp,%rbp
f: 48 83 ec 10 sub $0x10,%rsp
13: e8 00 00 00 00 callq 18
14: R_X86_64_PLT32 local_global_func-0x4
18: 89 45 fc mov %eax,-0x4(%rbp)
1b: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 22
1e: R_X86_64_GOTPCREL local_global_var-0x4
22: c7 00 20 00 00 00 movl $0x20,(%rax)
使用选项gcc -c -fpie exam1.c,然后对生成的二进制文件反汇编,输出代码如下。
000000000000000b :
b: 55 push %rbp
c: 48 89 e5 mov %rsp,%rbp
f: 48 83 ec 10 sub $0x10,%rsp
13: e8 00 00 00 00 callq 18
14: R_X86_64_PC32 local_global_func-0x4
18: 89 45 fc mov %eax,-0x4(%rbp)
1b: c7 05 00 00 00 00 20 movl $0x20,0x0(%rip) # 25
22: 00 00 00
1d: R_X86_64_PC32 local_global_var-0x8
比较3个变量的寻址方式和指令实现,结果如下表。
图中红色的0x0表示需要在链接时填入的偏移值。从图中可知,全局函数local_global_func在无特殊选项和-fpie情况下都是R_X86_64_PC32重定位类型的,在-fpic选项下,是R_X86_64_PLT32重定位类型的。而全局变量local_global_var与函数类似,也是在无特殊选项和-fpic选项下相同,都为R_X86_64_PC32重定位类型,只使用一条指令存储其数值(0x20)。在-fpie选项下为R_X86_64_GOTPCREL重定位类型,而且使用了两条指令,一条计算地址,另一条保存数值。
不同选项下生成可执行文件的比较
无特殊选项与-fpie选项对比
使用objdump-x输出文件头,分别如下所示。观察可知,主要差别在于两个地方:
1.文件标志(Fileflags)
分别为EXEC_P,HAS_SYMS, D_PAGED和HAS_SYMS,DYNAMIC, D_PAGED
2. 无特殊选项时,虚拟装载地址(LOAD ADDR)为400000;而在-fpie选项下,其虚拟装载地址为0x0。
-fpic与-fpie
这两个选项所产生的主要异同点如下。
1. 两者的文件标志都是DYNAMIC。
2. 装载地址都是0x0。
3. -fpic所产生的共享库文件没有PHDR和INTERP段。
无特殊选项
exam1_no: file format elf64-x86-64
exam1_no
architecture: i386:x86-64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x00000000004003c0
Program Header:
PHDR off 0x0000000000000040 vaddr 0x0000000000400040 paddr 0x0000000000400040 align 2**3
filesz 0x00000000000001c0 memsz 0x00000000000001c0 flags r-x
INTERP off 0x0000000000000200 vaddr 0x0000000000400200 paddr 0x0000000000400200 align 2**0
filesz 0x000000000000001c memsz 0x000000000000001c flags r--
LOAD off 0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21
filesz 0x00000000000006fc memsz 0x00000000000006fc flags r-x
LOAD off 0x0000000000000700 vaddr 0x0000000000600700 paddr 0x0000000000600700 align 2**21
filesz 0x000000000000022c memsz 0x0000000000000230 flags rw-
DYNAMIC off 0x0000000000000718 vaddr 0x0000000000600718 paddr 0x0000000000600718 align 2**3
filesz 0x00000000000001d0 memsz 0x00000000000001d0 flags rw-
NOTE off 0x000000000000021c vaddr 0x000000000040021c paddr 0x000000000040021c align 2**2
filesz 0x0000000000000020 memsz 0x0000000000000020 flags r--
EH_FRAME off 0x0000000000000604 vaddr 0x0000000000400604 paddr 0x0000000000400604 align 2**2
filesz 0x0000000000000034 memsz 0x0000000000000034 flags r--
STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**3
filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
-fpie
exam1_pie: file format elf64-x86-64
exam1_pie
architecture: i386:x86-64, flags 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
start address 0x00000000000006b0
Program Header:
PHDR off 0x0000000000000040 vaddr 0x0000000000000040 paddr 0x0000000000000040 align 2**3
filesz 0x00000000000001c0 memsz 0x00000000000001c0 flags r-x
INTERP off 0x0000000000000200 vaddr 0x0000000000000200 paddr 0x0000000000000200 align 2**0
filesz 0x000000000000001c memsz 0x000000000000001c flags r--
LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**21
filesz 0x0000000000000a2c memsz 0x0000000000000a2c flags r-x
LOAD off 0x0000000000000a30 vaddr 0x0000000000200a30 paddr 0x0000000000200a30 align 2**21
filesz 0x000000000000026c memsz 0x0000000000000270 flags rw-
DYNAMIC off 0x0000000000000a48 vaddr 0x0000000000200a48 paddr 0x0000000000200a48 align 2**3
filesz 0x00000000000001d0 memsz 0x00000000000001d0 flags rw-
NOTE off 0x000000000000021c vaddr 0x000000000000021c paddr 0x000000000000021c align 2**2
filesz 0x0000000000000020 memsz 0x0000000000000020 flags r--
EH_FRAME off 0x0000000000000934 vaddr 0x0000000000000934 paddr 0x0000000000000934 align 2**2
filesz 0x0000000000000034 memsz 0x0000000000000034 flags r--
STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**3
filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-
-fpic
exam1_pic: file format elf64-x86-64
exam1_pic
architecture: i386:x86-64, flags 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
start address 0x00000000000005c0
Program Header:
LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**21
filesz 0x0000000000000824 memsz 0x0000000000000824 flags r-x
LOAD off 0x0000000000000828 vaddr 0x0000000000200828 paddr 0x0000000000200828 align 2**21
filesz 0x0000000000000244 memsz 0x0000000000000248 flags rw-
DYNAMIC off 0x0000000000000840 vaddr 0x0000000000200840 paddr 0x0000000000200840 align 2**3
filesz 0x00000000000001c0 memsz 0x00000000000001c0 flags rw-
EH_FRAME off 0x0000000000000778 vaddr 0x0000000000000778 paddr 0x0000000000000778 align 2**2
filesz 0x0000000000000024 memsz 0x0000000000000024 flags r--
STACK off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**3
filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-