数据结构论坛

注册

 

发新话题 回复该主题

ELF文件格式解析 [复制链接]

1#
专业治疗白癜风医院 http://m.39.net/pf/bdfyy/bdfzj/

ELF文件格式的相关知识是Linux下进行pwn以及reverse的基础,是二进制可执行文件的一种形式,下面我们通过一个ELF文件的生成,并结合其ELF文件结构分析一下一个二进制文件在系统中执行时与权限相关的一些ELF结构知识点。文章内容较为浅显,大佬可略过,文章有不足之处,也恳请批评指正。

ELF文件的生成

一个main.c的文件,在linux系统上,经过gcc编译后可以生成一个可以执行的文件,以hello.c为例

#includestdio.h

intmain(){

printf(HelloWorld\n);

return0;

}

一个ELF的生成,最开始是在系统中编写的一个源文件,依次会经过预处理,编译,汇编,链接过程后,会生成一个ELF格式的可以执行的文件。我们往往通过gcchello.c-ohello即可生成一个文件名为hello的可执行文件,该程序会输出HelloWorld。

在如上中,我们通过gcc一条命令将hello.c编译成能够执行的二进制文件,但在这一条命令中,同时包含了预处理,编译,汇编,链接的过程。下面分别来看这几个过程

预处理

主要是处理头文件,预编译主要是处理那些源代码中的以#开始的预编译指令,会删除注释

gcc-Ehello.c-ohello.i

编译

编译,经过编译后,可以生成.s的汇编文件

gcc-Shello.i-ohello.s

汇编汇编,经过汇编后,生成机器指令

ashello.s-ohello.o

or

gcc-chello.s-ohello.o

链接

经过汇编后生成的是目标文件,编译器编译源代码后生成的文件叫目标文件,在目标文件中,其本身是按照ELF文件的格式存储的,但是其中的一些符号以及地址还没有被调整,再经过链接后,其生成的即是hello的二进制的可执行文件。

ld-s-ohellohello.o

ELF文件内容解析

一个标准的ELF文件,是由文件头(ELFHeader),程序头(SegmentHeader),节头(SectionHeader),符号表(SymbolTable),动态符号表(DynamicSymbolTable)等组成,我们在editor中,通过ELFTemplate来看一下hello的文件格式,如下图

ELFHeader

在ELFHeader中的file_identification指明该文件类型为ELF的二进制文件

在ELFFileHeader中通(e_phoffe_shoff两个变量,我们可以找到SegmentHeader和SectionHeader的位置,从而找到程序头和节表头的位置,在这两个表中,有对各自段以及节的详细的介绍。

在ELFHeader中的e_entry_START_ADDRESS中,保存着程序执行的入口,地址0x,程序的start的入口地址

在程序实际中,我们可以看到start的位置也在0x的位置处。

ELF程序头

ELF程序头,是程序装载不可缺少的一部分,可以分为以下几个段

PT_LOAD,即可以装载到系统中的段PT_DYNAMIC,动态段时动态链接的文件所必须的,包含着动态链接所需要的信息。PT_NOTE,保存一些系统相关的附加信息PT_INTERP,段只将位置和大小信息存放在一个以null为终止符的字符串中,是对程序解释器位置的描述PT_PHDR段保存了程序头表本身的位置和大小

其中在段的p_flags中,确定了该段的权限,在后文的节表头中,对相应的表有进一步的权限的确定。

memsz的值对应的时,hex()=0x6fc,所以该段的权限是读和可执行的权限,在0x到0xfc只有只读权限,这是由于节表中权限的设置导致的。

在ELF节表中的权限

ELF节表头

ELF中,包含很多的节表,我们可以通过readelf-Shello来查看

我们可以看到各种节表信息,常用以下几项

.text该节保存了程序代码.bss该节主要保存.data保存了初始化的全局变量.plt包含了动态链接器调用从共享库导入的函数必需的相关的代码.got.plt保存了全局的偏移表等表,got表位于该节在ELF的描述节表的相关的数据结构中,s_flags的标志用于描述该节的权限,不同的数值,分别对应了不同的权限,具体如下图

攻击相关got表劫持原理

在CTF的pwn相关题目中,我们可以通过栈溢出,以及堆溢出来实现shell权限的获取,获取权限往往可以通过劫持got表来实现,而这个实现的前提需要got表(.got.plt)权限为可写权限。首先看一下什么是plt表和got表

plt表,是过程链接表,程序动态调用的符号在可执行文件中是位置无关的符号,当程序调用时,会通过plt表将符号转移到绝对地址。

got表中保存了ELF文件在共享库中的绝对地址,在程序一开始运行的时候,got表是空的,当符号第一次调用的时候,会动态解析符号的绝对地址并填充到got表中,在第二次调用同一符号的时候,直接通过got表跳转。

这里对程序第一次解析符号的过程不加以详细描述,仅描述第二次调用时的跳转过程,改造源程序hello.c,将断点停在程序执行到第二个printf(Hello)时

#includestdio.h

intmain(){

printf(helloworld);

printf(Hello);

return0;

}

这里是call0x

我们来看一下0x处的汇编代码,这里是跳转,jmpQWORTPTR[rip+0xc12]

可以看到是跳转到0x618处,在下图中我们可以看到在0x618处的值为0xffff7a

下面我们通过单步调试s跟进程序,可以看到rip指向了0xffff7a

通过readelf-Shello可以查看到其got表的起始位置0x600

在实际的攻击的过程中,当我们将此处的got表的值更改为我们需要的system函数的时候,再配合传入实际的参数/bin/sh即可实现shell权限的获取。

防御相关

可以通过复写got表,实现got表劫持,当我们设置got表不可写的时候,该攻击也就失效了,RELRO技术可以实现该保护。在程序编译的时候,我们可以通过如下命令,实现got表不可写,需要注意的是,RELRO保护可分为PartialRELRO和FullRELRO,当编译的时候,我们指定PartialRELRO的时候,其got表仍旧可写

#PartialRELRO,gcc默认也是PartialRELRO

gcc-zlazy-otesttest.c

#FullRELRO

gcchello.c-o-znow

合天网安实验室相关实验推荐:ELF

分享 转发
TOP
发新话题 回复该主题