GCC -fpie选项生成文件分析

  • Post author:
  • Post category:其他



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










Linux


系统下,


ELF


格式的可执行文件的各个段都会被分配到不同的虚拟内存空间中。在操作系统实现地址随机化机制(


Address Space Layout Randomization


)之前,程序在任意一次执行下,所使用的虚拟空间的地址往往是相同的。这就给恶意攻击者的攻击行为提供了很大的便利(见


Stack Smashing for Fun and Profit


)。以下述程序为例。








#include <stdio.h>
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(P


osition I


ndependent 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 <main>:

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 <main+0xd>

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 <main+0x1a>

22:   00 00 00

1d: R_X86_64_PC32       local_global_var-0x8

使用选项gcc -c -fpic exam1.c,然后对生成的二进制文件反汇编,输出代码如下。


000000000000000b <main>:

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 <main+0xd>

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 <main+0x17>

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 <main>:

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 <main+0xd>

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 <main+0x1a>

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-









版权声明:本文为u012033027原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。