ELF文件类型 ELF程序头 ELF节头 ELF符号

版权声明:欢迎转载,注明出处 https://blog.csdn.net/youyou519/article/details/82659007

ELF文件类型

首先ELF文件可以被标记为以下几个类型:

  • ET_NONE:未知类型。
  • ET_REL:重定位文件,类型标记为relocatable意为着该文件被标记为了一段可重定位的代码段,有时也称为目标文件。可重定位目标文件通常是还未被链接到可执行程序的一段位置独立的代码。编译完是.o的格式。
  • ET_EXEC:可执行文件,类型为executable,表明这个文件被标记为可执行文件。这种类型被称为程序,是一个进程开始的入口。
  • ET_DYN:共享目标文件。类型为dynamic,以为着该文件被标记为了一个动态的可链接的目标文件,也称为共享库。这类共享库会在程序运行时被装载链接到程序的进程镜像中。
  • ET_CORE:核心文件。载程序崩溃或者进程传递了一个SIGSEGV信号(分段违规)时,会在核心文件中记录整个进程的镜像信息。可以使用GDB读取这类文件来辅助调试并查找程序崩溃的原因。

用到的一些命令

readelf -h 查看ELF文件,可以看到原始的ELF文件头。如下所示
readelf -h
ELF文件头从文件0偏移量开始,除了文件头之后剩余部分的一个映射。记录了ELF类型,结构,和程序开始执行的入口地址并提供其他ELF(节头和程序头偏移量)。

程序头

ELF程序头是对二进制文件中段的描述,是程序装载必需的部分。段载内核装载时被解析。描述了磁盘上可执行文件的内存布局,以及如何映射到内存中,可以通过引用原始ELF头中名为e_phoff(程序头表偏移量)的偏移量来得到程序头表。

常见有5种程序头类型:

PT_LOAD,一个可执行文件只是一个PT_LOAD类型的段。这类程序头描述的时可装载的段。一般一个需要动态链接的ELF可执行文件通常由两个可装载的段:存放程序代码的text段,存放全局变量和动态链接的data段。
上面两个段会被映射到内存,根据p_align中存放的值在内存中对齐。程序头主要描述程序执行时在内存中的布局。
PT_DYNAMIC,动态段时动态链接可执行文件持有的,包含动态链接器所必有的一些信息,包含了一些标记值和指针,比如运行是需要链接的共享库列表,全局偏移表,重定位条目的相关信息。
PT_NOTE,保存了与特定供应商或者系统相关的附加信息。
PT_INTERP,将信息二号位置存放在一个NULL为终止符的字符串中,对程序解释器位置的描述。
PT_PHDR,段保存了程序头标本身的位置和大小。Phdr表保存了所有的Phdr对文件(以及内存镜像)中段的描述信息。

用到的一些命令

readelf -l <文件名>命令查看文件的Phdr表:

readelf -l

ELF节头

段是程序执行的必要组成部分,每个段中,会有代码或者数据被划分为不同的节,节头表是对这些节位置和大小的描述。用于链接和调试。没有节头表程序仍可以政策执行。因为节头没有对程序的内存布局进行描述,这是程序头表的任务。用readelf可以看到节和段的关系。

  • .text节是保存了程序代码指令的代码节。一段可执行程序,存在Phdr,.text就会存在于text段中。由于.text节保存了程序代码,因此节的类型为SHT_PROGBITS。
  • .rodata 保存只读数据。类型SHT_PROGBITS。
  • .plt 过程链接表(Procedure Linkage Table),包含动态链接器调用从共享库导入的函数所必须的相关代码。存在于text段中,类型SHT_PROGBITS。
  • .bss节保存未初始化全局数据,是data的一部分。程序加载时数据被初始化成0,在程序执行期间可以赋值,未保存实际数据,类型SHT_NOBITS。
  • .got节保存全局偏移表。它和.plt节一起提供了对导入的共享库函数访问的入口。由动态链接器在运行时进行修改。如果攻击者获得堆或者.bss漏洞的一个指针大小写原语,就可以对该节任意修改。类型SHT_PROGBITS。
  • .dynsym节保存共享库导入的动态符号信息,该节在text段中,类型SHT_DYNSYM。
  • .dynstr保存动态符号字符串表,存放一系列字符串,代表了符号的名称,以空字符作为终止符。
  • .rel节保存重定位信息,类型SHT_REL。
  • .hash节,也称为.gnu.hash,保存一个查找符号散列表。
  • .symtab节,保存了ElfN_Sym类型的符号信息,类型SHT_SYMTAB。
  • strtab节,保存符号字符串表,表中内容被.symtab的ElfN_Sym结构中的st_name条目引用。类型SHT_SYMTAB。
  • .shstrtab节,保存节头字符串表,以空字符终止的字符串集合,保存了每个节节名,如.text,.data等。有个e_shsrndx的ELF文件头条目会指向.shstrtab节,e_shstrndx中保存了.shstrtab的偏移量。这节的类型是SHT_SYMTAB。
  • .ctors和.dtors节,前者构造器,后者析构器,指向构造函数和析构函数的函数指针,构造函数是在main函数执行前需要执行的代码,析构是main函数之后需要执行的代码。
    可以使用 readelf -S 查看ET_REL文件的节头。
root@ubuntu:~/Desktop# readelf -S UDP_25000
There are 28 section headers, starting at offset 0xebb64:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .note.ABI-tag     NOTE            080480d4 0000d4 000020 00   A  0   0  4
  [ 2] .init             PROGBITS        080480f4 0000f4 000017 00  AX  0   0  4
  [ 3] .text             PROGBITS        08048120 000120 0b5cc0 00  AX  0   0 32
  [ 4] __libc_thread_fre PROGBITS        080fdde0 0b5de0 0000e2 00  AX  0   0  4
  [ 5] __libc_freeres_fn PROGBITS        080fdec4 0b5ec4 000f6e 00  AX  0   0  4
  [ 6] .fini             PROGBITS        080fee34 0b6e34 00001a 00  AX  0   0  4
  [ 7] .rodata           PROGBITS        080fee60 0b6e60 01d89a 00   A  0   0 32
  [ 8] __libc_atexit     PROGBITS        0811c6fc 0d46fc 000004 00   A  0   0  4
  [ 9] __libc_subfreeres PROGBITS        0811c700 0d4700 00003c 00   A  0   0  4
  [10] __libc_thread_sub PROGBITS        0811c73c 0d473c 000004 00   A  0   0  4
  [11] .eh_frame         PROGBITS        0811c740 0d4740 00fc30 00   A  0   0  4
  [12] .gcc_except_table PROGBITS        0812c370 0e4370 00439f 00   A  0   0  4
  [13] .tdata            PROGBITS        08131000 0e9000 000014 00 WAT  0   0  4
  [14] .tbss             NOBITS          08131014 0e9014 000018 00 WAT  0   0  4
  [15] .ctors            PROGBITS        08131014 0e9014 000028 00  WA  0   0  4
  [16] .dtors            PROGBITS        0813103c 0e903c 00000c 00  WA  0   0  4
  [17] .jcr              PROGBITS        08131048 0e9048 000004 00  WA  0   0  4
  [18] .data.rel.ro      PROGBITS        08131060 0e9060 00063c 00  WA  0   0 32
  [19] .got              PROGBITS        0813169c 0e969c 00005c 04  WA  0   0  4
  [20] .got.plt          PROGBITS        081316f8 0e96f8 00000c 04  WA  0   0  4
  [21] .data             PROGBITS        08131720 0e9720 0014b4 00  WA  0   0 32
  [22] .bss              NOBITS          08132be0 0eabd4 008314 00  WA  0   0 32
  [23] __libc_freeres_pt NOBITS          0813aef4 0eabd4 000018 00  WA  0   0  4
  [24] .comment          PROGBITS        00000000 0eabd4 000e6a 00      0   0  1
  [25] .shstrtab         STRTAB          00000000 0eba3e 000126 00      0   0  1
  [26] .symtab           SYMTAB          00000000 0ebfc4 0156b0 10     27 1276  4
  [27] .strtab           STRTAB          00000000 101674 02935f 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

因为可重定位文件不会存在程序头,.o类型文件会被链接到可执行文件中,但是不会
直接加载到内存,所以readelf -l xxx.o 不会得到想要的结果。不过Linux中可加载内核模块(LKM)是个例外,LKM是ET_REL类型的文件,它会被直接加载进内核的内存中,并自动进行重定位。

ELF符号

符号是对某些类型的数据或者代码的符号引用。在大多数共享库和动态链接可执行文件中,存在两个符号表。.dynsym和.symtab。
.dynsym保存了引用来自外部文件符号的全局符号,是.symtab的子集,.symtab保存了所有符号。.dynsym只保存了动态/全局符号。

用两个符号表的原因

使用readelf -S 看到了一部分节被标记了A(ALLOC),WA(WRITE,ALLOC)或者AX(ALLOC/EXEC)。.dynsym被标记了ALLOC的,.symtab则没有标记。ALLOC表示有该标记的节会在运行时分配并装载进入内存,而.symtab不是运行是必须的,不会被装载到内存中。.dynsym保存的符号只能在运行时被解析,所以时运行时动态连接器所需要的唯一符号。.dynsym符号表对于动态链接可执行文件的执行来说必需的,而.symtab符号表只是用来调试和链接的。

参考文献

[1] 《Linux二进制分析》Ryan O’Neill

猜你喜欢

转载自blog.csdn.net/youyou519/article/details/82659007