ELF文件格式分析(一)

目录

  • 一、ELF 格式介绍
  • 二、ELF组成部分
    • 2.1) ELF Header
    • 2.2) Program Headers
    • 2.3)Section Headers Table
    • 2.3.2) Section

此篇文章介绍了ELF文件由哪些部分组成,他们的功能是什么,并在文章末尾给出了一份ELF文件解析的参考资料。

一、ELF 格式介绍

ELF(Executable and Linkable Format)即可执行的和可链接的格式。
出于方便性和效率考虑,ELF文件提供了两种并行视图,即链接视图,执行视图。
ELF文件格式分析(一)_第1张图片

二、ELF组成部分

下面我们使用 readelf 来读取ls的文件信息

2.1) ELF Header

以Magic Str开头,描述文件类型,abi, version等信息,并提供到Program Header Table*和 Section Header Table的索引。

[root@localhost tgt]# readelf -h  /bin/ls
ELF 头:
  Magic:  7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              DYN (共享目标文件)
  系统架构:                          AArch64
  版本:                              0x1
  入口点地址:              0x5914
  程序头起点:              64 (bytes into file)
  Start of section headers:          197648 (bytes into file)
  标志:             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         28
  Section header string table index: 27

2.2) Program Headers

存在于可执行文件或共享库中,是一个结构体组成的数组。一般在ELF Header之后。每个数组元素描述一个段(segment)或者其他程序执行时需要的信息;该结构用于执行阶段。

[root@localhost tgt]# readelf -l  /bin/ls

Elf 文件类型为 DYN (共享目标文件)
Entry point 0x5914
There are 9 program headers, starting at offset 64

程序头:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000001f8 0x00000000000001f8  R      0x8
  INTERP         0x0000000000000238 0x0000000000000238 0x0000000000000238
                 0x000000000000001b 0x000000000000001b  R      0x1
      [Requesting program interpreter: /lib/ld-linux-aarch64.so.1]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000020780 0x0000000000020780  R E    0x10000
  LOAD           0x000000000002ef30 0x000000000003ef30 0x000000000003ef30
                 0x0000000000001380 0x0000000000002648  RW     0x10000
  DYNAMIC        0x000000000002f948 0x000000000003f948 0x000000000003f948
                 0x0000000000000220 0x0000000000000220  RW     0x8
  NOTE           0x0000000000000254 0x0000000000000254 0x0000000000000254
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_EH_FRAME   0x000000000001c914 0x000000000001c914 0x000000000001c914
                 0x000000000000093c 0x000000000000093c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x000000000002ef30 0x000000000003ef30 0x000000000003ef30
                 0x00000000000010d0 0x00000000000010d0  R      0x1

 Section to Segment mapping:
  段节...
   00
   01     .interp
   02     .interp .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
   03     .init_array .fini_array .data.rel.ro .dynamic .got .data .bss
   04     .dynamic
   05     .note.gnu.build-id .note.ABI-tag
   06     .eh_frame_hdr
   07
   08     .init_array .fini_array .data.rel.ro .dynamic .got

2.3)Section Headers Table

索引每个section的位置,该结构用于链接阶段。

[root@localhost tgt]# readelf -S  /bin/ls
There are 28 section headers, starting at offset 0x30410:

节头:
  [] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000238  00000238
       000000000000001b  0000000000000000   A       0     0     1
  [ 2] .note.gnu.build-i NOTE             0000000000000254  00000254
       0000000000000024  0000000000000000   A       0     0     4
  [ 3] .note.ABI-tag     NOTE             0000000000000278  00000278
       0000000000000020  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000000298  00000298
       0000000000000040  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000000002d8  000002d8
       0000000000000c90  0000000000000018   A       6     3     8
  [ 6] .dynstr           STRTAB           0000000000000f68  00000f68
       00000000000005e4  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           000000000000154c  0000154c
       000000000000010c  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000001658  00001658
       0000000000000070  0000000000000000   A       6     3     8
  [ 9] .rela.dyn         RELA             00000000000016c8  000016c8
       00000000000016f8  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000002dc0  00002dc0
       0000000000000a98  0000000000000018  AI       5    22     8
  [11] .init             PROGBITS         0000000000003858  00003858
       0000000000000014  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000003870  00003870
       0000000000000730  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         0000000000003fa0  00003fa0
       0000000000013fa8  0000000000000000  AX       0     0     8
  [14] .fini             PROGBITS         0000000000017f48  00017f48
       0000000000000010  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         0000000000017f60  00017f60
       00000000000049b4  0000000000000000   A       0     0     16
  [16] .eh_frame_hdr     PROGBITS         000000000001c914  0001c914
       000000000000093c  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         000000000001d250  0001d250
       0000000000003530  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       000000000003ef30  0002ef30
       0000000000000008  0000000000000008  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       000000000003ef38  0002ef38
       0000000000000008  0000000000000008  WA       0     0     8
  [20] .data.rel.ro      PROGBITS         000000000003ef40  0002ef40
       0000000000000a08  0000000000000000  WA       0     0     8
  [21] .dynamic          DYNAMIC          000000000003f948  0002f948
       0000000000000220  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         000000000003fb68  0002fb68
       0000000000000498  0000000000000008  WA       0     0     8
  [23] .data             PROGBITS         0000000000040000  00030000
       00000000000002b0  0000000000000000  WA       0     0     8
  [24] .bss              NOBITS           00000000000402b0  000302b0
       00000000000012c8  0000000000000000  WA       0     0     8
  [25] .gnu.build.attrib NOTE             0000000000041578  000302b0
       0000000000000024  0000000000000000           0     0     4
  [26] .gnu_debuglink    PROGBITS         0000000000000000  000302d4
       0000000000000028  0000000000000000           0     0     4
  [27] .shstrtab         STRTAB           0000000000000000  000302fc
       0000000000000113  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

2.3.2) Section

节区名 介绍
[.text] .text 节是保存了程序代码指令的代码节。一段可执行程序,如果存在Phdr,.text 节就会存在于text 段中。由于.text 节保存了程序代码,因此节的类型为SHT_PROGBITS。
[.dynsym] 保存了引用来自外部文件符号的全局符号,如printf 这样的库函数。.dynsym 是被标记了ALLOC 的,在运行时分配并装载进入内存,因此动态链接可执行文件的执行来说是必需的。该节保存在text 段中,节类型被标记为SHT_DYNSYM。
[.rodata] .rodata 节保存了只读的数据,如一行C 语言代码中的字符串。下面这条命令就是存放在.rodata 节中的:
printf(“Hello World!\n”);因为.rodata 节是只读的,所以只能存在于一个可执行文件的只读段中。因此,只能在text 段(不是data 段)中找到.rodata 节。由于.rodata 节是只读的,因此节类型为SHT_PROGBITS。
[.plt] 程序链接表(Procedure Linkage Table,PLT)包含了动态链接器调用从共享库导入的函数所必需的相关代码。由于其存在于text 段中,同样保存了代码,因此节类型为SHT_PROGBITS。
[.data] 不要将.data 节和data 段混淆了,.data 节存在于data 段中,保存了初始化的全局变量等数据。由于其保存了程序的变量数据,因此类型被标记为SHT_PROGBITS。
[.bss] .bss 节保存了未进行初始化的全局数据,是data 段的一部分,占用空间不超过4 字节,仅表示这个节本身的空间。程序加载时数据被初始化为0,在程序执行期间可以进行赋值。由于.bss 节未保存实际的数据,因此节类型为SHT_NOBITS。
[.got.plt] .got 节保存了全局偏移表。.got 节和.plt 节一起提供了对导入的共享库函数的访问入口,由动态链接器在运行时进行修改。如果攻击者获得了堆或者.bss 漏洞的一个指针大小的写原语,就可以对该节任意进行修改。.got.plt 节跟程序执行有关,因此节类型被标记为SHT_PROGBITS。
[.symtab] .symtab 中保存了可执行文件的本地符号,如全局变量,或者代码中定义的本地函数等。只是用来进行调试和链接的,有时候为了节省空间,会将.symtab 符号表从生产二进制文件中删掉

参考资料:

  1. ELF文件格式分析-北京大学操作系统实验室.pdf
  2. ELF文件 及 nm & readelf & objdump 使用与对比

你可能感兴趣的:(二进制分析,linux)