GCC -fpie选项生成文件分析

不同选项下的虚拟内存分布

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-0040100000600000-00601000,而且在两次不同的执行中,地址不变。堆栈段(stack)在两次执行中则有了变化,第一次为7fff783b9000-7fff783da000,第二次为7fff7f27a000-7fff7f29b000。这是因为操作系统的地址随机化机制,把堆栈段的地址随机化了,每次不同的执行都对应不同的堆栈段。

GCC -fpie选项生成文件分析_第1张图片

2不使用任何选项所生成程序的两次不同执行

使用选项-fpic

使用-fpic所编译生成的程序不是一个可执行程序,因此不能观察其执行时的虚拟内存分布状况,将在下一节讨论该选项的作用。

使用选项-fpie

使用选项-fpie(Position Independent Executable)生成的文件是可执行文件,其两次执行效果如图3所示。从图中我们可以看到,第一次执行时,程序的代码段和数据段所对应的虚拟地址分别为7fb75116a000-7fb75116b0007fb75136a000-7fb75136b000。第二次执行时,对应的虚拟地址分别为7f02313ac000-7f02313ad0007f02315ac000-7f02315ad000。与不加-fpie选项的情况不同,这两次执行,elf文件中的代码段被映射到了不同的虚拟内存地址上,这是因为编译器在该选项下可以生成位置无关的代码,从而在执行时,可以被装载到不同的地址空间。当代码、数据、stackheap在每次执行时,都被随机化分配到不同的空间的时候,攻击者的难度无疑大大增加了。

GCC -fpie选项生成文件分析_第2张图片

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个变量的寻址方式和指令实现,结果如下表。

GCC -fpie选项生成文件分析_第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_PAGEDHAS_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-






你可能感兴趣的:(心得)